Compare commits

..

2015 Commits

Author SHA1 Message Date
dfa90812f4 [2296-2] 모듈 이동 시 モジュール間離隔(간격) 미검증 수정 + 입력창 Del키 막힘(preventDefault) 수정 2026-07-01 09:34:17 +09:00
d562d02413 [2173] ship 후속 수정 — const __raw 과잉 주석처리 복원
작업내용:
- equalizeSymmetricHips(_apply): const __raw 주석 복원 (라인 6736)
- equalizeParallelEaveLabels(_apply): const __raw 주석 복원 (라인 6808)
- 원인: 이전 ship Python 정리 스크립트의 const __ 정규식이 디버그 변수가 아닌 활성 헬퍼까지 주석처리

Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
2026-06-29 17:33:39 +09:00
8c30c3c2bb [2173] 케라바 처마→박공 마루 미생성 수정 — 잔여단계 순서무관 연결성 판정으로 일반화
작업내용:
- drawRoofByAttribute otherIs-replace 블록: intersect===analyze.end 일치 시 zero-length 붕괴를 farEnd(교차점에서 먼 끝점) 보존으로 수정(Fix-A)
- wall-merge-residual: baseLines.length===4 하드코딩+순서의존 point-count 게이트를 제거, 잔여라인 전체 끝점(innerLines+residualLines) 기반 순서무관 연결성(startAnchored/endAnchored) 판정으로 교체(Fix-B) — ㄱ자(6변) 등 임의 다각형 마루 네트워크 지원
- sharedCnt/dup tolerance 0.5 적용(UI drift 대응)
- KERAB-GABLE-DIAG 진단 로그 전체 주석처리(재활용 가능하도록 삭제 안 함)
2026-06-29 16:11:53 +09:00
157828e29f [최종수정 _N] 케라바 라인변경 보조 변경 — kerab-rule-checker 出幅(출폭) 겹침 밴드 검증 보강 + QPolygon 디버그 수치 라벨 + qpolygon-utils 보정
Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
2026-06-29 13:35:13 +09:00
276fc8665f [이전복귀_0] useEavesGableEdit.js 4월13일(ab94869e) 버전으로 복원 — 케라바 작업 이전 개발자 버전으로 되돌려 옛 버전 문제점 추적
Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
2026-06-29 13:35:08 +09:00
9c6cabe911 [지붕면] 지붕면 할당 inner-inner T-접합 분할 보완 — A/B TYPE 박공→처마 우측 면 미생성 수정
Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
2026-06-29 13:09:06 +09:00
d0c3110801 [지붕재] select 폴링 stale closure 수정 — 데이터 도착 즉시 종료 + 매칭 0건 에러 안내
Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
2026-06-29 12:34:00 +09:00
b031e703ec [main] fix: fetchNoticeList·fetchFaqList catch 블록 스코프 오류 수정 — param·apiUrl 선언을 try 밖으로 이동 2026-06-26 14:36:34 +09:00
e79ee3dc52 [골짜기] 박공→처마 변환 시 골짜기 roofLine 보존 + R8 힙·수평 roofLine 생성
- gableWallLines 필터 + cleanupDangling에서 lineName='roofLine' 제외 — 골짜기 외곽선 삭제 방지
- snapRoofLineCorner: 出幅 이동 코너에 맞춰 인접 세로 roofLine(L-1/L-3) 끝점 갱신
- 변환변 수평 roofLine(L-8) 신규 생성(중복 가드 포함)
- R8: 인접 처마 코너에서 > 완성 힙 생성
- QPolygon 라벨 체계 개편(L/H/W/R/HE/B) + classifyLineForLabel 공개화
- isHip에 isDiagonalHip 가드 — 축정렬 hip 오분류 방지
- drawRoofLine에 lineName='roofLine' 부여 — purge 제외·라벨 L 보장

Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
2026-06-25 14:04:32 +09:00
db7ea8c676 [골짜기마루] A타입 생성 시 골짜기 박스라인(B) 추가 — 마루와 별개로 出幅 겹침 경계 유지
[작업내용] :
- drawGableRoof 후처리: 평행 두 마루가 수직축으로 겹치면(골짜기)
  짧은 마루 위치에 겹침구간 박스라인(kerabValleyOverlapLine, 라벨 B) 생성
- drawValleyBoxLine 헬퍼 추가 — 마루 삭제해도 박스 경계 잔존(박스라인+마루)
- 진단 로그([GABLE-H8-*][VALLEY-BOX-FILL]) 주석화 + logger import 제거

Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
2026-06-24 14:45:50 +09:00
eb18d0e1ac [2294] fix: 누락된 kerab-rule-checker.js 추가 — dev 빌드 module not found 해결
[작업내용] :
- useEavesGableEdit.js 의 import 대상인 kerab-rule-checker.js 가 커밋되지 않아 dev 빌드가 '@/util/kerab-rule-checker' 모듈 해석에 실패하던 문제 수정
- 케라바 기하 불변식 체커(checkKerabRules) 파일을 저장소에 추가

Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
2026-06-23 18:05:18 +09:00
c3c90925cd H-8 가드 2026-06-23 16:55:16 +09:00
48532fa226 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into hotfix/dev_yscha 2026-06-23 16:01:24 +09:00
d5b2426596 [A타입] 지붕생성 노치 결합면 중복 roofLine 스킵 가드 추가 — H-8 과길이(마루 위 겹침) 수정 2026-06-23 15:54:22 +09:00
5279f35b7b [2294] 체크포인트 — 타입 박공→처마 라인변경 RG-1 실삭제 + H-8 박스막음
[작업내용] :
- 마루(RG-1) hide 대신 canvas.remove + innerLines splice 로 실제 삭제
  (LABEL-MAP·디버그 라벨이 visible 무시 → hide 는 검증에서 잔존)
- H-8 박스막음: 과길게 뻗은 끝점을 boxCorner 로 내려 H-7↔H-2 사이만
  닫는 짧은 세로 roofLine 으로 보정 (\|/ 수렴형상 금지)

Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
2026-06-23 14:46:06 +09:00
cc6c734250 보조선 사이즈 변경 후 지붕면 할당 시 할당 제대로 안되는 현상 수정 2026-06-23 14:22:46 +09:00
ad5fa31576 사이즈 변경 시 1. 왼쪽 위 고정, 2. 오른쪽 아래 고정 2026-06-22 15:19:20 +09:00
58fbb34c74 보조선 select 풀릴 시 색 변경 제거 2026-06-22 13:59:59 +09:00
c93f098939 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into feature/redo-undo 2026-06-22 13:35:04 +09:00
Jaeyoung Lee
45a3199fba fix: 용마루 비직교 형상 외곽 지붕선 소실 수정
[작업내용] :
- splitRoofLinesToInnerLines 의 hasOverlapLine 가드를 축정렬 bbox 포함 → 공선+구간 덮음으로 교체
- 육각형 등 잘린 모서리의 짧은 수평/수직 처마변(bbox 한 축 두께 0)이 대각선 추녀 bbox 에
  삼켜져 skip → 원본 외곽선 transparent 로 소실되던 false positive 제거
- 양 끝점이 모두 상대 선분 위(isPointOnLineNew)일 때만 overlap 인정 → 공선 아닌 추녀는 통과
- 변별/박공/한쪽흐름 경로 회귀 없음(직교 형상은 bbox 포함=공선 겹침이라 동작 불변)
- 설계 문서 §10 진단 결과·수정·회귀 분석 기록

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 13:07:20 +09:00
591f467548 [api] pcsItemList 에 pcsSerParallelYn 전달 — 5개 PCS API 파라미터 보강
Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
2026-06-22 11:57:04 +09:00
3c2367de32 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into feature/redo-undo 2026-06-22 11:05:21 +09:00
78ce77df0b Merge pull request '발전시뮬레이션 옆 경제시뮬레이션 버튼 활성화 처리' (#925) from feature/jp_0615 into dev
Reviewed-on: #925
2026-06-19 16:40:23 +09:00
02f0828bd5 발전시뮬레이션 옆 경제시뮬레이션 버튼 활성화 처리 2026-06-19 16:39:01 +09:00
3dd022daea Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into feature/redo-undo 2026-06-19 11:05:20 +09:00
cef7935a91 Merge branch 'feature/redo-undo' of https://git.hanasys.jp/qcast3/qcast-front into feature/roofline-edit2 2026-06-18 10:49:50 +09:00
3ccd2dc991 [token] ConvertAPI DWG/DXF 변환을 서버 프록시로 전환 — 토큰 클라이언트 비노출 2026-06-18 10:33:09 +09:00
7b9a9b9146 모듈 제거 안되는 현상 수정 2026-06-18 10:31:45 +09:00
488cd54060 표준시공II 변경 2026-06-17 17:39:15 +09:00
0b5b91ba15 innerLines 수정 시 분할 안되는 현상 수정 2026-06-17 16:24:46 +09:00
271c132bac A,B타입 보조선 두개이상 변경 시 이상 수정 2026-06-17 14:33:42 +09:00
7773e08617 Merge branch 'feature/redo-undo' of https://git.hanasys.jp/qcast3/qcast-front into feature/roofline-edit2 2026-06-16 17:28:14 +09:00
3a51f1e208 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into feature/roofline-edit2 2026-06-16 17:28:04 +09:00
f5e8b91969 Merge branch 'feature/roofline-edit' of https://git.hanasys.jp/qcast3/qcast-front into feature/roofline-edit2 2026-06-16 17:09:58 +09:00
1ccbfb521f Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into feature/redo-undo 2026-06-16 16:24:07 +09:00
Jaeyoung Lee
509626e644 fix: 지붕형상 저장 후 일반 상태 외벽선 클릭 선택 복원
[작업내용] :
- e17af8f4 가 색칠된 외벽선을 selectable:false 로 잠가, 지붕형상 저장 후 일반 상태에서 외벽선 클릭 선택이 막히던 회귀 수정
- useRoofShapeSetting(자동) / useRoofShapePassivitySetting(수동) 저장 시 외벽선 selectable:true 로 복원
- 드래그는 lockMovement(useCanvas 전역), 보조선 클릭 가로채기는 z-order(18abbeb9)로 이미 별도 차단되어 부작용 없음

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 15:40:30 +09:00
b5d4f74158 [2294_4] 케라바 골짜기 변→처마 Rule1 — 게이블벽 제거+골짜기 힙 생성+마루 절삭/확장/Y반사
切妻(게이블) 골짜기 변을 처마(eaves)로 전환 시:
- collinear 게이블벽(HIP) 제거 후 出幅(offset) surgical 반영
- 양 코너 45° 골짜기 힙 2개 생성, 첫 교차까지 raycast
- cleanupDangling: HIP dangler 만 제거, 마루(RIDGE)·VALLEY 절대 보존
- anchor 잃은 마루 끝점: 가로지르는 힙 있으면 절삭, 없으면 roofLine까지 확장
- 확장 마루가 힙 몸통 닿으면 힙 먼 끝 절삭(힙은 마루를 못 넘음)
- 새 힙이 기존 마루에 직격(hitKind===RIDGE)이면 반대편 Y 반사 힙 생성

Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
2026-06-16 15:07:53 +09:00
6b9846c5ba Merge branch 'feature/roofline-edit' of https://git.hanasys.jp/qcast3/qcast-front into feature/roofline-edit2
# Conflicts:
#	src/hooks/usePolygon.js
2026-06-16 14:07:57 +09:00
ee390de934 한쪽 흐름의 경우 추가 2026-06-16 14:06:19 +09:00
a13c4c4ac6 복잡한 지붕면 대응 2026-06-16 10:05:01 +09:00
9b18c29ad6 복잡한 지붕면 대응 2026-06-16 10:04:37 +09:00
3092bbcbff Merge pull request '발전시뮬레이션 팝업창 오픈 소스 및 메뉴 숨김처리' (#921) from feature/jp_0615 into dev
Reviewed-on: #921
2026-06-16 09:24:35 +09:00
e24f534d9b 발전시뮬레이션 팝업창 오픈 소스 및 메뉴 숨김처리 2026-06-16 09:23:12 +09:00
a6284ad3eb Merge pull request '발전시뮬레이션 팝업창 오픈 소스' (#919) from feature/jp_0615 into dev
Reviewed-on: #919
2026-06-15 18:04:05 +09:00
fd7bf4062d 발전시뮬레이션 팝업창 오픈 소스 2026-06-15 18:03:03 +09:00
e68d4cdd19 conflict 수정 2026-06-15 17:47:34 +09:00
bf3eb49047 Merge branch 'feature/roofline-edit' of https://git.hanasys.jp/qcast3/qcast-front into feature/roofline-edit2
# Conflicts:
#	src/hooks/useContextMenu.js
2026-06-15 17:47:14 +09:00
e1e6be28d1 지붕 라인 없는 경우 생성 안되도록 수정 2026-06-15 17:46:01 +09:00
a5ad6dd0e6 Merge pull request '발전시뮬레이션 팝업창 오픈 소스' (#917) from feature/jp_0615 into dev
Reviewed-on: #917
2026-06-15 16:41:24 +09:00
582e32bc02 발전시뮬레이션 팝업창 오픈 소스 2026-06-15 16:40:11 +09:00
75b0df084b Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into hotfix/dev_yscha 2026-06-15 16:04:00 +09:00
c6d6d9fa08 Merge pull request 'feature/jp_0615' (#915) from feature/jp_0615 into dev
Reviewed-on: #915
2026-06-15 16:03:02 +09:00
7423f7a05a 발전시뮬레이션 팝업창 오픈 소스 2026-06-15 16:01:26 +09:00
1e0124d9f2 [2294_5] 케라바 사각형 결정론적 솔버 + 처마전면 지붕면 할당 수정
- solveRectangleKerab: 4변 타입만으로 힙/마루 재생성(forward·revert 단일 공식)
- nGable=0 우진각·1 Y apex·1 CASE B 케라바·2 맞배 지원, 그 외 기존 maze 폴백
- hip attributes.extended=true 부여 → integrateExtensionLines merge 스킵 → split 4면 정상 할당
- outerLine bbox 기준 sideOf 수정(出幅 offset 좌표 불일치 해소)
- apex depth clamp min(L/2, oppDist) → R3-outside 방지
- detectKerabHipMarks·extendKerabHipsTo45 함수 추출(reclick·revert 45° 통합)
- KERAB-EXTHIP-COORD-CLEAN: orphan ext-hip 좌표 기반 재제거
- useRefFiles: 변환 API FileData 누락 시 에러 메시지 swalFire 안내
- kerab-offset-surgical: skipKerabHips 옵션(케라바 hip t-ratio 재계산 방지)

Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
2026-06-15 16:01:11 +09:00
666e75244d 발전시뮬레이션 팝업창 오픈 소스 2026-06-15 15:59:50 +09:00
593c2c53ff 보조선 기준으로 지붕 나누기 추가 2026-06-15 15:16:58 +09:00
d0493287ec 보조선 기준으로 지붕 나누기 추가 2026-06-15 15:16:02 +09:00
8511c1915c local에서 한글세팅 2026-06-15 13:45:44 +09:00
591c828bcf Merge branch 'feature/roofline-edit' of https://git.hanasys.jp/qcast3/qcast-front into feature/roofline-edit2
# Conflicts:
#	src/hooks/roofcover/useRoofShapePassivitySetting.js
2026-06-15 13:18:21 +09:00
Jaeyoung Lee
07f787670d feat: 한쪽흐름(shed) 외곽 처마선 편집 + 처마선 치수 중복 정리
[작업내용] :
- splitRoofLinesToInnerLines: degreeOf 옵션 추가 — 한쪽흐름 경사변(gable) plane/cos 실제길이 보정
- drawShedRoof: 외곽 처마선을 innerLine 으로 split(degreeOf 주입)해 편집 가능화
- removeRoofLengthText 추가: 처마선 innerLine 이 치수 전담 → roof 원본 변 치수(lengthText) 제거 (박공/split 공용)
- drawGableRoof: removeRoofLengthText 호출로 박공 roof 원본 치수 중복 제거
- commonDeleteText: 보조선(QLine) 삭제 시 parentId 매칭으로 lengthText 동반 제거 (치수 잔존 방지)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 13:10:10 +09:00
Jaeyoung Lee
0944aa3a8c fix: ErrorBoundary fallback 렌더 시 errorInfo null 크래시 방지
[작업내용] :
- getDerivedStateFromError 가 errorInfo 를 채우지 않아 첫 fallback 렌더에서 this.state.errorInfo 가 null
- componentStack 접근에 옵셔널 체이닝(?.) 추가 — componentDidCatch 의 setState 후 정상 표시
- ErrorBoundary 가 자기 자신을 크래시시켜 원본 에러를 가리던 이중 에러 해소

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 13:09:56 +09:00
234b720b78 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into feature/redo-undo 2026-06-15 10:17:17 +09:00
96585240ef 오프셋 설정에 시 길이 반영 제대로 안되는 현상 수정 2026-06-12 18:29:17 +09:00
bf98f69145 [2294_4] 타입 박공→처마 우진각 지붕할당 4면 미생성 수정 — SK 격자 반-엣지 purge + startPoint 방어 동기화 2026-06-12 17:11:40 +09:00
aa45a891bc [1189_4] 드래그 박스 모듈 Del 삭제 불가 수정 — roof 복수선택 시 discardActiveObject 차단 + module 우선 currentObject 설정 2026-06-12 16:40:00 +09:00
Jaeyoung Lee
fd83fa3aa0 fix: 박공(gable) 지붕도 처마선 외곽 전담 시 roof 원본 외곽선 숨김
[작업내용] :
- drawGableRoof 가 처마선 innerLine 생성(roof.innerLines.push) 후 roof 원본 외곽(stroke)을 transparent 로 숨김
- 박공은 split 공용 함수를 거치지 않으므로 drawGableRoof 내에서 직접 처리
- 처마선 삭제 시 roof 기본 stroke:'black' 이 노출되던 문제 해결 (변별/용마루와 동일 동작)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 14:57:38 +09:00
Jaeyoung Lee
53ecbcca0f fix: 처마선 split 시 roof 원본 외곽선 숨김을 공용 함수로 통합
[작업내용] :
- splitRoofLinesToInnerLines 가 처마변 innerLine화 후 roof 원본 외곽(stroke)을 transparent 로 숨기도록 통합
- 변별/용마루 양 경로 공통 적용 — 처마선 삭제 시 roof 기본 stroke:'black' 이 노출되던 문제 해결
- 용마루 호출부(skeletonBuilder)의 개별 roof.set 제거 (split 함수가 담당)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 14:45:12 +09:00
48fa507046 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into feature/redo-undo
# Conflicts:
#	src/hooks/module/useModule.js
#	src/hooks/roofcover/useEavesGableEdit.js
#	src/hooks/useCanvasEvent.js
2026-06-12 14:20:29 +09:00
158e90e169 [2294_3] TYPE 박공(切妻)→처마 완전 우진각(寄棟) 재구성 + inward 안쪽법선 보정
- applyTypeGableToEavesPattern inward 방향을 변의 폴리곤 안쪽 법선으로 도출
  (2회차 리버트 apex 폴리곤 밖 폭주 = roofLine 절삭 불변식 위반 수정)
- reconstructHipRoofRidgeIfComplete 신규: 양쪽 박공이 모두 처마가 되면
  X자 세로 마루 스텁을 버리고 힙 4개 절삭(R2/R3) + 가로 마루 1개(R1)로 재구성
- type-eaves 분기 최종 형상에 runKerabRuleCheck 추가

Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
2026-06-12 14:16:31 +09:00
Jaeyoung Lee
87545e5df1 feat: 용마루(skeleton) 지붕 외곽 처마선 편집 기능
[작업내용] :
- 추녀 연장(drawBaselineToRooflineHelpers) 후 splitRoofLinesToInnerLines 호출 → 처마변을 편집 가능한 innerLine(hip/roofLine)으로 등록
- 처마선 innerLine 이 외곽선을 전담하므로 roof 원본 외곽(stroke) 숨김 처리
- 설계 문서 정정: 대상 브랜치 roofline-edit, §4 리스크1 검증 완료, §9 측정 시점 정정
  (추녀 연장 후 종점이 처마변 위에 정합 → split 은 연장 다음에 호출)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 13:58:15 +09:00
Jaeyoung Lee
1bf5f45b63 refactor: 처마변 split 로직 splitRoofLinesToInnerLines 공용 함수 추출
[작업내용] :
- drawRoofByAttribute 의 인라인 split 블록을 splitRoofLinesToInnerLines 공용 함수로 추출 (변별/용마루 경로 공용)
- 분할점 거리순 정렬 버그 수정 (a[1] → a.distance — 분할점 2개 이상에서 정렬이 no-op 이던 문제)
- 분할점 끝점 판정을 almostEqual(1e-10) → 끝점과의 유클리드 거리 1px 이내 제외로 변경
  (SK 추녀 연장의 부동소수 오차 흡수, 0 길이 조각 생성 방지)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 13:58:08 +09:00
6937934fc1 [1189_3] 모듈 기본설정 마우스 드래그 이동 비활성화 — 복수선택·Del 삭제는 유지 2026-06-12 10:48:51 +09:00
1cb039a741 [2294_2] 케라바→처마 전환(타입 라인변경) 힙 단일 직선 45° 연장으로 통일
Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
2026-06-12 09:27:55 +09:00
Jaeyoung Lee
18abbeb902 fix: offset 0인 변에서 색칠된 외벽선이 보조선 선택을 가로채던 문제 수정
[작업내용] :
- 지붕형상 저장 시 외벽선을 bringToFront(맨 앞) 대신 WALL/ROOF 폴리곤 바로 위 인덱스에 삽입
- offset 0이면 보조선이 외벽선과 같은 좌표에 그려지는데, 외벽선이 맨 위에 있으면
  클릭을 가로채(selectable:false라도 evented는 유지됨) 보조선 선택이 불가능했음
- z-order를 폴리곤 < 외벽선 < 보조선·텍스트로 유지해 색 표시와 보조선 클릭을 양립

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 18:09:31 +09:00
126789ef46 [골짜기라인] 골짜기→케라바 내부확장라인(rLine) 정지점 절반 규칙으로 통일
등구배 지붕에서 마루는 양쪽 처마에서 등거리(절반) — 이 불변식을 적용.
기존 ridge-stop 우선/없으면 wall 끝까지 휴리스틱은 방향(가로/세로)에 따라
결과가 들쭉날쭉했다. (start + wallHit) / 2 중점으로 고정, 방향 무관 통일.
wLine(wallBase) 도 vEnd(절반) 파생으로 '-extend' 라인 제거.

Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
2026-06-11 15:39:28 +09:00
Jaeyoung Lee
7900b2e67b fix: removePitchText가 경사 텍스트를 삭제하지 못하던 버그 수정
[작업내용] :
- canvas.remove()에 배열을 통째로 넘겨 Fabric이 무시(silent no-op)하던 것을 spread로 개별 전달하도록 수정
- addPitchText의 중복 방지가 무력화되어 같은 자리에 경사/출폭 텍스트가 겹겹이 쌓이던 문제 해결

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 14:52:19 +09:00
e3c092a6b4 저장 후 보조선으로 나뉘어지지 않는 현상 수정 2026-06-11 13:03:18 +09:00
1db206fe34 [2294_1] 케라바 出幅(출폭) 토글 라인 재계산 핸들러 분리 + 규칙 검증 추가
골짜기(케라바 패턴)·일반 라인을 독립 핸들러로 분리해 상호 간섭(whack-a-mole) 차단.
일반 라인은 절대 재계산(apex 불변, roofLine-hit 끝점만 재교차) + apex junction degree≥1 임계.
KERAB-RULE-CHECK(R1~R4) forward/revert 검증, RIDGE-DIAG 자연 마루 길이 불변 감시 추가.
REVERT/VALLEY-EXT 끝점 공유 가드로 게이블 hip 확장·길이 0 붕괴 방지.

Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
2026-06-10 18:05:36 +09:00
83107a801d 지붕면 할당 로직 수정 2026-06-10 16:50:46 +09:00
Jaeyoung Lee
e17af8f4b4 fix: 지붕형상 설정/수동설정 외벽선 표시·선택 상태 정리
[작업내용] :
- 지붕형상 저장 후 색칠된 외벽선 선택·드래그 차단, 변별로 설정 재진입 시 선택 복원
- 수동설정 저장 시에도 자동설정과 동일하게 외벽선을 타입별 색상으로 표시
- 두 모달을 진입 시 스냅샷 → 저장 시 커밋 / 저장 없이 닫으면 롤백 구조로 변경
  (팝업을 열었다 닫기만 해도 외벽선이 숨겨지고 지붕면이 삭제되던 문제 수정)
- 롤백 시 WALL 폴리곤은 원본 객체를 원래 z-순서에 재삽입
  (재생성 시 최상단에 올라가 보조선 클릭을 가로채던 문제 방지)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 13:31:25 +09:00
54e6a31dcf [1189_2] 手動回路割り当て(수동 회로 할당) 화면에서 드래그 박스 복수선택 활성화
작업내용 :
- PassivityCircuitAllocation.jsx: canvas.selection=true (화면 한정) + selection:created/updated 이벤트로 handleBoxSelect 등록
- handleBoxSelect: activeSelection 의 모듈만 targetModules 에 가산 병합, 이후 discard (단일 클릭 토글 보존)
- cleanup: 이벤트 해제 + canvas.selection 복원 + discardActiveObject

Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
2026-06-10 13:10:54 +09:00
f2a444ffb7 [1189_1] 모듈 복수선택(드래그 박스/Shift) + 단일 마우스 드래그 이동 — 手動配置 활성 시 드래그 모드 중지
- canvas.selection: 모듈 기본설정 + 手動配置 비활성 조건에서만 true
- refineModuleSelection: 비모듈 혼합 선택 시 모듈만 재구성, 단일/복수 이동잠금 해제
- commitModuleDragMove: 드래그 완료 후 지붕면 이탈/겹침 검증, 위반 시 시작 위치 복원
- modulesRemove: 복수선택 일괄 삭제 지원, 회로 연결 시 전체 차단
- QContextMenu: 2개 이상 선택 시 컨텍스트 메뉴 차단

Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
2026-06-10 11:25:05 +09:00
6099dcbd5d Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into hotfix/dev_yscha 2026-06-09 16:48:09 +09:00
c0539f49ec 지붕선 선택 후 이동 시 깨지는 현상 수정 2026-06-09 16:11:51 +09:00
26dbe35ea4 [2283-2] 자동배치(自動配置) 비선택면 모듈 보존 — 選択面のみ再配置、未選択面はそのまま維持 2026-06-09 16:01:48 +09:00
Jaeyoung Lee
946088ddc2 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into feature/roofline-edit 2026-06-09 14:53:53 +09:00
567fa49d61 [2283-1] 자동배치(自動配置) 다지붕 선택 시 북면 열 감소 수정 — 대칭 보정 비대칭 지붕 제외 가드 추가
Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
2026-06-09 14:53:07 +09:00
Jaeyoung Lee
5c02d1d45c docs: roofline 편집 설계에 박공·한쪽흐름 4경로 분석 추가 — 한쪽흐름 degree 전략 주입(§8)
- §7 4경로(변별/박공/용마루/한쪽흐름) 외곽 지붕선 처리·편집 가능 여부 종합
- §8 한쪽흐름 경사변 plane≠actual 대응: splitRoofLinesToInnerLines degreeOf 콜백 주입
- §6 YAGNI 박공/한쪽흐름 항목을 범위 편입으로 갱신

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 14:49:56 +09:00
dda00e75b3 배치면 이동 시 길이 계산 수정 2026-06-09 14:17:31 +09:00
6969e6620e [2234] 수동 1매(手動 1枚) 단부(端部) 배치 시 지붕 밖(屋根外) 오판 수정 — checkModuleDisjointSurface 허용오차(spare) 0→0.5
Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
2026-06-09 10:56:21 +09:00
f6b0cf714e Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into feature/redo-undo
# Conflicts:
#	src/hooks/surface/usePlacementShapeDrawing.js
#	src/hooks/surface/useSurfaceShapeBatch.js
2026-06-09 09:58:24 +09:00
af954728d8 [2234] 手動 1枚 端部 배치 屋根外 오판 수정 — checkModuleDisjointSurface spare 0→0.5
Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
2026-06-09 09:41:33 +09:00
98b9ff8da0 [2209] PCS Single-P 비병설 복수 등록 차단 — 체크박스 전환 시 selectedModels 리셋 2026-06-08 16:48:30 +09:00
cc9aa238f2 [2250] 배치면 サイズ変更 후 치수 라벨 stale 수정 — planeSize 재계산 + 라벨 재생성 2026-06-08 16:01:34 +09:00
7a65fa734c Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into hotfix/dev_yscha 2026-06-08 15:53:12 +09:00
0980f6b0ce [2207] 이동/복사 모달 保存 버튼 문구 → 移動/コピー — PanelEdit type 분기 + i18n 키 신규 2026-06-08 15:50:46 +09:00
49fe0e04b0 Merge pull request '[2243] 配置面 입력치수 라벨 변질 수정 — planeSize 2순위/2단계 ±20mm 흡착 제거, 끝점 공유변만 상속' (#893) from hotfix/dev_yscha into dev
Reviewed-on: #893
2026-06-08 15:31:51 +09:00
dc7a2ea956 [2243] 配置面 입력치수 라벨 변질 수정 — planeSize 2순위/2단계 ±20mm 흡착 제거, 끝점 공유변만 상속
Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
2026-06-08 15:27:22 +09:00
5e557708f1 [server] PM2 wrapper exec→spawn 패치 — stdio:inherit + exit/error 감지 (운영서버 동기화) 2026-06-08 11:27:34 +09:00
8c2edf05e7 [2272] 한쪽흐름 처마 offset 반전 수정 — margin/padding 판정을 면적비교로 교체
골짜기(凹凸) 형상에서 offset=0 변(케라바·shed) 코너가 원본 경계에 남고
inPolygon 의 경계점 nudge 비대칭으로 inside 오판 → padding 오선택 → 처마 반전.
winding·골짜기 수에 무관한 shoelace 면적 절대값 비교로 판정 교체.

Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
2026-06-05 17:53:22 +09:00
dccdfdd175 모듈 undo redo시 계산 추가 2026-06-05 16:02:21 +09:00
02062ee4d1 [1529] 시공법 버튼 constTp 매핑 — WORK_LV_ID_-2(標準施工Ⅱ) 신규 항목 대응 + 인덱스 밀림 방지
Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
2026-06-05 15:51:46 +09:00
632221ac01 도머 오프셋 undo 안되는 현상 수정 2026-06-05 15:14:20 +09:00
6e144d7d04 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into feature/redo-undo
# Conflicts:
#	src/components/fabric/QPolygon.js
#	src/hooks/roofcover/useEavesGableEdit.js
#	src/hooks/roofcover/useRoofAllocationSetting.js
2026-06-05 14:39:23 +09:00
4d8fc60cf1 동이동 형올림내림 undo 했을때 선 색깔 이상
보조선 잡고 삭제시 지붕의 보조선은 삭제 안되고있음.
도머 설치 시 복사 후 undo 세번해야 다사라짐
도머 오프셋이동 시 안됨 수정
2026-06-05 13:57:51 +09:00
b9fb769e74 [출폭] 처마 출폭변경 시 hip 내부라인 끝점 불변 룰 적용 — CORNER-SHORTCUT/SHRINK-TRIM skipInnerLines + 45° ray-cast 확장
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-05 13:43:02 +09:00
ddf7cb9f13 [1423] 설계가져오기 T01 전용 + 2차점 강제 select 반영 — No data/네트워크 에러 분리 + vExt cascade 평행이동 2026-06-05 11:00:29 +09:00
8192b0b07f [0000] 케라바 라인이동 후 반대쪽 실행 버그 수정 — baseLine 기하학적 매칭 + KERAB-SKIP 가드
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-04 17:48:44 +09:00
9565764912 [0000] 케라바 kLine — wLine 이동 후 baseLine 끝점(t1/t2) 기준으로 foot 계산
wLine 이동 후 케라바 변경 시 선택된 target 은 이동 전 wallLine 좌표를 가짐.
wall.baseLines 에서 wallId 매칭으로 이동된 실제 끝점(t1/t2)을 찾아
kLine foot 계산 기준으로 사용 → 이상한 대각선 수정.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-04 17:19:53 +09:00
a8d59873b7 [1961] 평행 처마 쌍 라벨 통일 — OVER_EPS 1mm 클램프 보정 equalizeParallelEaveLabels
Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
2026-06-04 17:01:11 +09:00
df1a2d6fe6 [2294] 케라바 골짜기 할당 — 밴드 dedup + fold 대각선 제거 (변 연장 흡수)
작업내용 :
- 밴드 이중 생성 제거: buildOverlapLine(케라바 토글) + RECALC 중복 → RECALC 전 기존 kerabValleyOverlapLine 제거로 단일 소스 통일
- ladder → 단일 quad 밴드: V1+3변(connector/wall)만 생성, 내부 분할 제거
- 밴드 식별 ov≥2: split 후 V1 edge가 lineName 유실 → overlap-typed 변 2개 이상인 sub-roof를 밴드로 식별
- fold 방식 교체: detour 우회 → V1 전체를 가진 면(F-3)에 OV 외곽경로로 변 연장 흡수. 대각선 제거, 면적 보존 검증 추가
- vExt split(Option A): ridge 교차점에서 vExt를 분할해 RG-2를 할당 그래프에 연결
- QPolygon: ROOF-FACE-DIAG 디버그 라벨 + debugCapture 덤프 추가 (local 가드)

Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
2026-06-04 15:37:36 +09:00
c2d8aeb572 [2089] useSwal alert html 지원 + 저구배 경고 줄바꿈 수정
- useSwal alert 분기에 html 옵션 지원 추가 (html 있으면 html, 없으면 text)
- roof-pitch-warning: text → html + \n을 <br>로 치환하여 줄바꿈 렌더링
- ja.json: 構造物 → 架台 용어 수정 + \n 추가
- ko.json: 문구 다듬기 + \n 추가
2026-06-04 09:46:47 +09:00
8ef0810058 [2294] 케라바 출폭 surgical — vExt cascade + corner snap + ExtRidge 연결 2026-06-02 16:58:54 +09:00
44401a3cc1 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into hotfix/dev_yscha 2026-06-02 16:57:36 +09:00
562bc79beb [배치면초기설정] 저장값 우선 반영 — basicSetting 덮어쓰기 제거 + inputMode 동기화 2026-06-02 16:52:42 +09:00
010398d07e Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into feature/redo-undo 2026-06-02 16:50:35 +09:00
4378712b39 수동설정 전부 안되면 설정완료 불가
수동설정 완료 시 스냅샷
보조선 작성 완료 시 스냅샷
개구 배치시 undo 2회 필요 수정
2026-06-02 13:40:52 +09:00
Jaeyoung Lee
4644269852 docs: 지붕 생성 로직 분석 메모리 문서 추가
qpolygon-utils 지붕 생성 구조 / drawRoofByAttribute 내부 구조 /
skeleton-utils 용마루 두 경로 차이 분석. roofline-edit 구현 시 reference.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 09:52:17 +09:00
Jaeyoung Lee
b28397c91a docs: 용마루(skeleton) 경로 외곽 지붕선 편집 기능 설계 문서 추가
drawSkeletonRidgeRoof 경로에서 숨겨진 외곽 지붕선을 변별 경로와 동일한
selectable innerLine으로 생성해 기존 select+우클릭 편집 기능을 재사용하는 설계.
splitRoofLinesToInnerLines 공용 추출 + 정렬버그 수정 방향 포함.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 09:45:20 +09:00
a452ea8f94 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into feature/redo-undo
# Conflicts:
#	src/hooks/roofcover/useEavesGableEdit.js
2026-06-01 17:19:53 +09:00
15eaa4950a 처마 케라바 변경 에러 수정 2026-06-01 16:18:19 +09:00
141afa7a36 [2294] 케라바 출폭 surgical — skLine 절삭/복원 + 패턴 segment 매핑 2026-06-01 13:53:05 +09:00
c121937525 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into feature/redo-undo
# Conflicts:
#	src/hooks/roofcover/useEavesGableEdit.js
#	src/hooks/roofcover/useRoofAllocationSetting.js
2026-05-29 17:49:02 +09:00
b8ea9aba78 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into hotfix/dev_yscha 2026-05-29 16:07:22 +09:00
e32d2dc3a5 [2173] 케라바 vExt — stop·V apex trim·overlap 머지·offset bbox 2026-05-29 16:02:51 +09:00
5391e6de71 화살표 css 수정 2026-05-28 18:25:20 +09:00
sangwook yoo
f91b953ef5 feat: Add development server startup script for UR environment
- Introduces a new `start:ur` script in `package.json` to launch the Next.js application on port 5050 for the UR environment.
2026-05-28 17:35:28 +09:00
7fe1cca02a Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into feature/redo-undo
# Conflicts:
#	package.json
#	src/hooks/roofcover/useEavesGableEdit.js
#	src/hooks/useCanvasEvent.js
#	src/styles/_contents.scss
2026-05-28 17:04:16 +09:00
577ca47b5e 키보드 이벤트 제거 2026-05-28 16:48:36 +09:00
23dafe3b18 [2173] valleyExt — trim·cascade·ridge-stop 다 비활성 (확장만, 별도 룰 후속) 2026-05-28 13:51:07 +09:00
036c283e0c [2173] valleyExt Phase 1/2 helper 추출 + wallBase 독립 ray + ridge-stop 룰 2026-05-28 11:10:59 +09:00
sangwook yoo
f8641079e6 Disable PDF input mode option 2026-05-27 17:54:49 +09:00
sangwook yoo
dba2b16277 Merge remote-tracking branch 'origin/dev' into dev 2026-05-27 17:30:47 +09:00
sangwook yoo
379216a78a feat(btob): Add BtoB development server startup script
- Adds a new `start:btob` script to `package.json` for launching the Next.js application on port 5060 for the BtoB environment.
- Introduces `startscript-btob.js` as a convenience executable to run the `start:btob` command.
2026-05-27 17:05:11 +09:00
sangwook yoo
6a99b6de4a Merge remote-tracking branch 'origin/dev' into feature/pv-btob 2026-05-27 16:56:33 +09:00
8be7b03072 Merge pull request 'dev' (#871) from dev into prd-deploy
Reviewed-on: #871
2026-05-27 16:21:54 +09:00
a4b7f519bb [2173] valleyExt TRIM — hip-presence 원래hip-only + cascade hide 2026-05-27 16:18:33 +09:00
e9f5ceab69 [2173] 골짜기확장라인 TRIM 재활성 + Y junction 보존 + 끝점일치 보존 2026-05-27 15:20:55 +09:00
sangwook yoo
74c980adaf redo/undo 기능 개발을 위한 로컬 시작 스크립트 추가 2026-05-27 14:42:02 +09:00
sangwook yoo
79aafbd613 Merge remote-tracking branch 'origin/dev' into claude/lucid-shaw-91e51f 2026-05-27 14:41:26 +09:00
5ba5623d46 [2173] 골짜기확장라인 self-extension 모델 + TRIM/FIXPOINT-cuts off — 확장만 2026-05-27 13:58:26 +09:00
e1dbbf74de Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into feature/redo-undo
# Conflicts:
#	src/components/fabric/QPolygon.js
#	src/hooks/module/useModule.js
#	src/hooks/roofcover/useEavesGableEdit.js
#	src/hooks/roofcover/useRoofAllocationSetting.js
#	src/hooks/usePlan.js
2026-05-27 10:52:46 +09:00
8d7ffdc957 [2173] KERAB-OFFSET-SURGICAL flag ON — 케라바 토글 出幅 surgical 반영 활성화 2026-05-27 10:09:52 +09:00
f5f8eb9460 [2173] 처마/케라바 토글 KERAB-SIMPLE 알고리즘 + offset-surgical helper(flag off) 2026-05-27 10:03:56 +09:00
f5758cb383 모듈 세로 가운데정렬, 가로 가운데정렬 snapshot 추가 2026-05-26 11:13:47 +09:00
b775af1911 모듈 redo undo 시 선택되어 있는것 해제 2026-05-26 10:31:08 +09:00
39f4b0130f 열이동, 단이동 시 스냅샷 저장 위치 수정 2026-05-26 10:23:25 +09:00
9a24860b1c 모듈 삭제 시 스냅샷 중복 제거 2026-05-26 10:16:31 +09:00
37a1c24667 모듈/가대 설정 undo, redo 이상 동작 수정 2026-05-20 15:30:52 +09:00
de5b9f1fb7 도머 undo redo 후 제거 하면 lengthText 남아 있는 현상 수정, 도머 이동 시 두번 계산되는 현상 수정 2026-05-20 15:10:48 +09:00
sangwook yoo
f8467879a8 Merge remote-tracking branch 'origin/dev' into claude/lucid-shaw-91e51f 2026-05-20 09:41:37 +09:00
sangwook yoo
849f7a1dc9 feat(pdf-gemini): 배치면 초기설정에 PDF 도면 업로드 옵션 추가 — Gemini 외곽선 분석
- 「배치면 초기설정」 모달 寸法入力方法 라디오에 '도면 파일 업로드' 옵션 추가
- 선택 시 PDF 파일 업로드 + 읽기 페이지 설정(전체/입면도·평면도 지정) UI 노출
- 保存 클릭 시 /api/gemini/floor-plan 호출 → 외곽선을 캔버스에 반영한 후 기존 저장 흐름 진행
- Gemini File API 업로드 + finally 파일 폐기, 정점/면적 검증, 페이지 힌트 프롬프트 주입
- usePdfImport 훅: mm→canvas 좌표 변환, CCW 정규화, 기존 외곽선 덮어쓰기 confirm 후 QLine/QPolygon 생성
- @google/generative-ai 의존성 추가, GEMINI_MODEL env 4개 환경에 동기화
- 다국어(ja/ko) 메시지 추가, .playwright-mcp/ gitignore

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 09:39:41 +09:00
d48c94c99e Merge pull request 'dev' (#868) from dev into prd-deploy
Reviewed-on: #868
2026-05-20 09:02:59 +09:00
dc857b28fa [2173] junction-extended 패턴 outer hip 제거 + strokeWidth SK와 통일
[작업내용] :
- 대전제 반영: 케라바 ridge 가 생기는 분기에서 c1·c2 에 붙은 outer hip 2개 제거
  · inner hip(junction 너머) 보존
  · __removedHipsSnapshot 첨부 → revert 시 hip 2개 자동 복원
- revert 분기에서 hip 스냅샷 + ridge 스냅샷 둘 다 순회 복원하도록 대칭 보강
- 케라바 패턴 생성 라인 strokeWidth 2 → 4 (SK 라인과 동일, 6개소)
2026-05-20 08:46:43 +09:00
eb12b24138 [2173] 처마→케라바 junction-extended 패턴 + RG-1 자동 제거
[작업내용] :
- findHipPairViaJunction: H-7/H-1 의 junction 너머 inner hip(H-4/H-2) 무한확장 교점을 apex 채택
- applyKerabJunctionExtendedPattern (순수 추가):
  · ext hip 2개(junction→apex) + 중앙 ridge(mid→apex) 추가
  · ridgeEndpointsOnBothExtensions 로 RG-1 식별 → 스냅샷 첨부 후 제거 (RG-2 보존)
- applyKerabRevertPattern: __removedRidgesSnapshot(배열) 반복 복원으로 forward 와 대칭
- useRoofAllocationSetting: 분할 직전/후 패턴 라인 진단 로그(KERAB-PATTERN-DIAG)
2026-05-19 17:12:48 +09:00
sangwook yoo
c1247a95db Track docs markdown files 2026-05-19 16:20:48 +09:00
35ba41a104 탭에 path제거 2026-05-19 12:32:48 +09:00
ce84468f7c Merge pull request 'dev' (#865) from dev into prd-deploy
Reviewed-on: #865
2026-05-18 17:00:30 +09:00
sangwook yoo
530832e78d Merge remote-tracking branch 'origin/dev' into claude/lucid-shaw-91e51f 2026-05-18 16:32:47 +09:00
e74604d3f9 [2240] 손상 plan 좌표 가드/복원 — 로드 가드+저장 차단+복원 자동호출 2026-05-18 16:19:37 +09:00
sangwook yoo
51e6057c34 chore(codex): Add PreToolUse hook for graphify search context
Integrates a PreToolUse hook that provides context from the 'graphify' knowledge graph.
When the AI is about to use search commands (e.g., `grep`, `find`, `ripgrep`),
it will be prompted to consult `GRAPH_REPORT.md` if a graph exists, guiding it to
leverage structured knowledge before searching raw files.
2026-05-18 16:15:12 +09:00
sangwook yoo
e381ae0ebf chore(pdf-gemini): Gemini API 키 env 추가 + PDF 평면도 분석 구현 계획 문서
- .env.development / .env.production 에 GEMINI_API_KEY 추가 (PDF 평면도 분석 기능용)
- docs/ 에 PDF → Gemini → 캔버스 반영 기능 구현 계획서(html) 추가

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 16:05:18 +09:00
f32f9187d1 [DB] 컬럼 길이 가드 — addressee 합산 50자 + receiveUser 20자 클립 2026-05-18 15:00:20 +09:00
497c60775b Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into hotfix/dev_yscha 2026-05-18 12:42:49 +09:00
eaae0f4a84 [1961] hip 라벨 정합성 — SK↔할당 일치 + 0.5 step 표시 통일 2026-05-18 12:41:55 +09:00
589067d4b2 Merge pull request 'docs(S3): S3 이미지 저장 흐름 정리 문서 추가' (#861) from worktree-docs-s3-flow into dev
Reviewed-on: #861
2026-05-15 16:01:10 +09:00
sangwook yoo
313c3c629d docs(S3): S3 이미지 저장 흐름 정리 문서 추가
5개 이미지 API 라우트(upload/cad/map/canvas/estimate-image-copy)의 서버 처리,
클라이언트 콜러(useRefFiles/useImgLoader/useEstimateController), S3 키 레이아웃,
캔버스 캡처 가드 조건과 알려진 거친 모서리를 한 문서에 정리.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-15 15:58:53 +09:00
sangwook yoo
0211c57103 docs(README): create-next-app 보일러플레이트 → 실제 프로젝트 문서로 교체
- Stack / Scripts / Env / Routes / Source Layout / Deployment / Conventions / Troubleshooting 섹션을 현재 코드 상태(package.json, next.config.mjs, .env.*, ecosystem 파일) 기반으로 작성
- CLAUDE.md (gotcha 위주) 와 역할 분담: README 는 stack·디렉토리·env·배포 같은 기본 정보 담당
- docs/ PDF 와 .agents/skills/ 참조 인덱스 포함

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 15:35:02 +09:00
sangwook yoo
899b150ba8 docs(CLAUDE.md): 부정확 정보 정리 — README 신뢰 주장 제거 + graphify 섹션 현실 반영
- README.md 가 사실상 create-next-app 보일러플레이트인데 "authoritative" 라고 안내하던 문구 교체 — 단일 진실 공급원이 CLAUDE.md + docs/ PDF 임을 명시
- graphify-out/ 디렉토리 부재 및 .gitignore 미등록 현실 반영, 28줄 가이드를 5줄 핵심 사용법으로 축소

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 15:31:28 +09:00
sangwook yoo
9c0871d1fb Merge remote-tracking branch 'origin/dev' into claude/relaxed-jang-35f22b 2026-05-15 15:10:11 +09:00
8e7a6af3db 소스정리 2026-05-15 12:31:24 +09:00
116953ee2f 소스정리 2026-05-15 12:28:32 +09:00
905b51b865 [1423] 2차 user 판매점 select 항상 enable + 設計依頼 매핑 실패 시 본인 초기화 2026-05-15 12:19:15 +09:00
bfd9d446a3 [1423] 2차 user 판매점 select 항상 enable + 設計依頼 매핑 실패 시 본인 초기화 2026-05-15 11:09:28 +09:00
3aa61a25c4 Merge pull request '[1423] 設計依頼 import 매핑 정책 롤백 — StuffDetail.jsx 7e9ec4e3 복원' (#857) from dev into prd-deploy
Reviewed-on: #857
2026-05-15 10:05:39 +09:00
e915a386cc [1423] 設計依頼 import 매핑 정책 롤백 — StuffDetail.jsx 7e9ec4e3 복원
운영 배포 후 문제 보고로 PLANREQ-MATCH 2026-05-12 재정의 직전 안정 버전으로 되돌림.
2026-05-15 10:03:23 +09:00
b58af58b79 Merge pull request 'dev' (#855) from dev into prd-deploy
Reviewed-on: #855
2026-05-14 17:46:14 +09:00
f6652acbd7 2차 로그정리 2026-05-14 17:26:11 +09:00
347536e87a 2차 로그정리 2026-05-14 17:21:46 +09:00
042cdc4b83 1차 로그주석처리 2026-05-14 17:04:43 +09:00
91321745d4 [1423] 設計依頼 import 매핑 정책 재정의 — 미매칭 시 로그인 초기값 reset 2026-05-14 16:29:38 +09:00
f9fe9f788e Merge pull request 'dev' (#850) from dev into prd-deploy
Reviewed-on: #850
2026-05-14 14:56:30 +09:00
7e9ec4e3f2 로그 수정 2026-05-14 14:55:05 +09:00
d60bf27778 로그 수정 2026-05-14 14:49:41 +09:00
281d9bb583 로그 수정 2026-05-14 14:38:27 +09:00
518b51d33f Merge pull request '[2218] 초기 비밀번호 설정 메시지 수정' (#847) from dev into prd-deploy
Reviewed-on: #847
2026-05-14 14:35:16 +09:00
ccca89fef6 [2218] 초기 비밀번호 설정 메시지 수정 2026-05-14 14:32:07 +09:00
aad21d6c76 Merge pull request '[2218] 초기 비밀번호 설정 메시지 수정' (#845) from dev into prd-deploy
Reviewed-on: #845
2026-05-14 13:23:51 +09:00
9a10186c17 [2218] 초기 비밀번호 설정 메시지 수정 2026-05-14 13:21:54 +09:00
7ad7f3c6ac Merge pull request '[1961] split 길이 계산 Math.round(hypot)*10 → calcLinePlaneSize 통일 — 좌우대칭 hip 라벨 1mm 갈림 차단' (#843) from dev into prd-deploy
Reviewed-on: #843
2026-05-14 10:42:27 +09:00
490ab6c49f [1961] split 길이 계산 Math.round(hypot)*10 → calcLinePlaneSize 통일 — 좌우대칭 hip 라벨 1mm 갈림 차단 2026-05-14 10:36:11 +09:00
73bc78fc7e Merge pull request '[VALLEY-SKIP] isValleyVertex 노치 top_in 잘못된 eaveHelpLine 4개 차단 — zero-length neighbor + collinear continuation 2단 가드' (#841) from dev into prd-deploy
Reviewed-on: #841
2026-05-13 16:45:05 +09:00
aac4f1fb50 [VALLEY-SKIP] isValleyVertex 노치 top_in 잘못된 eaveHelpLine 4개 차단 — zero-length neighbor + collinear continuation 2단 가드 2026-05-13 16:43:13 +09:00
70f0345fb5 Merge pull request '[2210] 초기 비밀번호 설정 메시지 수정' (#839) from dev into prd-deploy
Reviewed-on: #839
2026-05-13 16:40:48 +09:00
25d2fbe419 [2210] 초기 비밀번호 설정 메시지 수정 2026-05-13 16:39:30 +09:00
2eee9edbc5 Merge pull request 'dev' (#837) from dev into prd-deploy
Reviewed-on: #837
2026-05-13 14:37:44 +09:00
0af4cd0f7d Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into hotfix/dev_yscha 2026-05-13 14:36:25 +09:00
77bca24e7c [SK-EXTENDED-PERSIST] hip __extended 플래그 save/load 보존 — attributes.extended 동기 + console→logger 2026-05-13 14:27:06 +09:00
6d4c832a54 Merge pull request 'dev' (#835) from dev into prd-deploy
Reviewed-on: #835
2026-05-13 12:21:46 +09:00
d15cdea721 Merge pull request 'removeShortEdges 직각 노치 변형 차단 — tiny edge 머지 시 axis-aligned 가드' (#833) from org into dev
Reviewed-on: #833
2026-05-13 12:21:03 +09:00
cd95b68d96 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into hotfix/dev_yscha 2026-05-13 12:16:23 +09:00
a332737654 [RACK-TILT] removeShortEdges 직각 노치 변형 차단 — tiny edge 머지 시 axis-aligned 가드 2026-05-13 12:14:42 +09:00
7c37cca3c7 Merge pull request '[1956-RACK-TILT] 빨강 점선(가대선/モジュール配置領域) 가로/세로 축 스냅' (#832) from dev into prd-deploy
Reviewed-on: #832
2026-05-13 10:44:19 +09:00
4f120e6e11 [1956-RACK-TILT] 빨강 점선(가대선/モジュール配置領域) 가로/세로 축 스냅
closing edge 의 FP drift 누적(15mm/576mm ≈ 1.5°) 으로 가로/세로 라인이
기울어 보이는 문제. snapNearAxisEdges(angleTolDeg=2) 각도 기반 스냅을
4종 폴리곤(trestle/dormerTrestle/trestlePolygon/moduleSetupSurface) 에 적용.

- QPolygon.initialize name 가드: load/new 대부분 케이스 커버
- handleOuterlinesTest 끝 snap: trestlePolygon 은 makePolygon 후 .set 이라 별도

#1956 (vertical tilt, 2026-04-08) 의 horizontal 형제 케이스.
2026-05-13 10:35:16 +09:00
sangwook yoo
29c60c0b54 fix: 코드리뷰 보강 — .gitignore 화이트리스트 + 훅 case 정밀화
- .gitignore: .claude/** 차단 + settings.json·md negation 화이트리스트로 전환.
  settings.local.json/projects·history·todos·cache 등 머신 전용 파일 누수 차단.
- .claude/settings.json: PreToolUse 훅 case 패턴을 첫 토큰 정확 매칭으로 교체.
  *grep* 과매칭(egrep·git grep·grepl) 및 rg 단독 미매칭 문제 해소.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 18:36:20 +09:00
sangwook yoo
26b1e31d74 docs: CLAUDE.md 최신 git 추적 현황 반영
- AGENTS.md alias 한 줄 추가 (Codex 등 타 에이전트 호환)
- .agents/skills 섹션 신설 — Vercel·Next.js 룰북 95개 위치/트리거 안내
- graphify-out 이 gitignore 된 로컬 캐시임 + 빈 상태 부트스트랩 방법 명시

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 18:16:36 +09:00
sangwook yoo
3ac66feba2 chore: .agents/.claude 설정·CLAUDE.md·AGENTS.md git 추적 추가
gitignore 의 *.md / .claude/ 차단을 풀어 다음 파일을 git 으로 추적.
- /CLAUDE.md, /AGENTS.md (루트 에이전트 가이드)
- .agents/**/*.md (skills 문서 95개)
- .claude/settings.json (Claude Code 프로젝트 설정)
.claude/worktrees/ 는 git worktree 메타이므로 신규 ignore 라인으로 제외.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 18:05:03 +09:00
sangwook yoo
4aa805c60f chore: gitignore 에 graphify-out/ 추가
graphify 지식 그래프 산출물(AST 캐시·graph.json·HTML·리포트)은 로컬 캐시이며
post-commit hook 으로 자동 갱신되므로 git 추적에서 제외.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 18:00:26 +09:00
4ea6e77151 Merge pull request '[1961] strict y1===y2 drift 2곳 수정 — tolerance 0.5 통일' (#830) from dev into prd-deploy
Reviewed-on: #830
2026-05-12 17:27:30 +09:00
76a53c64d9 [1961] strict y1===y2 drift 2곳 수정 — tolerance 0.5 통일 2026-05-12 17:25:17 +09:00
55e0528c26 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into feature/redo-undo
# Conflicts:
#	src/hooks/module/useModuleBasicSetting.js
2026-05-12 10:31:50 +09:00
0e9cf938e9 지붕형상 수동설정 undo redo 안되는 현상 수정 2026-05-11 09:59:49 +09:00
11d732e117 Merge pull request 'dev' (#828) from dev into prd-deploy
Reviewed-on: #828
2026-05-08 17:56:36 +09:00
2aef32e03c [2204] 0-면적 개구/그림자/도머 차단 — SizeSetting 입력 가드 + 손상 plan 로드 skip 2026-05-08 17:48:37 +09:00
a5e794b3ec [2204] 손상 plan InvalidStateError 3단 방어 (entry/cache/save guard) 2026-05-08 17:39:58 +09:00
8192f0929a [オブジェクト選択不可] ObjectSetting 모달 닫힘 시 roof selectable 원복 cleanup 추가 2026-05-08 17:35:55 +09:00
6f22ca7f3d Merge pull request 'dev' (#826) from dev into prd-deploy
Reviewed-on: #826
2026-05-08 08:53:39 +09:00
42abe1a720 claude 제거 2026-05-08 08:40:30 +09:00
df208a0cfb Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into feature/redo-undo 2026-05-07 17:13:51 +09:00
77de97eef1 지붕형상 설정 시 undo redo 되도록 수정 2026-05-07 15:53:08 +09:00
9de3424cdf undo redo 시 지붕면 할당 안되는 현상 수정 2026-05-07 15:21:31 +09:00
4e78429da8 [견적서] 1차 판매점명 행 노출 조건 보정 — storeLvl === '1' 일 때만 표시 2026-05-07 14:41:37 +09:00
dd39e6f07b 707 생기는 현상 수정 2026-05-07 14:40:20 +09:00
6a34fe53c6 [견적서] 1차 판매점명 행 노출 조건 보정 — storeLvl === '1' 일 때만 표시 2026-05-07 14:35:44 +09:00
ce3decb61c Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into feature/redo-undo 2026-05-07 14:18:42 +09:00
edf521e617 plan 이동 시 undo redo stack 제거 2026-05-07 14:18:34 +09:00
ae2ba0add5 [755] 객체 선택 시 Del/Backspace 즉시 삭제 2026-05-07 11:14:09 +09:00
80720fef2a 외벽선, 배치면 Redo undo 수정 2026-05-07 10:53:51 +09:00
2074c4aa93 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into feature/redo-undo
# Conflicts:
#	src/components/floor-plan/modal/auxiliary/AuxiliaryEdit.jsx
2026-05-07 09:53:11 +09:00
1d9eb39208 [1461] 物件情報 敬称 셀렉트 디폴트 적용 + reload 빈값 보강 2026-05-07 09:43:41 +09:00
16f595daff Merge pull request 'cluade' (#821) from dev into prd-deploy
Reviewed-on: #821
2026-05-06 18:12:14 +09:00
dd5fea7f21 cluade 2026-05-06 18:06:48 +09:00
6338fb3011 Merge pull request 'dev' (#819) from dev into prd-deploy
Reviewed-on: #819
2026-05-06 17:50:17 +09:00
4ebe4cb455 [1845] 실내집중형 PCS 수동 회로분할 동일 방위/구배 강제 — INDFCS + 수동 분할에서 다방위/다구배 직렬회로 구성 차단 2026-05-06 17:43:29 +09:00
cee4f9e4e9 claude 2026-05-06 16:46:06 +09:00
a5d2964e8e [2089] 저구배+특정기와 시공 매뉴얼 안내 confirm 추가 2026-05-06 16:44:26 +09:00
66833fd11b 인수인계서 5종 + Claude 메모리 git 관리 시작 — 다중 컴 동기화 2026-05-06 15:45:30 +09:00
ba92e3400d [1845] 실내집중형 PCS 수동 회로분할 동일 방위/구배 강제 — INDFCS + 수동 분할에서 다방위/다구배 직렬회로 구성 차단 2026-05-06 15:16:46 +09:00
27ee53c350 dev 스크립트 추가 — dev:local-api, dev:log:local-api (localhost:8080 API 전환용) 2026-05-06 10:23:31 +09:00
6f41e127bc SK 인덱스 어긋남 보강 — ensureCounterClockwiseLines graph 키 0.1 round 통일 2026-05-06 10:17:11 +09:00
78ccf3339e Merge pull request '補助線サイズ変更/移動/コピー 모달에 계산기패드 토글 버튼 추가' (#815) from dev into prd-deploy
Reviewed-on: #815
2026-05-04 14:16:11 +09:00
60cb86910e 補助線サイズ変更/移動/コピー 모달에 계산기패드 토글 버튼 추가 2026-05-04 14:15:20 +09:00
efca023f1d Merge pull request '동시 UPDATE 데드락 방어 — abort+latest, X-Idempotency-Key, 직렬화 - api 통신에러' (#813) from dev into prd-deploy
Reviewed-on: #813
2026-05-04 13:55:18 +09:00
3000de9c32 동시 UPDATE 데드락 방어 — abort+latest, X-Idempotency-Key, 직렬화 - api 통신에러 2026-05-04 13:54:07 +09:00
426a72b815 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into feature/redo-undo
# Conflicts:
#	src/hooks/roofcover/useRoofAllocationSetting.js
2026-05-04 10:58:34 +09:00
f5beab6bd4 Merge pull request '라벨변경' (#811) from dev into prd-deploy
Reviewed-on: #811
2026-04-30 18:27:52 +09:00
376ccab8e7 라벨변경 2026-04-30 18:26:34 +09:00
c864e69b2b Merge pull request 'innerLine' (#809) from dev into prd-deploy
Reviewed-on: #809
2026-04-30 17:03:13 +09:00
c2eba138a6 innerLine 2026-04-30 17:02:16 +09:00
be809e5891 Merge pull request 'dev' (#807) from dev into prd-deploy
Reviewed-on: #807
2026-04-30 13:09:08 +09:00
02c8422e87 라인색 2026-04-30 13:07:50 +09:00
c221515744 동일라벨 2026-04-30 12:21:59 +09:00
645e8b344d 보조라인1 2026-04-30 11:16:19 +09:00
a04e7c6a1b Merge pull request '마루이동' (#805) from dev into prd-deploy
Reviewed-on: #805
2026-04-29 18:32:04 +09:00
beeeeb32e5 마루이동 2026-04-29 18:31:14 +09:00
1428de4b3c Merge pull request 'dev' (#803) from dev into prd-deploy
Reviewed-on: #803
2026-04-29 18:17:39 +09:00
ca8778ba31 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into feature/redo-undo
# Conflicts:
#	src/hooks/surface/usePlacementShapeDrawing.js
2026-04-29 18:07:53 +09:00
75c2d16b05 Merge pull request '1961, 1969, 2118 수정' (#801) from dev into prd-deploy
Reviewed-on: #801
2026-04-29 18:04:28 +09:00
2a7c78a927 확장자 1단계마무리 2026-04-29 18:02:14 +09:00
16eb41aab6 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into hotfix/dev_yscha 2026-04-29 17:48:12 +09:00
abee58fcae 확장자 1단계마무리 2026-04-29 17:47:46 +09:00
3f5f490884 1961, 1969, 2118 수정 2026-04-29 14:47:17 +09:00
df712657c3 Merge pull request 'dev' (#799) from dev into prd-deploy
Reviewed-on: #799
2026-04-28 18:07:08 +09:00
b49e3871d5 redo undo 작업중 2026-04-28 16:12:21 +09:00
0b27a161db 평면도 확정 시 확인 다이얼로그 추가 (버튼/Enter 키) 2026-04-27 17:49:55 +09:00
6c0145ef30 외벽선 확정 시 확인 다이얼로그 추가 (버튼/Enter 키) 2026-04-27 17:49:55 +09:00
0e3acedf2d 평면도 확정 시 확인 다이얼로그 추가 (버튼/Enter 키) 2026-04-27 17:43:35 +09:00
7d1e49a158 외벽선 확정 시 확인 다이얼로그 추가 (버튼/Enter 키) 2026-04-27 17:28:00 +09:00
d9bea9dcfb 잔여선 삭제 및 라벨표시(local) 2026-04-27 15:15:56 +09:00
1d00e000be 잔여선 삭제 및 라벨표시(local) 2026-04-27 15:11:39 +09:00
21c9f85a51 라인줄표시, 인덱스 재설치 2026-04-27 13:49:48 +09:00
aab8ebbb05 빌드 오류 수정: debugLabels import 제거
dev-deploy 빌드에서 untracked 파일(@/util/debugLabels) 참조로 실패.
디버그 전용 라벨 부착 호출은 로컬 한정이므로 커밋 대상에서 제거.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-27 13:49:48 +09:00
d810b89658 라인줄표시, 인덱스 재설치 2026-04-27 13:49:48 +09:00
0b0a1786da 라인오버하면 평행라인으로 변경 2026-04-27 13:49:48 +09:00
428ff36afb 라인줄표시, 인덱스 재설치 2026-04-27 13:24:21 +09:00
6b02b2cad0 빌드 오류 수정: debugLabels import 제거
dev-deploy 빌드에서 untracked 파일(@/util/debugLabels) 참조로 실패.
디버그 전용 라벨 부착 호출은 로컬 한정이므로 커밋 대상에서 제거.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-27 13:20:19 +09:00
8fef6c9f28 라인줄표시, 인덱스 재설치 2026-04-27 13:09:28 +09:00
26b583e2f5 라인오버하면 평행라인으로 변경 2026-04-24 20:02:39 +09:00
b7cf8eeca9 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into feature/redo-undo 2026-04-24 16:15:05 +09:00
cce18d47ad 도머 설치된 지붕면 이동시 이상 수정 2026-04-24 16:14:58 +09:00
936d490803 [1806]서까래 높이 조정에 따른 지붕 형상 불일치 - 라인오버체크2 2026-04-23 15:26:58 +09:00
01b9625a13 [1806]서까래 높이 조정에 따른 지붕 형상 불일치 - 라인오버체크2 2026-04-23 15:24:21 +09:00
b8db935518 보조라인 맞춤 2026-04-23 12:35:14 +09:00
3d142cd95c sk라인 기록 보존 2026-04-23 12:35:14 +09:00
a3e608f059 Cross Mix(DC3) 접속함을 PCS 담당 모듈 타입으로 필터링해 1건만 내려가도록 통합 로직 적용 2026-04-23 12:35:14 +09:00
558769063b PassivityCircuitAllocation에 전달하던 미사용 ref prop 제거
passivityCircuitAllocationRef (useRef) 선언 제거
원인: function component에 ref 전달 시 React warning 발생 (forwardRef 미사용)
PCS 접속함 응답에 추가된 moduleTpCd 필드를 Cross Mix 식별을 위해 프론트 상태 보존 및 getQuotationItem 제출 파라미터까지 흘려보내고, dedupe 키를 (itemId, moduleTpCd) 조합으로 변경.
2026-04-23 12:35:13 +09:00
eaf6bcb3d3 trestles[]에 subModuleTpCd 추가 (Cross Mix 식별용, 단일/동계열은 빈 값) 2026-04-23 12:35:13 +09:00
8493fb037b [1806]서까래 높이 조정에 따른 지붕 형상 불일치 - 라인오버체크 2026-04-23 12:34:10 +09:00
3c1a1f89b4 보조라인 맞춤 2026-04-23 12:24:21 +09:00
a4d3f7a4c9 sk라인 기록 보존 2026-04-22 16:41:55 +09:00
7a987116b8 sk라인 기록 보존 2026-04-22 16:39:28 +09:00
0eb888fbb9 Cross Mix(DC3) 접속함을 PCS 담당 모듈 타입으로 필터링해 1건만 내려가도록 통합 로직 적용 2026-04-22 13:35:47 +09:00
795c9789bd PassivityCircuitAllocation에 전달하던 미사용 ref prop 제거
passivityCircuitAllocationRef (useRef) 선언 제거
원인: function component에 ref 전달 시 React warning 발생 (forwardRef 미사용)
PCS 접속함 응답에 추가된 moduleTpCd 필드를 Cross Mix 식별을 위해 프론트 상태 보존 및 getQuotationItem 제출 파라미터까지 흘려보내고, dedupe 키를 (itemId, moduleTpCd) 조합으로 변경.
2026-04-21 17:51:47 +09:00
cd2c2d0b91 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into hotfix/dev_yscha 2026-04-21 16:10:36 +09:00
f022956c44 trestles[]에 subModuleTpCd 추가 (Cross Mix 식별용, 단일/동계열은 빈 값) 2026-04-21 16:10:15 +09:00
78e4225069 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into feature/redo-undo 2026-04-20 10:27:20 +09:00
a5675d41d8 배치면 이동 시 길이 계산 수정 2026-04-20 10:27:12 +09:00
c872d5c0f6 [1960]출력된 [배치도·계통도]에 가대가 표시되는 경우 2026-04-17 18:07:28 +09:00
554e1cc876 Merge pull request '[1960]출력된 [배치도·계통도]에 가대가 표시되는 경우' (#785) from hotfix/dev_yscha into dev
Reviewed-on: #785
2026-04-17 18:05:29 +09:00
4d91da5f9d [1960]출력된 [배치도·계통도]에 가대가 표시되는 경우 2026-04-17 18:04:05 +09:00
2ae3c47f59 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into feature/redo-undo
# Conflicts:
#	src/hooks/roofcover/useWallLineOffsetSetting.js
2026-04-17 13:29:10 +09:00
8d6c4a6504 [1806]서까래 높이 조정에 따른 지붕 형상 불일치 - 라인오버체크 2026-04-16 13:22:43 +09:00
1028d210ad 회로 설정 이상 수정 2026-04-16 13:22:43 +09:00
83101a8e09 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into hotfix/dev_yscha 2026-04-16 13:18:59 +09:00
caab0d9d81 [1806]서까래 높이 조정에 따른 지붕 형상 불일치 - 라인오버체크 2026-04-16 13:18:39 +09:00
a40c47699c 회로 설정 이상 수정 2026-04-16 11:23:23 +09:00
b9bb4708a8 [1485] 오브젝트 복사기능 권한 설정 2026-04-15 17:59:28 +09:00
0d724a2ed1 Merge pull request '로그 삭제' (#782) from dev into prd-deploy
Reviewed-on: #782
2026-04-15 17:53:23 +09:00
6cc9201962 로그 삭제 2026-04-15 17:52:22 +09:00
77e3f688c6 redo undo 작업중 2026-04-15 17:37:49 +09:00
e2ad711244 Merge pull request '외벽선 편집 및 오프셋 길이 계산 수정' (#780) from dev into prd-deploy
Reviewed-on: #780
2026-04-14 15:05:18 +09:00
55dd3d9d3d 외벽선 편집 및 오프셋 길이 계산 수정 2026-04-13 18:10:01 +09:00
af3ec6e31b Merge pull request '[1491]실내 집중형 PCS를 사용하는 회로 구성에서 접속함이 정상적으로 산출되지 않는 현상' (#778) from dev into prd-deploy
Reviewed-on: #778
2026-04-13 16:37:31 +09:00
d0590f513e [1491]실내 집중형 PCS를 사용하는 회로 구성에서 접속함이 정상적으로 산출되지 않는 현상 2026-04-13 16:27:28 +09:00
d4766e30a3 undo redo 시 메뉴 변경 2026-04-13 11:10:38 +09:00
c40d67aa34 Merge pull request 'dev' (#776) from dev into prd-deploy
Reviewed-on: #776
2026-04-13 11:07:05 +09:00
eedc4d759c Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into hotfix/dev_yscha 2026-04-13 11:05:22 +09:00
9c25d3ed66 [1960]출력된 [배치도·계통도]에 가대가 표시되는 경우 2026-04-13 11:04:59 +09:00
53226d00fd Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into feature/redo-undo 2026-04-13 10:42:35 +09:00
8eca824c8f 배치면 이동 시 화살표 위치 수정 2026-04-13 10:35:12 +09:00
50f2667fac Merge pull request '[1971] 외벽선 작성 방향 “반시계 방향” 표시 삭제' (#774) from dev into prd-deploy
Reviewed-on: #774
2026-04-13 10:34:35 +09:00
d15c9e69ec [1971] 외벽선 작성 방향 “반시계 방향” 표시 삭제 2026-04-13 10:33:34 +09:00
ab94869efd Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into feature/redo-undo 2026-04-13 09:55:45 +09:00
a3361ddc9d 지붕재 변경 및 할당 시 스택 초기화 2026-04-13 09:55:38 +09:00
59649c8fb7 Merge pull request '지붕 이동 시 보조선 길이 안따라 가는 현상 수정' (#772) from dev into prd-deploy
Reviewed-on: #772
2026-04-13 09:08:37 +09:00
21f25995cb 지붕 이동 시 보조선 길이 안따라 가는 현상 수정 2026-04-10 10:23:02 +09:00
afd00b642c Merge pull request 'dev' (#770) from dev into prd-deploy
Reviewed-on: #770
2026-04-09 18:13:53 +09:00
20235ac236 offset<->moveline 벽라인정상이동 2026-04-09 11:28:57 +09:00
462e21ddbd Merge pull request '[1491]cvrLmtRow에 따른 처마커버 단수' (#767) from hotfix/dev_yscha into dev
Reviewed-on: #767
2026-04-08 17:43:13 +09:00
471093eb8a [1491]cvrLmtRow에 따른 처마커버 단수 2026-04-08 17:41:21 +09:00
1e087d33c5 Merge pull request '움직인라인그대로유지offset' (#766) from dev into prd-deploy
Reviewed-on: #766
2026-04-08 16:59:41 +09:00
94f18d1525 움직인라인그대로유지offset 2026-04-08 16:41:49 +09:00
1935baf471 Merge pull request 'dev' (#764) from dev into prd-deploy
Reviewed-on: #764
2026-04-08 16:24:27 +09:00
c05c08e390 sk4/8 2026-04-08 16:22:06 +09:00
828ea12484 회로 할당 제대로 안되는 현상 수정 2026-04-08 15:59:04 +09:00
e3aae27cc4 Merge pull request 'dev 관련 링크수정2' (#762) from dev into prd-deploy
Reviewed-on: #762
2026-04-08 12:52:21 +09:00
31b0113246 dev 관련 링크수정2 2026-04-08 12:50:47 +09:00
680ea847e9 Merge pull request 'dev' (#760) from dev into prd-deploy
Reviewed-on: #760
2026-04-08 12:38:54 +09:00
197bd63eed Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into hotfix/dev_yscha 2026-04-08 12:35:16 +09:00
a2bbb035d9 dev 관련 링크수정 2026-04-08 12:34:53 +09:00
aa16a45ba5 1956 모듈 설치면 제대로 설치 안되는 현상 수정 2026-04-08 11:39:20 +09:00
d341b4a52d Merge pull request 'dev' (#758) from dev into prd-deploy
Reviewed-on: #758
2026-04-08 10:22:10 +09:00
71da4d07fe 임시저장 상세 폼의 물건명 입력 50자제한 2026-04-08 10:08:42 +09:00
afe0a377db 자동 로그인, 로그아웃 추적2 2026-04-03 16:20:12 +09:00
b021b7beea 로그아웃 결과없이 2026-04-03 16:15:13 +09:00
b7ba6d8fc1 [1480]사의 각도 설정을 "각도"로 했을 때 발생하는 문제 2026-04-03 14:28:00 +09:00
e0a4da21b8 [1480]사의 각도 설정을 "각도"로 했을 때 발생하는 문제 2026-04-03 14:28:00 +09:00
c6da437a8d 자동 로그인, 로그아웃 추적 2026-04-03 13:35:23 +09:00
ccf60ee784 자동 로그인, 로그아웃 추적 2026-04-03 13:17:58 +09:00
138bdbdd35 로그인, 로그아웃 추적 2026-04-03 11:14:47 +09:00
3eaf3f2c17 [1480]사의 각도 설정을 "각도"로 했을 때 발생하는 문제 2026-04-03 09:12:43 +09:00
9022812199 [1480]사의 각도 설정을 "각도"로 했을 때 발생하는 문제 2026-04-02 18:27:12 +09:00
cd5eebd08f [1840]생성되지 않은 팔작지붕 도면 사례 2026-04-02 14:45:02 +09:00
67267170ae Merge pull request '[1480]사의 각도 설정을 "각도"로 했을 때 발생하는 문제' (#750) from dev into prd-deploy
Reviewed-on: #750
2026-04-02 10:30:11 +09:00
dffb8c0dfb [1480]사의 각도 설정을 "각도"로 했을 때 발생하는 문제 2026-04-02 10:29:10 +09:00
76e508869c Merge pull request 'dev' (#748) from dev into prd-deploy
Reviewed-on: #748
2026-04-01 18:38:34 +09:00
0e4b0b410f 혹장 계산 roofLine 2026-04-01 18:10:58 +09:00
e04ff156d6 Revert "수동 배치 시 자동 물떼세 배치 한다로 수정"
This reverts commit b1c35376da012c1b685f7b58a0b14b3ffddf5425.
2026-04-01 17:54:49 +09:00
9d491bd8aa 1830 모듈 설치영역 대각 있는 현상 수정 2026-04-01 17:54:49 +09:00
13f10fd18e Revert "수동 배치 시 자동 물떼세 배치 한다로 수정"
This reverts commit b1c35376da012c1b685f7b58a0b14b3ffddf5425.
2026-04-01 17:48:25 +09:00
2101d30d98 1830 모듈 설치영역 대각 있는 현상 수정 2026-04-01 17:11:58 +09:00
1d449737f6 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into feature/redo-undo 2026-04-01 15:55:01 +09:00
d68f92c5d5 redo undo 작업중 2026-04-01 15:54:55 +09:00
f87bfb902e [1423]설계 가져오기 기능에 대해 2026-04-01 15:14:14 +09:00
735fb360be Merge pull request 'dev' (#744) from dev into prd-deploy
Reviewed-on: #744
2026-04-01 13:13:43 +09:00
44aca56d75 물건검색에 1차점 숨김(2차점 로그인) 2026-04-01 11:28:28 +09:00
984401881d 확대축소100%가운데정렬 2026-03-31 16:34:54 +09:00
bb775b2847 errSave api 추가 2026-03-31 16:27:26 +09:00
8d9379c3df 확대축소100%가운데정렬 2026-03-31 10:54:18 +09:00
910974f81b getRoofMaterialList api에러10번시도 2026-03-31 10:18:46 +09:00
0483f7487a Merge pull request '[1480]사의 각도 설정을 "각도"로 했을 때 발생하는 문제2' (#740) from dev into prd-deploy
Reviewed-on: #740
2026-03-30 18:50:11 +09:00
4a270ba57e [1480]사의 각도 설정을 "각도"로 했을 때 발생하는 문제2 2026-03-30 18:49:06 +09:00
66e1127ed0 Merge pull request '[1480]사의 각도 설정을 "각도"로 했을 때 발생하는 문제' (#738) from dev into prd-deploy
Reviewed-on: #738
2026-03-30 18:35:56 +09:00
2514fc9214 [1480]사의 각도 설정을 "각도"로 했을 때 발생하는 문제 2026-03-30 18:34:57 +09:00
fa1ff11690 Merge pull request 'dev' (#736) from dev into prd-deploy
Reviewed-on: #736
2026-03-30 16:38:57 +09:00
0a2e2bb1fe Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into hotfix/9999 2026-03-30 16:37:09 +09:00
13cd92114f top,bottom = in, 할당추가 2026-03-30 16:36:46 +09:00
b1c35376da 수동 배치 시 자동 물떼세 배치 한다로 수정 2026-03-30 15:37:12 +09:00
2a09afc791 Merge pull request 'dev' (#734) from dev into prd-deploy
Reviewed-on: #734
2026-03-27 16:24:45 +09:00
f2b527b77c top,bottom = in, 할당제외 2026-03-27 16:23:06 +09:00
7b8b995e1c 1805 설치영역 제대로 설치 안되는 현상 수정 2026-03-27 10:46:38 +09:00
66d05f4f30 Merge pull request 'dev' (#732) from dev into prd-deploy
Reviewed-on: #732
2026-03-27 09:48:25 +09:00
6cf556002e [1807]거치대 선택 시 ‘낙설 방지 금구 설치’가 자동으로 체크되는 현상2 2026-03-27 09:47:19 +09:00
1681fd8512 [1807]거치대 선택 시 ‘낙설 방지 금구 설치’가 자동으로 체크되는 현상 2026-03-27 09:22:59 +09:00
fdfa8d5281 #1805 겹치는 선 있을 경우 모듈 설치영역 이상 수정 2026-03-26 17:38:53 +09:00
048c30639c Merge pull request 'dev' (#729) from dev into prd-deploy
Reviewed-on: #729
2026-03-26 10:39:49 +09:00
76d3637a65 [1798][배치면 그리기]의 오류 2026-03-26 10:37:26 +09:00
6967b8cda0 外壁の編集とオフセット 토글추가 2026-03-26 10:12:48 +09:00
84ac6d508f 棟線移動・桁上げ下げ 토글추가 2026-03-26 10:01:08 +09:00
94f050d917 軒/ケラバ変更 토글추가 2026-03-26 09:57:28 +09:00
2c19a208cd 補助線の作成 토글추가 2026-03-26 09:36:50 +09:00
a0cac79574 補助線の作成 토글추가 2026-03-26 09:34:08 +09:00
111ce5e16c Merge pull request 'dev' (#727) from dev into prd-deploy
Reviewed-on: #727
2026-03-25 18:24:52 +09:00
c7238b2e27 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into feature/redo-undo 2026-03-23 10:30:33 +09:00
56b4ee9563 [1766]방위 변경 체크박스를 넣었을 때 견적서 표기 방법에 대해(1) 2026-03-20 18:09:50 +09:00
10d9079ca5 처마커버 설치이상 현상 수정 2026-03-20 18:04:27 +09:00
f3bddbea75 [1766]방위 변경 체크박스를 넣었을 때 견적서 표기 방법에 대해 2026-03-20 14:41:43 +09:00
18083f8913 처마커버 설치이상 현상 수정 2026-03-20 10:19:00 +09:00
3365000b87 물건등록- 계산기 토클버튼 추가 2026-03-19 17:58:46 +09:00
662a4653fc 물건등록- 계산기 토클버튼 추가 2026-03-19 17:57:43 +09:00
ea849ee5f2 견적서- 계산기 토클버튼 추가 2026-03-19 17:42:51 +09:00
bc389e3a3c サイズ変更 - 계산기 토클버튼 추가 2026-03-19 17:19:10 +09:00
b10fc27014 モジュール/架台設定 - 계산기 토클버튼 추가 2026-03-19 17:15:20 +09:00
025b7ede7c 配置面初期設定 - 계산기 토클버튼 추가 2026-03-19 16:51:36 +09:00
e07139947b Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into feature/redo-undo
# Conflicts:
#	src/hooks/surface/useSurfaceShapeBatch.js
2026-03-19 16:37:40 +09:00
ebc6181bd4 屋根形状の設定 - 계산기 토클버튼 추가 2026-03-19 16:33:33 +09:00
678b9442cd オブジェクト配置 - 계산기 토클버튼 추가 2026-03-19 16:02:56 +09:00
739a9e7412 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into feature/1466 2026-03-19 15:50:22 +09:00
8d1e661cbb Merge pull request '[1751] 기와 형태에서 처마 돌출 치수를 변경했을 때의 동작' (#724) from dev into prd-deploy
Reviewed-on: #724
2026-03-19 13:39:08 +09:00
1dd4e0cf83 [1751] 기와 형태에서 처마 돌출 치수를 변경했을 때의 동작 2026-03-19 13:35:14 +09:00
a11010c00a #1749 모듈 배치 후 회로 설정 오류 수정 2026-03-19 10:38:03 +09:00
a701f3c066 Merge pull request 'dev' (#722) from dev into prd-deploy
Reviewed-on: #722
2026-03-18 16:50:15 +09:00
ea2c900c83 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev 2026-03-18 16:41:22 +09:00
606acf9189 배치면 다수 있을 경우 길이계산 안되는현상 수정 2026-03-18 16:41:08 +09:00
6fb7caa357 Merge pull request 'dev' (#720) from dev into prd-deploy
Reviewed-on: #720
2026-03-18 16:30:00 +09:00
a9791eac62 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into hotfix/9999 2026-03-18 16:28:17 +09:00
0e70c3afea [1455]처마 커버 표시 2026-03-18 16:27:55 +09:00
f29d3aa147 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev 2026-03-18 16:17:23 +09:00
3c702cf5f3 회전 시 길이 계산 반영 안되는 현상 수정 2026-03-18 16:17:15 +09:00
1fa6340d88 Merge pull request '[카톡]견적서 1차점 숨김 - 2차점 로그인' (#718) from dev into prd-deploy
Reviewed-on: #718
2026-03-18 14:32:19 +09:00
a10196f982 [카톡]견적서 1차점 숨김 - 2차점 로그인 2026-03-18 14:31:21 +09:00
af92ffcbaa Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into feature/1466 2026-03-18 13:26:22 +09:00
a47f96a52e Merge pull request '1525 면형상배치 치수 표기 이상' (#716) from dev into prd-deploy
Reviewed-on: #716
2026-03-18 13:24:14 +09:00
f77794ceed 1525 면형상배치 치수 표기 이상 2026-03-18 10:54:24 +09:00
7e08184227 undo redo 작업중 2026-03-18 10:13:12 +09:00
617c8ef24e Merge pull request '[1482]도면에서 흐름 방향이 좌우로 설정된 경우 표시가 일부 잘리는 현상' (#714) from dev into prd-deploy
Reviewed-on: #714
2026-03-17 09:27:31 +09:00
5f4673dd3f Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into hotfix/9999 2026-03-17 09:25:42 +09:00
4b098fa5d5 [1482]도면에서 흐름 방향이 좌우로 설정된 경우 표시가 일부 잘리는 현상 2026-03-17 09:24:56 +09:00
4ac29ddf90 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into feature/1466 2026-03-17 08:16:23 +09:00
bbcea68c36 Merge pull request '[1482]플랜을 복사했을 때, 출력된 견적서에 [하제피치]가 표시되는 경우가 있다' (#712) from dev into prd-deploy
Reviewed-on: #712
2026-03-16 18:25:18 +09:00
df7ba927e8 Merge pull request '[1482]플랜을 복사했을 때, 출력된 견적서에 [하제피치]가 표시되는 경우가 있다' (#710) from hotfix/9999 into dev
Reviewed-on: #710
2026-03-16 18:24:27 +09:00
e912d0848d [1482]플랜을 복사했을 때, 출력된 견적서에 [하제피치]가 표시되는 경우가 있다 2026-03-16 18:23:25 +09:00
1d4d1ab381 계산기 토글 버튼 추가 2026-03-13 16:44:59 +09:00
431b7216c5 Merge pull request 'dev' (#709) from dev into prd-deploy
Reviewed-on: #709
2026-03-12 17:12:56 +09:00
64ba5ad36e 플랜별 지붕재 변경저장 2026-03-12 13:39:43 +09:00
63578276aa fetchBasicSettings에서 correntObjectNo를 Recoil state에서 직접 읽고 있어서, 새 오브젝트가 생성될 때 아직 이전 값이 남아있는 stale closure 문제 2026-03-12 12:54:18 +09:00
2cc2814709 roofWidth,roofHeight null문제 2026-03-12 11:08:17 +09:00
32aa8f9b32 Merge pull request '[1470] 배치도·계통도 및 구조도의 화질 개선 cell 이미지 추가' (#707) from dev into prd-deploy
Reviewed-on: #707
2026-03-11 18:27:46 +09:00
be95b1b39b [1470] 배치도·계통도 및 구조도의 화질 개선 cell 이미지 추가 2026-03-11 18:19:35 +09:00
2bad7e313a Merge pull request 'dev' (#705) from dev into prd-deploy
Reviewed-on: #705
2026-03-10 15:08:27 +09:00
07ce6b6f4d [1456]배치면 초기 설정에서의 지붕재와 촌 위치 변경 2026-03-10 15:06:55 +09:00
b3e85a11dc Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev-yscha 2026-03-10 12:33:05 +09:00
19b45b4810 지붕면설정창 확인 2026-03-10 12:32:17 +09:00
670f2a4193 Fix typo: 'ues client' -> 'use client' 2026-03-10 12:29:26 +09:00
aef0a41243 Merge pull request 'dev' (#703) from dev into prd-deploy
Reviewed-on: #703
2026-03-09 15:38:35 +09:00
e3a1393603 Merge pull request 'error 메시지 처리' (#701) from dev-yscha into dev
Reviewed-on: #701
2026-03-09 15:35:13 +09:00
bafd3787a8 error 메시지 처리 2026-03-09 15:32:30 +09:00
e24c9e9814 Merge pull request 'dev' (#700) from dev into prd-deploy
Reviewed-on: #700
2026-03-09 14:28:53 +09:00
161f9832cd Merge pull request 'error 메시지 처리' (#697) from dev-yscha into dev
Reviewed-on: #697
2026-03-09 14:27:12 +09:00
9ade7e0161 error 메시지 처리 2026-03-09 14:25:49 +09:00
6f5513a5e1 Merge pull request 'dev' (#696) from dev into prd-deploy
Reviewed-on: #696
2026-03-09 14:24:01 +09:00
c34037eb73 Merge pull request '500에러 발생확인' (#694) from dev-yscha into dev
Reviewed-on: #694
2026-03-09 14:23:18 +09:00
9ec59bdabb 500에러 발생확인 2026-03-09 14:20:35 +09:00
d30d92df0b Merge pull request 'dev' (#693) from dev into prd-deploy
Reviewed-on: #693
2026-03-09 13:38:14 +09:00
b1e823083e 1524 모듈 배치영역 생성 오류 수정 2026-03-09 12:28:23 +09:00
b735a6f54a #1522 모듈 단삽입 오류 수정 2026-03-09 11:11:57 +09:00
942470dc76 #1523 보조선 길이 업데이트 오류 수정 2026-03-09 10:34:53 +09:00
d4cca0a54d #1525 면형상 배치 길이 다른 현상 수정 2026-03-09 10:26:03 +09:00
516d3b32a1 #1526 보조선 이용해서 할당 시 길이 계산 안되는 현상 수정 2026-03-09 10:05:39 +09:00
abc61b2a0c Merge pull request 'dev' (#691) from dev into prd-deploy
Reviewed-on: #691
2026-03-06 18:44:11 +09:00
69f0c5a8b2 점선그리드 확대 축소에 영향 받는 현상 수정 2026-03-05 18:03:50 +09:00
f5649fb625 #1519 【HANASYS DESIGN】[配置面]で、吸着点をクリックして屋根面を作成した時の挙動 배치면 스냅포인트 지붕면 생성 시 길이 계산 수정 2026-03-05 15:44:30 +09:00
e9ed7169ed 임의그리드 잘리는 현상 수정 2026-03-05 13:28:25 +09:00
85896212ed 점그리드 다시 표시 2026-03-05 12:38:21 +09:00
bea4d8ac3c Merge pull request 'dev' (#688) from dev into prd-deploy
Reviewed-on: #688
2026-03-04 18:25:38 +09:00
3fdd9411e4 #1511 【HANASYS DESIGN】東西方向のモジュール配置を自動で行うと、間隔が狭くなる事象
동,서 방향 모듈 설치 제대로 안되는 현상 수정
2026-03-04 14:43:28 +09:00
4c248f5766 [1514]sk수정2 2026-03-04 13:53:47 +09:00
8f5b2c3730 [1514]sk수정 2026-03-04 12:58:56 +09:00
4b136ea2ed Merge pull request '삭제' (#684) from dev into prd-deploy
Reviewed-on: #684
2026-03-03 21:00:39 +09:00
e0ba5e5abb 삭제 2026-03-03 20:59:24 +09:00
b7c84d677b Merge pull request '삭제' (#682) from dev into prd-deploy
Reviewed-on: #682
2026-03-03 20:25:34 +09:00
d35d65f688 삭제 2026-03-03 20:22:51 +09:00
bd6de977bd Merge pull request '!!!' (#679) from dev into prd-deploy
Reviewed-on: #679
2026-03-03 19:55:28 +09:00
bbe5418f1c !!! 2026-03-03 19:54:40 +09:00
1638d0ae33 Resolve merge conflicts between dev and prd-deploy 2026-03-03 19:47:20 +09:00
61dc83d7db !!! 2026-03-03 19:30:23 +09:00
8d91a78dee 2차점 이상이 들어온 경우 물건번호에서 1차판매점 영역 비활성화 처리 2026-03-03 19:23:18 +09:00
7801f5a2d1 rackInfos is null: 랙을 그리려고 할 때 랙 정보가 없음 2026-03-03 19:23:18 +09:00
ab04911ac6 [1386] 1,2차 수정 2026-03-03 19:23:18 +09:00
95811760dc [1386] 1,2차 수정 2026-03-03 19:23:17 +09:00
884d674046 Merge pull request '2차점 이상이 들어온 경우 물건번호에서 1차판매점 영역 비활성화 재처리' (#675) from feature/qcast-1512 into dev
Reviewed-on: #675
2026-03-03 18:57:11 +09:00
162b75bf2e 2차점 이상이 들어온 경우 물건번호에서 1차판매점 영역 비활성화 재처리 2026-03-03 18:56:01 +09:00
f08ec28ea3 Merge pull request '2차점 이상이 들어온 경우 물건번호에서 1차판매점 영역 비활성화 처리' (#673) from feature/qcast-1512 into dev
Reviewed-on: #673
2026-03-03 18:45:37 +09:00
67c15fc454 2차점 이상이 들어온 경우 물건번호에서 1차판매점 영역 비활성화 처리 2026-03-03 18:44:36 +09:00
418616ba97 rackInfos is null: 랙을 그리려고 할 때 랙 정보가 없음 2026-03-02 12:32:36 +09:00
b81540fe62 [1386] 1,2차 수정 2026-02-26 16:44:00 +09:00
26202558a5 [1386] 1,2차 수정 2026-02-26 15:48:35 +09:00
1a2fa7bbf6 sk개선 2026-02-25 17:51:28 +09:00
b549693e8e sk개선 2026-02-25 17:49:26 +09:00
5a64ff945a #1478 외벽선 작성으로 지붕면 할당 시 계산 오류 수정 2026-02-25 10:32:19 +09:00
022278a248 [1506] 동,현이동 sk 문제 2026-02-25 10:32:19 +09:00
c1bd6db634 #1478 외벽선 작성으로 지붕면 할당 시 계산 오류 수정 2026-02-25 10:29:51 +09:00
34a4980db8 [1506] 동,현이동 sk 문제 2026-02-25 10:11:21 +09:00
188e6ee2f2 [1475] 플랜복사(다중지붕 복사안됨 - 단일지붕) 2026-02-23 17:29:30 +09:00
f08b3cef5b [1475] 플랜복사(다중지붕 복사안됨 - 단일지붕) 2026-02-23 17:28:15 +09:00
fc9623d76d [1480] 설정값 오류 (이전플랜저장 -> 현플랜저장) 2026-02-23 16:53:45 +09:00
64db226721 이동시 spare 0.5 추가 2026-02-23 16:53:45 +09:00
f64d5c438e [1490]메뉴 표기 변경 요청 (실측값입력-> 배치면) 2026-02-23 16:53:45 +09:00
d1045fa0df Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev_ysCha 2026-02-23 16:48:41 +09:00
9ada34c020 [1480] 설정값 오류 (이전플랜저장 -> 현플랜저장) 2026-02-23 16:48:16 +09:00
4bf90e7dfb 이동시 spare 0.5 추가 2026-02-23 11:40:34 +09:00
4ce66733dd [1490]메뉴 표기 변경 요청 (실측값입력-> 배치면) 2026-02-23 10:40:37 +09:00
7836da9d43 rack정보 제대로 못가져오는 현상 수정 2026-02-23 09:09:03 +09:00
df4b2a889b rack정보 제대로 못가져오는 현상 수정 2026-02-20 11:41:19 +09:00
9944c92351 치수입력방식에 따른 페이지구분 2026-02-13 13:14:12 +09:00
c2b4f5c7df 치수입력방식에 따른 페이지구분 2026-02-13 13:09:08 +09:00
14dc84502c [1386]T01 계정이 작성한 안건 중 해당 판매점 ID가 열람 가능한 것에 한합니다. 2026-02-13 08:26:25 +09:00
89cefff5e4 Merge pull request '메뉴를 찾아가지 못하는 오류 발생' (#660) from dev into prd-deploy
Reviewed-on: #660
2026-02-12 15:58:00 +09:00
55e201db9c 메뉴를 찾아가지 못하는 오류 발생 2026-02-12 15:50:27 +09:00
ab9920998e Merge pull request '[1455]처마커버 표시문제' (#658) from dev into prd-deploy
Reviewed-on: #658
2026-02-12 15:04:53 +09:00
cf84e51f24 [1455]처마커버 표시문제 2026-02-12 15:02:55 +09:00
af6e623ed1 Merge pull request '간격이 다를 때 스켈레톤이 문제' (#656) from dev into prd-deploy
Reviewed-on: #656
2026-02-12 14:24:55 +09:00
4b635493fe 간격이 다를 때 스켈레톤이 문제 2026-02-12 14:22:41 +09:00
de83e75a8c Merge pull request 'dev' (#654) from dev into prd-deploy
Reviewed-on: #654
2026-02-11 18:39:26 +09:00
0f59fa2b36 지붕면 회전 시 길이 계산 수행하도록 수정 2026-02-11 15:26:46 +09:00
2cef7546ab Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev 2026-02-11 15:24:04 +09:00
a92f335a83 지붕재 변경 시 길이 계산 강제 실행 2026-02-11 15:23:58 +09:00
aa302aaa8d Merge pull request 'dev' (#652) from dev into prd-deploy
Reviewed-on: #652
2026-02-10 17:56:38 +09:00
b4baaa06dd 전화번호 0000-000-000 추가 2026-02-10 17:55:31 +09:00
256189fe40 배치면 초기 설정 후 길이 계산 오류 수정, 배치면 복사 시 길이 이상 수정 2026-02-10 11:44:34 +09:00
a124f8dafb Merge pull request '케라바 지붕 출폭 0일때 문제 발생 사항 처리.' (#650) from dev into prd-deploy
Reviewed-on: #650
2026-02-09 17:49:21 +09:00
Jaeyoung Lee
67e62ee905 케라바 지붕 출폭 0일때 문제 발생 사항 처리. 2026-02-09 17:47:07 +09:00
2b15dfb801 Merge pull request '특수문자 줄임' (#648) from dev into prd-deploy
Reviewed-on: #648
2026-02-09 13:48:52 +09:00
c593f007c8 특수문자 줄임 2026-02-09 13:20:47 +09:00
4d8ec15090 Merge pull request '[1449]3.변별 offset 설정 sk 설정' (#646) from dev into prd-deploy
Reviewed-on: #646
2026-02-06 15:11:50 +09:00
d43d18088a [1449]3.변별 offset 설정 sk 설정 2026-02-06 15:08:21 +09:00
d041f00363 Merge pull request 'dev' (#644) from dev into prd-deploy
Reviewed-on: #644
2026-02-04 17:50:55 +09:00
43df05beee 정가 추가 2026-02-04 16:31:53 +09:00
d2c56ceff7 serQtyList가 없는 상태에서 find를 호출해 에러 발생 2026-02-04 16:31:23 +09:00
f1efc6b561 [1457] 기본폰트 변경 2026-02-04 16:30:06 +09:00
de5370c2d3 Merge pull request '배치면의 경우 실제 길이 계산 시 통일 제거, 모듈 설치 시 영역 검사 spare 엄격하게 적용' (#642) from dev into prd-deploy
Reviewed-on: #642
2026-02-04 11:40:09 +09:00
9f0044fe67 배치면의 경우 실제 길이 계산 시 통일 제거, 모듈 설치 시 영역 검사 spare 엄격하게 적용 2026-02-04 10:50:15 +09:00
547e516528 Merge pull request '[1455] 처마 커버 표시' (#640) from dev into prd-deploy
Reviewed-on: #640
2026-02-04 10:15:01 +09:00
676af34229 [1455] 처마 커버 표시 2026-02-04 10:14:12 +09:00
1438ca59d1 Merge pull request '[1447] 전체 종류의 대각선 입력' (#638) from dev into prd-deploy
Reviewed-on: #638
2026-02-04 09:56:04 +09:00
73e4830342 [1447] 전체 종류의 대각선 입력 2026-02-04 09:51:40 +09:00
8476bbb883 Merge pull request 'dev' (#636) from dev into prd-deploy
Reviewed-on: #636
2026-02-03 18:46:38 +09:00
255d7628db Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev 2026-02-03 17:18:28 +09:00
7b1d5357a6 forceUpdate 추가 2026-02-03 17:18:21 +09:00
9680824b87 Merge pull request '[1447]실측값 입력의 대각선 입력' (#634) from dev into prd-deploy
Reviewed-on: #634
2026-02-03 14:36:04 +09:00
7de45b1ee0 [1447]실측값 입력의 대각선 입력 2026-02-03 14:34:54 +09:00
383e1badbf Merge pull request '아이디 신청시 영문, 숫자, 특수문자만..입력가능' (#632) from dev into prd-deploy
Reviewed-on: #632
2026-02-03 14:11:40 +09:00
8efda6cb5e 아이디 신청시 영문, 숫자, 특수문자만..입력가능 2026-02-03 13:47:54 +09:00
b7020af233 Merge pull request 'outerLine 선택 시 메뉴 변경' (#630) from dev into prd-deploy
Reviewed-on: #630
2026-02-02 18:23:04 +09:00
acab90a179 outerLine 선택 시 메뉴 변경 2026-02-02 16:16:04 +09:00
fcde23dc52 Merge pull request '[144]육지봉 메뉴 임시삭제(개발, 운영)' (#628) from dev into prd-deploy
Reviewed-on: #628
2026-02-02 14:35:05 +09:00
3932055d46 [144]육지봉 메뉴 임시삭제(개발, 운영) 2026-02-02 14:34:11 +09:00
b1f7d1e408 Merge pull request 'dev' (#626) from dev into prd-deploy
Reviewed-on: #626
2026-02-02 14:30:58 +09:00
fe8f847950 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev_ysCha 2026-02-02 14:29:34 +09:00
b4efbbeb7f [1436]처마 커버의 처음 체크2 2026-02-02 14:26:39 +09:00
6cdd89eb0c [1436]처마 커버의 처음 체크2 2026-02-02 14:24:52 +09:00
79a66390b6 [1436]처마 커버의 처음 체크 2026-02-02 13:43:00 +09:00
c1a59041ba 이미지 캡쳐 시 해상력 2배 2026-02-02 13:43:00 +09:00
fe61a87af9 Merge pull request '[1436]처마 커버의 처음 체크' (#623) from dev_ysCha into dev
Reviewed-on: #623
2026-02-02 13:38:17 +09:00
f06319a6ee Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev_ysCha 2026-02-02 13:37:17 +09:00
d95e5d63e0 [1436]처마 커버의 처음 체크 2026-02-02 13:36:33 +09:00
825fca0fa1 이미지 캡쳐 시 해상력 2배 2026-02-02 10:16:26 +09:00
0257c4af85 Merge remote-tracking branch 'origin/prd-deploy' into prd-deploy 2026-01-30 18:31:09 +09:00
37fb19071c 모듈 설치 제대로 안되는 현상 수정 2026-01-30 18:30:39 +09:00
552cd7cc26 [1444]육지봉 TypeError: Cannot destructure property 'rackYn' of 'trestleDetail' as it is null. 2026-01-30 18:30:38 +09:00
7f7131a256 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev 2026-01-30 15:51:52 +09:00
c9f0a70385 모듈 설치 제대로 안되는 현상 수정 2026-01-30 15:51:46 +09:00
d643ffe2ea [1444]육지봉 TypeError: Cannot destructure property 'rackYn' of 'trestleDetail' as it is null. 2026-01-30 14:34:57 +09:00
13292d4c50 [1444]육지봉 메뉴 임시삭제(운영만) 2026-01-30 14:15:50 +09:00
8a20fee0f1 Merge pull request '[144]육지봉 메뉴 임시삭제' (#619) from dev into prd-deploy
Reviewed-on: #619
2026-01-30 14:01:14 +09:00
083d144bca [144]육지봉 메뉴 임시삭제 2026-01-30 14:00:03 +09:00
05b6c77fd6 Merge pull request 'dev' (#617) from dev into prd-deploy
Reviewed-on: #617
2026-01-30 11:40:21 +09:00
3aefa10ffd Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev_ysCha 2026-01-30 11:39:05 +09:00
a4b20b71c5 팩스 - 체크 추가 2026-01-30 11:38:34 +09:00
1207195d04 오브젝트 설치 이상 수정 및 그림자 중복 허용 2026-01-30 11:21:06 +09:00
7973c654b5 지붕면 복사 안되는 현상 수정 2026-01-30 11:16:18 +09:00
a47f5f657a 메뉴 수정 2026-01-30 10:54:14 +09:00
331a85f998 Merge pull request '[1436]처마 커버의 처음 체크' (#615) from dev into prd-deploy
Reviewed-on: #615
2026-01-29 10:21:51 +09:00
c6e0c12394 [1436]처마 커버의 처음 체크 2026-01-29 10:18:12 +09:00
09a5fdba41 Merge pull request 'dev' (#613) from dev into prd-deploy
Reviewed-on: #613
2026-01-29 09:18:34 +09:00
ba19434224 #1438 지붕면 할당 후 계산식 수정 2026-01-28 17:09:51 +09:00
ea6a17e375 길이 계산 후 렌더링 안되는 현상 수정 2026-01-28 15:24:47 +09:00
1431dd9d9e Merge pull request '점선그리드만 제거' (#611) from dev into prd-deploy
Reviewed-on: #611
2026-01-28 09:54:23 +09:00
5cbb075188 점선그리드만 제거 2026-01-27 18:26:54 +09:00
84c2642800 Merge pull request 'dev' (#609) from dev into prd-deploy
Reviewed-on: #609
2026-01-27 16:50:35 +09:00
7d92bbe4d1 Merge pull request '한글=>일본어' (#607) from dev_ysCha into dev
Reviewed-on: #607
2026-01-27 16:49:47 +09:00
b9f3b60333 한글=>일본어 2026-01-27 16:48:42 +09:00
2e4bcb98cd text 와 planSize 및 actualSize가 안맞는 문제 2026-01-27 16:48:07 +09:00
fdaefd79e9 모듈 설치 영역 꼬임 수정 2026-01-27 16:15:30 +09:00
3e97eb379b type이 null인 경우 처리 2026-01-27 10:24:31 +09:00
3bfc976df7 Merge pull request 'eaveHelpLine planSize 추가' (#606) from dev into prd-deploy
Reviewed-on: #606
2026-01-26 18:44:35 +09:00
cd1e910668 eaveHelpLine planSize 추가 2026-01-26 15:14:28 +09:00
0102559a35 Merge pull request 'dev' (#604) from dev into prd-deploy
Reviewed-on: #604
2026-01-26 15:04:37 +09:00
31715aa621 현이동저장이후 할당문제 2026-01-26 15:01:43 +09:00
68005562e3 wallLine texts제거 2026-01-26 14:04:35 +09:00
01b1c97248 #1418 보조선 삭제 용이하게 수정 2026-01-26 13:51:21 +09:00
b58a85b904 #1424 그리드 영역 우선 주석 2026-01-26 13:42:10 +09:00
7193dbcf79 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev 2026-01-26 13:40:25 +09:00
f0ecc17dff wallLine 제거되는 현상 수정 2026-01-26 13:40:17 +09:00
9fcc0396ca Merge pull request 'dev' (#602) from dev into prd-deploy
Reviewed-on: #602
2026-01-26 08:42:26 +09:00
257bae1fb0 Merge pull request 'dev_cha' (#600) from dev_cha into dev
Reviewed-on: #600
2026-01-25 23:58:14 +09:00
b6711a1bb9 actureSize - 소수점처리 2026-01-25 23:57:28 +09:00
b6258c6a2a actureSize 2026-01-25 23:42:57 +09:00
36bd66f41b Merge pull request 'dev' (#599) from dev into prd-deploy
Reviewed-on: #599
2026-01-23 17:45:05 +09:00
75326e38ee Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev 2026-01-23 17:43:11 +09:00
3284401526 pcs 자동추천 이상 수정 2026-01-23 17:43:03 +09:00
6de8e9bbba Merge pull request 'dev' (#597) from dev into prd-deploy
Reviewed-on: #597
2026-01-22 18:16:19 +09:00
1d941724db 할당변경(orange 제거) 2026-01-22 18:13:42 +09:00
f4846e62b0 eaveHelpLine actualSize 추가 2026-01-22 17:36:48 +09:00
ec5729fa8e Merge pull request 'dev' (#595) from dev into prd-deploy
Reviewed-on: #595
2026-01-20 18:32:28 +09:00
eeddc300fd Merge pull request '전화번호 - 포함, 팩스필수' (#593) from dev_ysCha into dev
Reviewed-on: #593
2026-01-20 15:03:51 +09:00
2316e92cfe 전화번호 - 포함, 팩스필수 2026-01-20 15:03:03 +09:00
6268234324 Merge pull request 'dev' (#592) from dev into prd-deploy
Reviewed-on: #592
2026-01-19 15:40:30 +09:00
5cebb8a530 Merge pull request 'dev_ysCha' (#590) from dev_ysCha into dev
Reviewed-on: #590
2026-01-19 15:39:36 +09:00
578f6e8bd4 Merge remote-tracking branch 'origin/dev_ysCha' into dev_ysCha 2026-01-19 15:37:53 +09:00
bb47f14d95 [1413] 지붕재 목록 api 호출 다수 => 한번 2026-01-19 15:37:39 +09:00
0f0af23131 Merge pull request 'dev' (#588) from dev into prd-deploy
Reviewed-on: #588
2026-01-19 13:41:48 +09:00
a98fb2560f Merge pull request 'dev' (#587) from dev into dev_ysCha
Reviewed-on: #587
2026-01-19 13:41:29 +09:00
87a283c79d Merge pull request '이메일 30자 -> 50자' (#586) from dev_ysCha into dev
Reviewed-on: #586
2026-01-19 13:40:52 +09:00
97b3a8c2ba Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev_ysCha 2026-01-19 13:39:33 +09:00
a687be997f 이메일 30자 -> 50자 2026-01-19 13:39:11 +09:00
b361ccf4b7 세로로 긴 경우 이미지 잘리는 현상 수정 2026-01-19 10:23:12 +09:00
8c66a20f3a Merge pull request 'dev' (#585) from dev into prd-deploy
Reviewed-on: #585
2026-01-19 09:20:35 +09:00
76e8e581db [1410] : [HANASYS DESIGN] [북면]으로 취급하는 범위에 대해 늘어남에 따라 계산 수정 2026-01-16 17:23:42 +09:00
310068bf6a circuitTestle 폰트 수정 제거 2026-01-16 09:51:32 +09:00
a092132b9f Merge pull request 'dev' (#583) from dev into prd-deploy
Reviewed-on: #583
2026-01-15 18:34:18 +09:00
d95dcf608d 동쪽 치도리 배치시 문제 수정 2026-01-15 14:36:26 +09:00
6c23240345 logout 호출 시 전부 login 으로 튕기도록 수정 2026-01-15 10:26:39 +09:00
8475dc4b1f 패스워드 변경 팝업 변경안함 클릭 시 멈춤현상 수정 2026-01-15 10:24:50 +09:00
c501896f59 선 작성 시 기본 attributes.planeSize 추가 2026-01-15 09:55:52 +09:00
a0964efa08 Merge pull request 'dev' (#581) from dev into prd-deploy
Reviewed-on: #581
2026-01-14 12:23:17 +09:00
4fdd7d55d0 형올림내림 시 오류 수정 2026-01-13 13:44:29 +09:00
0f481e2933 선이 가지고있는 planeSize로 계산하도록 수정 2026-01-13 13:32:45 +09:00
8a1e0fa638 Merge pull request 'dev' (#579) from dev into prd-deploy
Reviewed-on: #579
2026-01-12 16:57:33 +09:00
65e268f3d6 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev 2026-01-12 16:10:31 +09:00
f91afaed3a pcs가 한개인 경우 selectbox 제거 2026-01-12 16:10:27 +09:00
4133f1b512 Merge pull request 'dev' (#577) from dev into prd-deploy
Reviewed-on: #577
2026-01-12 15:39:51 +09:00
363c19d9a0 Merge pull request '[1383]견적서 아이템 인코딩' (#575) from dev_ysCha into dev
Reviewed-on: #575
2026-01-12 15:38:56 +09:00
3dd96d043f [1383]견적서 아이템 인코딩 2026-01-12 15:38:21 +09:00
aa837d8dbf 블럭처리 2026-01-12 11:12:29 +09:00
d59e64c5bb Merge pull request 'dev' (#573) from dev into prd-deploy
Reviewed-on: #573
2026-01-12 11:10:18 +09:00
bdd7a475d5 1차점 생성 2차점 가격 미노출 2026-01-12 11:08:49 +09:00
a368c8d40d Merge pull request '1차점에서 만든 견적서 2차점 가격 미노출 설정' (#571) from dev_cha into dev
Reviewed-on: #571
2026-01-12 09:05:06 +09:00
e61913355e 1차점에서 만든 견적서 2차점 가격 미노출 설정 2026-01-11 15:24:43 +09:00
c4b03c6745 diff 2로 수정 2026-01-09 17:28:02 +09:00
Jaeyoung Lee
1ccabd3efb Merge branch 'dev' into feature/design-remake 2026-01-09 17:15:59 +09:00
Jaeyoung Lee
9369c1c952 qline 실치수 표시 부분 수정 2026-01-09 17:15:18 +09:00
f2b59553ad Merge pull request '시작 전 quotationParam null 세팅 추가' (#570) from dev into prd-deploy
Reviewed-on: #570
2026-01-09 17:01:02 +09:00
5a73b3787f 시작 전 quotationParam null 세팅 추가 2026-01-09 15:33:04 +09:00
db34bcb9a5 Merge pull request 'dev' (#568) from dev into prd-deploy
Reviewed-on: #568
2026-01-09 10:37:48 +09:00
04f06702a8 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev_ysCha 2026-01-09 10:34:17 +09:00
ed63cc664c YJSS 숨김처리 2026-01-09 10:33:49 +09:00
af90e0fa36 오류 수정 2026-01-09 09:53:40 +09:00
ff505130a2 Merge pull request 'dev' (#566) from dev into prd-deploy
Reviewed-on: #566
2026-01-08 19:04:31 +09:00
6a276c4bd7 Merge pull request '한글 -> 일어변경' (#564) from dev_ysCha into dev
Reviewed-on: #564
2026-01-08 19:03:38 +09:00
174a31aa72 한글 -> 일어변경 2026-01-08 19:02:48 +09:00
3f0e639877 Merge pull request 'dev' (#563) from dev into prd-deploy
Reviewed-on: #563
2026-01-08 18:15:37 +09:00
0edc7bd161 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev_ysCha 2026-01-08 18:12:30 +09:00
de707796a3 더블계산패드노출 2026-01-08 18:12:10 +09:00
fbb1f352e9 벽 라인 wallLine 속성 변경으로 수정 2026-01-08 17:03:14 +09:00
10bdc6e8ab Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev 2026-01-08 16:41:18 +09:00
8d5d36fce6 실치수 계산 시 소수점 적용 문제로 인한 수정 모든 roof lines를 확인하여 length 통일 2026-01-08 16:41:11 +09:00
a2c02f6213 Merge pull request 'dev' (#561) from dev into prd-deploy
Reviewed-on: #561
2026-01-08 15:19:11 +09:00
916a6caf5a Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev_ysCha 2026-01-08 15:18:07 +09:00
12f442b3ed 반영 변경 2026-01-08 15:17:45 +09:00
9683b38cf0 방향키에 숫자패드 숨김 2026-01-08 15:15:17 +09:00
49253b7668 1390 【HANASYS DESIGN】복도에서 작성한 경우 치수 취급 2026-01-08 15:04:00 +09:00
a258f61093 탭키, 방향키 계산 2026-01-08 14:46:21 +09:00
ddd3eaf82a 치수 표시 선 1의 자리 0으로 나오는 현상 수정 2026-01-08 12:54:00 +09:00
3a3afe9b3b 실내집중형 북면모듈 오류 수정 2026-01-08 10:26:24 +09:00
dd2ee6385a Merge pull request '초기화' (#559) from dev into prd-deploy
Reviewed-on: #559
2026-01-08 10:18:14 +09:00
4651ebc365 초기화 2026-01-08 10:15:44 +09:00
dc7f5e25e7 Merge pull request 'dev' (#557) from dev into prd-deploy
Reviewed-on: #557
2026-01-07 17:53:52 +09:00
861fd31825 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev_ysCha 2026-01-07 17:52:37 +09:00
a5dc5caaf3 각도계산 초기화 2026-01-07 17:51:11 +09:00
47de1ef61d azimuth: string -> int 계산식 2026-01-07 17:49:37 +09:00
c499653e79 북면 모듈 설치 여부 추가, 북면 모듈 설치 시 동면 제외 2026-01-07 16:59:30 +09:00
5ac023d72d [HANASYS DESIGN] 회전 기능을 이용했을 때의 불량 2026-01-07 10:35:24 +09:00
6872c6bb16 배치면 그리기, 외벽선 그리기 각도 문제 해결 2026-01-07 10:12:25 +09:00
bff666914c font 설정 오류 수정 2026-01-07 10:07:31 +09:00
cdec0bbae0 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev_ysCha 2026-01-06 17:49:06 +09:00
47078a2205 maxHeight 추가 2026-01-06 17:47:30 +09:00
c8d0dd30e5 폰트 설정 오류 수정 2026-01-06 16:50:48 +09:00
6ec7355045 Merge pull request '방위각 제한 (-180 ~ 180) : string 제한' (#554) from dev into prd-deploy
Reviewed-on: #554
2026-01-06 14:40:16 +09:00
2b29a62616 방위각 제한 (-180 ~ 180) : string 제한 2026-01-06 14:39:18 +09:00
bdb8400d10 Merge pull request 'dev' (#552) from dev into prd-deploy
Reviewed-on: #552
2026-01-06 13:47:20 +09:00
72e4aded51 계산기패드 초기화안되는 문제 2026-01-06 13:46:14 +09:00
a128b0b5bb 방위각 제한 (-180 ~ 180) 2026-01-06 12:22:57 +09:00
faba67b232 Merge pull request 'dev' (#550) from dev into prd-deploy
Reviewed-on: #550
2026-01-05 19:02:58 +09:00
295694ee82 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev_ysCha 2026-01-05 19:00:04 +09:00
faa619d124 메인에서 새로고침 chagePasswordPopOpen 2026-01-05 18:59:43 +09:00
Jaeyoung Lee
eec659257a Merge branch 'dev' into feature/design-remake
# Conflicts:
#	src/common/common.js
#	src/hooks/roofcover/useRoofAllocationSetting.js
2026-01-05 15:15:02 +09:00
Jaeyoung Lee
8f421d08da 변별로 처리 시 용마루 하단지붕 파생 라인 처리 및 불필요 코드 제거 2026-01-05 15:11:53 +09:00
3ba8145d30 Merge pull request '회전 후 잘리는 현상 수정' (#548) from dev into prd-deploy
Reviewed-on: #548
2026-01-05 09:27:31 +09:00
a74789d8e8 회전 후 잘리는 현상 수정 2026-01-02 14:37:08 +09:00
2f58455802 Merge pull request '할당처리' (#546) from dev into prd-deploy
Reviewed-on: #546
2025-12-31 15:42:41 +09:00
fd8cbf726c 할당처리 2025-12-31 15:41:46 +09:00
af44dfaa70 Merge pull request 'dev' (#544) from dev into prd-deploy
Reviewed-on: #544
2025-12-31 15:40:20 +09:00
7557f61130 북면 설치 가능 모듈은 단독으로만 가능함 2025-12-31 14:17:45 +09:00
1524766b7e 외벽선 없는 경우 처리 추가 2025-12-31 13:52:50 +09:00
Jaeyoung Lee
5131e20a5c Merge branch 'feature/design-remake' into dev 2025-12-31 13:19:32 +09:00
Jaeyoung Lee
5bbf372e47 지붕 보조선 버그 수정 2025-12-31 13:19:12 +09:00
9e867ba53a 단 이동 시 복사로 표기되는 오류 수정 2025-12-31 10:42:46 +09:00
1889c58d0f Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev 2025-12-31 10:42:38 +09:00
f0e70ec2df 단 이동 시 복사로 표기되는 오류 수정 2025-12-31 10:42:00 +09:00
606b866b43 Merge pull request 'getAddLine(newPStart, newPEnd, 'red') 이동 - 동시변경' (#542) from dev into prd-deploy
Reviewed-on: #542
2025-12-31 10:20:54 +09:00
d25ab38f11 getAddLine(newPStart, newPEnd, 'red') 이동 - 동시변경 2025-12-31 10:05:51 +09:00
a730138f9f Merge pull request '수동 회로 설정 로직 수정' (#540) from dev into prd-deploy
Reviewed-on: #540
2025-12-31 08:28:11 +09:00
2c7dcbc57d 수동 회로 설정 로직 수정 2025-12-30 17:01:24 +09:00
cbdeb88c43 Merge pull request 'session의 StoreId => 1,2차점 storeId' (#538) from dev into prd-deploy
Reviewed-on: #538
2025-12-30 14:30:23 +09:00
9cdb7ebd0f session의 StoreId => 1,2차점 storeId 2025-12-30 14:24:41 +09:00
5a6b61f2d2 Merge pull request 'dev' (#536) from dev into prd-deploy
Reviewed-on: #536
2025-12-30 11:00:43 +09:00
008a1230cc wallbaseLine merge 2025-12-30 10:57:53 +09:00
Jaeyoung Lee
f0e71df5d4 Merge branch 'dev' into feature/design-remake 2025-12-30 10:16:30 +09:00
Jaeyoung Lee
c78d3ecf6b 용마루 파생 하단 지붕 처리 중 임시 저장 2025-12-30 10:16:02 +09:00
39aa240725 Merge pull request 'dev' (#534) from dev into prd-deploy
Reviewed-on: #534
2025-12-29 22:51:45 +09:00
b9efc4aa47 모듈 설치 시 validation 추가 2025-12-29 16:56:39 +09:00
bbd3d1ec4b 모듈 복사, 제거, 추가 후 선택 안되는 현상 수정 2025-12-29 14:51:55 +09:00
953ce7535d 외주 이격 설정 오류 수정 2025-12-29 11:44:08 +09:00
c454b1936f 수동설정 시 실내집중형 로직 수정 2025-12-29 11:10:02 +09:00
eba5c4f9b2 Merge pull request 'dev' (#532) from dev into prd-deploy
Reviewed-on: #532
2025-12-24 17:30:52 +09:00
54767d2f26 northModuleYn 컬럼 추가 2025-12-24 17:27:50 +09:00
c4bcad3bc3 보조선 있는 경우 지붕선 사라지는 현상 수정 2025-12-24 15:10:42 +09:00
8215730df0 Merge pull request 'dev' (#530) from dev into prd-deploy
Reviewed-on: #530
2025-12-24 15:09:12 +09:00
984428ee4f Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev_ysCha 2025-12-24 15:05:42 +09:00
152bcb6a5b 자리수12자리 2025-12-24 15:05:17 +09:00
aef935fe48 Merge pull request 'row 선택 시 병렬 수 0으로 가는 현상 수정' (#527) from dev into prd-deploy
Reviewed-on: #527
2025-12-24 15:01:37 +09:00
c1e4fe5b10 row 선택 시 병렬 수 0으로 가는 현상 수정 2025-12-24 15:00:15 +09:00
2ed7700f66 Merge pull request 'dev' (#526) from dev into prd-deploy
Reviewed-on: #526
2025-12-24 14:25:44 +09:00
15d94bab53 abs제거 2025-12-24 14:24:30 +09:00
a6dff928c0 pcs최초 데이터 참조 수정 2025-12-24 14:20:22 +09:00
0cc722eb3b Merge pull request 'dev' (#524) from dev into prd-deploy
Reviewed-on: #524
2025-12-24 14:14:32 +09:00
456ec7643e 전체삭제 2025-12-24 14:13:30 +09:00
c8ad779c58 최초 pcs 관련 데이터 참조하도록 수정 2025-12-24 14:05:09 +09:00
34f97f04a8 Merge pull request 'onClick event 수정' (#522) from dev into prd-deploy
Reviewed-on: #522
2025-12-24 13:31:12 +09:00
92a3b15d40 onClick event 수정 2025-12-24 13:26:55 +09:00
fd65d17e9c Merge pull request 'dev' (#520) from dev into prd-deploy
Reviewed-on: #520
2025-12-24 10:42:52 +09:00
ae1ae05d09 회로 설정 수정 2025-12-24 09:50:47 +09:00
cc5734db16 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev 2025-12-24 09:40:46 +09:00
714d744d71 Merge pull request 'dev' (#518) from dev into prd-deploy
Reviewed-on: #518
2025-12-23 15:31:35 +09:00
dc080a8737 견적서에 계산기추가 2025-12-23 15:30:27 +09:00
59b65df1d6 abs() 수정 2025-12-23 15:21:29 +09:00
0ee7f6c590 계산기 2025-12-23 14:19:35 +09:00
48e6a090da Merge pull request '좌표삭제' (#516) from dev into prd-deploy
Reviewed-on: #516
2025-12-23 13:33:14 +09:00
3f9bc960b3 좌표삭제 2025-12-23 13:31:38 +09:00
b02faafb3e 도머, 그림자, 개구 이격 30으로 수정 2025-12-23 10:17:58 +09:00
72dd6ef5df Merge pull request 'dev' (#514) from dev into prd-deploy
Reviewed-on: #514
2025-12-22 18:22:19 +09:00
f8cc93d54e 할당완료 2025-12-22 18:20:54 +09:00
54ac757ced 계산기 투입 2025-12-22 17:54:07 +09:00
c7ef63152b Merge pull request 'dev' (#512) from dev into prd-deploy
Reviewed-on: #512
2025-12-22 17:34:54 +09:00
2e3ae74a82 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev_ysCha 2025-12-22 17:33:44 +09:00
6fdb0df00c 0.이하 처리 2025-12-22 17:31:45 +09:00
Jaeyoung Lee
83769c55ab Merge branch 'feature/design-remake' into dev 2025-12-22 14:45:58 +09:00
Jaeyoung Lee
8f1874c3cd 하단레벨 지붕선 작성로직 B타입 추가. 2025-12-22 14:45:23 +09:00
357e0b02ed Merge pull request '특이사항 제거' (#510) from dev into prd-deploy
Reviewed-on: #510
2025-12-22 13:20:16 +09:00
ab44be17fd 특이사항 제거 2025-12-22 13:19:09 +09:00
b33cc92daa Merge pull request '오브젝트 등록수정' (#508) from dev into prd-deploy
Reviewed-on: #508
2025-12-22 10:51:51 +09:00
e3ce0dea3f 오브젝트 등록수정 2025-12-22 10:50:43 +09:00
e992e0f194 Merge pull request 'dev' (#506) from dev into prd-deploy
Reviewed-on: #506
2025-12-22 08:55:53 +09:00
8289f7feef skt 변경 2025-12-21 00:15:18 +09:00
e958f2ee4d 마이너스 좌표 비교 2025-12-21 00:06:45 +09:00
Jaeyoung Lee
8c92f14f7d Merge branch 'feature/design-remake' into dev 2025-12-19 17:36:15 +09:00
Jaeyoung Lee
494b06bfe3 하단지붕선 작성 로직 추가. 2025-12-19 17:35:53 +09:00
Jaeyoung Lee
de005aefe7 Merge branch 'feature/design-remake' into dev 2025-12-19 14:19:21 +09:00
Jaeyoung Lee
fd024cf30b 케라바 벽취합 조합에서의 버그 처리. 2025-12-19 14:17:07 +09:00
533f7630ee Merge pull request '[1360] 일본어' (#504) from dev into prd-deploy
Reviewed-on: #504
2025-12-19 13:13:40 +09:00
67ebf0b514 [1360] 일본어 2025-12-19 13:12:41 +09:00
9c9ca8f026 Merge pull request 'dev' (#502) from dev into prd-deploy
Reviewed-on: #502
2025-12-19 09:20:44 +09:00
8be85a3f9e skt 2025121901 2025-12-19 09:19:26 +09:00
e93ecf169d Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev_ysCha 2025-12-19 08:55:58 +09:00
306c71ead2 동이동변경 2025-12-18 18:05:08 +09:00
50376446d0 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev 2025-12-18 17:59:25 +09:00
1a432913fc A,B타입 지붕재 할당 시 방향 오류 수정 2025-12-18 17:59:16 +09:00
7be6b60e12 동이동변경 2025-12-18 17:27:28 +09:00
abb498178d Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev_ysCha 2025-12-18 17:19:21 +09:00
618604b52c 가대설정: 계산기 추가 2025-12-18 16:17:28 +09:00
f202cfd33e 모듈시공: 계산기 추가 2025-12-18 16:14:31 +09:00
86852cc4cd 방향모듈설정: 계산기 추가 2025-12-18 14:32:16 +09:00
c81a679a4f Merge pull request 'dev' (#499) from dev into prd-deploy
Reviewed-on: #499
2025-12-18 14:06:56 +09:00
b6bfa8fd2e Merge pull request '오브젝트 저장: 계산기 추가' (#497) from dev_ysCha into dev
Reviewed-on: #497
2025-12-18 14:06:06 +09:00
03a629b9c8 오브젝트 저장: 계산기 추가 2025-12-18 13:40:08 +09:00
6776b2b49a Merge pull request 'dev' (#496) from dev into prd-deploy
Reviewed-on: #496
2025-12-18 11:29:54 +09:00
Jaeyoung Lee
252782f275 Merge branch 'feature/design-remake' into dev 2025-12-18 11:28:16 +09:00
Jaeyoung Lee
013fd963b3 변별로 4각에서 마루선 그려지지 않는 경우 처리 2025-12-18 11:27:56 +09:00
f345b0dbbe 처마/케라바 변경 탭 변경시 버튼 값 초기화 2025-12-18 10:12:34 +09:00
9e06bb04a1 Merge pull request 'dev' (#494) from dev into prd-deploy
Reviewed-on: #494
2025-12-18 09:35:18 +09:00
Jaeyoung Lee
86d595ef52 주석처리 안된곳 처리. 2025-12-17 17:50:18 +09:00
Jaeyoung Lee
ce5192ba1a Merge branch 'dev' into feature/design-remake 2025-12-17 17:34:56 +09:00
Jaeyoung Lee
3a8769bd91 변별로 설정 1차 운영서버 반영 2025-12-17 17:33:16 +09:00
5e93c4d0e8 qna_고객명노출 2025-12-17 16:07:51 +09:00
d1742b3837 Merge pull request 'dev' (#492) from dev into prd-deploy
Reviewed-on: #492
2025-12-17 15:52:59 +09:00
e7d83b727a 동->현이동 2025-12-17 15:51:31 +09:00
d096aae6db skt컨텐츠메뉴노출 2025-12-17 15:37:37 +09:00
eb71af3799 중복제거 2025-12-17 15:36:36 +09:00
d951cad178 Merge pull request 'dev' (#490) from dev into prd-deploy
Reviewed-on: #490
2025-12-16 18:36:59 +09:00
e9b6624cb7 Merge pull request 'dev_ysCha' (#488) from dev_ysCha into dev
Reviewed-on: #488
2025-12-16 18:35:56 +09:00
7bf2db7479 동이동현이동 : 계산기 추가 2025-12-16 17:58:46 +09:00
1ad47291f2 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev 2025-12-16 17:52:48 +09:00
8eeff43b4c 지붕재 할당 전 지붕 옮길 시 outerLinePoints 좌표도 수정 필요 2025-12-16 17:52:41 +09:00
6f47b9dc54 면형상배치 : 계산기 추가 2025-12-16 17:39:28 +09:00
b81fc389f5 보조선이동복사 : 계산기 추가 2025-12-16 17:20:28 +09:00
bbf2dfbe7c 보조선사이즈변경 : 계산기 추가 2025-12-16 17:14:47 +09:00
4f483dbfee 외벽편집오브셋 : 계산기 추가 2025-12-16 16:20:26 +09:00
19bdaa52fd 보조선로만들기 : 계산기 추가 2025-12-16 16:07:18 +09:00
f9f29223e4 개구 : 계산기 추가 2025-12-16 15:49:40 +09:00
0abfb52931 Merge pull request '[1346]문의게시판 : cnustNm 추가' (#487) from dev into prd-deploy
Reviewed-on: #487
2025-12-16 14:43:05 +09:00
b76f6eb057 [1346]문의게시판 : cnustNm 추가 2025-12-16 14:12:44 +09:00
b0b87bbd66 지붕면 이동 후 선택 제대로 안되는 현상 수정, 지붕면 할당 시 방향 화살표 전부 남쪽으로 되는 현상 수정 2025-12-16 13:34:52 +09:00
eb7555be49 Merge pull request 'dev' (#484) from dev into prd-deploy
Reviewed-on: #484
2025-12-16 11:21:52 +09:00
4a959ba8cf skeleton open 2025-12-16 11:19:54 +09:00
22f603d017 Merge remote-tracking branch 'origin/prd-deploy' into prd-deploy 2025-12-16 11:16:48 +09:00
cd97ada0df skeleton open 2025-12-16 11:14:58 +09:00
33fa60360b Merge pull request 'skeleton open' (#482) from dev_ysCha into dev
Reviewed-on: #482
2025-12-16 11:10:08 +09:00
fdeb0dab9a skeleton open 2025-12-16 11:09:05 +09:00
cc78381367 Merge pull request 'dev' (#481) from dev into prd-deploy
Reviewed-on: #481
2025-12-16 10:46:54 +09:00
01a34fb8bc Merge pull request '[1351]경사도 기본 수치 추가 inclBase' (#479) from dev_ysCha into dev
Reviewed-on: #479
2025-12-16 10:46:12 +09:00
b35ae4407a [1351]경사도 기본 수치 추가 inclBase 2025-12-16 10:44:43 +09:00
b2108af279 Merge pull request 'dev' (#478) from dev into prd-deploy
Reviewed-on: #478
2025-12-15 18:23:24 +09:00
cbd80d0602 Merge pull request 'skt수정2' (#476) from dev_ysCha into dev
Reviewed-on: #476
2025-12-15 18:22:50 +09:00
b4a7bb2e48 skt수정2 2025-12-15 18:21:40 +09:00
b854e03571 Merge pull request 'dev' (#475) from dev into prd-deploy
Reviewed-on: #475
2025-12-15 17:59:36 +09:00
11e5ade5bd Merge pull request 'skt수정' (#473) from dev_ysCha into dev
Reviewed-on: #473
2025-12-15 17:58:31 +09:00
4eb89068c5 skt수정 2025-12-15 17:58:00 +09:00
be4e628c09 Merge pull request 'dev' (#472) from dev into prd-deploy
Reviewed-on: #472
2025-12-15 16:44:44 +09:00
9a37959b60 Merge pull request '[1351]경사도 기본 수치 추가 inclBase' (#471) from dev_ysCha into dev
Reviewed-on: #471
2025-12-15 16:44:05 +09:00
ff5cd04b27 [1351]경사도 기본 수치 추가 inclBase 2025-12-15 16:42:31 +09:00
f57b07e5e1 Merge pull request 'dev' (#470) from dev into prd-deploy
Reviewed-on: #470
2025-12-15 14:26:52 +09:00
39bf5f5ccc Merge pull request 'followLine 수정' (#468) from dev_ysCha into dev
Reviewed-on: #468
2025-12-15 14:25:50 +09:00
9d9cf4a05d followLine 수정 2025-12-15 14:24:15 +09:00
71156e387e Merge pull request 'dev' (#467) from dev into prd-deploy
Reviewed-on: #467
2025-12-15 01:31:57 +09:00
25c7037e86 Merge pull request 'out 완료' (#465) from dev_cha into dev
Reviewed-on: #465
2025-12-15 01:31:08 +09:00
6c5a0a8a54 out 완료 2025-12-15 01:29:04 +09:00
8c7fd80743 할당완료 2025-12-13 17:19:18 +09:00
9fa8c1a534 Merge pull request 'dev' (#464) from dev into prd-deploy
Reviewed-on: #464
2025-12-13 17:14:18 +09:00
e9d0668ed2 group object 좌표 재계산 로직 수정 2025-12-13 17:09:50 +09:00
6633677890 도머 오프셋 선택 시 오류 수정 2025-12-13 17:09:50 +09:00
d519f9f080 플랜 이동 시 외벽선 남아있는 현상 수정
지붕재 변경 시 같은 지붕재를 가지고 있는 다른 지붕들의 지붕재도 변경 추가
2025-12-13 17:09:48 +09:00
fb8ce256ff 복사 후 문제 수정 2025-12-13 17:09:47 +09:00
f9b63bca42 Merge pull request '할당완료' (#462) from dev_cha into dev
Reviewed-on: #462
2025-12-13 17:05:49 +09:00
be6fdce63c 할당완료 2025-12-13 17:04:59 +09:00
53a1f4ed00 group object 좌표 재계산 로직 수정 2025-12-12 15:27:26 +09:00
97a444bccd Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev 2025-12-12 15:20:36 +09:00
a6e935bc15 도머 오프셋 선택 시 오류 수정 2025-12-12 15:20:28 +09:00
Jaeyoung Lee
f3cd10931d 변별로 처리 중간저장 2025-12-11 14:39:46 +09:00
f898b4434b Merge pull request 'sortRoofLines 개선' (#460) from dev_cha into dev
Reviewed-on: #460
2025-12-11 01:51:43 +09:00
fef352933f sortRoofLines 개선 2025-12-11 01:51:04 +09:00
ab78399e42 Merge pull request '시뮬레이션 수정' (#458) from dev_ysCha into dev
Reviewed-on: #458
2025-12-10 11:21:20 +09:00
2205809d0b 시뮬레이션 수정 2025-12-10 11:20:10 +09:00
c5d830f6c0 플랜 이동 시 외벽선 남아있는 현상 수정
지붕재 변경 시 같은 지붕재를 가지고 있는 다른 지붕들의 지붕재도 변경 추가
2025-12-10 10:24:37 +09:00
16870adc30 Merge pull request 'sortedBaseLines' (#456) from dev_cha into dev
Reviewed-on: #456
2025-12-10 00:41:57 +09:00
540812861d sortedBaseLines 2025-12-10 00:40:53 +09:00
5ca4f3d901 Merge pull request 'dev_ysCha' (#454) from dev_ysCha into dev
Reviewed-on: #454
2025-12-09 10:41:53 +09:00
2625dcdd08 절단변경 2025-12-09 10:40:51 +09:00
d0156bf838 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev_ysCha 2025-12-09 10:39:36 +09:00
559c085183 Merge pull request '동이동 추가' (#452) from dev_cha into dev
Reviewed-on: #452
2025-12-09 00:16:42 +09:00
a8c7d05278 동이동 추가 2025-12-09 00:10:40 +09:00
f5e315eb03 Merge pull request 'Merge pull request '좌표삭제, 라인사이즈 줄임' (#450) from dev_ysCha into dev' (#451) from dev into dev_ysCha
Reviewed-on: #451
2025-12-08 10:32:50 +09:00
4a0eb66a3c Merge pull request '좌표삭제, 라인사이즈 줄임' (#450) from dev_ysCha into dev
Reviewed-on: #450
2025-12-08 10:32:16 +09:00
5da154df5d 좌표삭제, 라인사이즈 줄임 2025-12-08 10:31:32 +09:00
d7fa196570 Merge pull request 'v0.3' (#448) from dev_cha into dev
Reviewed-on: #448
2025-12-07 21:35:24 +09:00
2220e2ea08 v0.3 2025-12-07 16:05:28 +09:00
23b8aa7fa7 Merge pull request 'dev_cha' (#447) from dev_cha into dev
Reviewed-on: #447
2025-12-07 14:51:30 +09:00
acde55f4d1 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev_cha 2025-12-07 14:49:14 +09:00
dbb6f0af81 동이동 및 fabric 버전업 2025-12-07 14:48:05 +09:00
8943ab2f38 v0.2 2025-12-07 13:54:45 +09:00
28d26511de 복사 후 문제 수정 2025-12-05 15:36:56 +09:00
d67147063f Merge pull request 'dev' (#446) from dev into prd-deploy
Reviewed-on: #446
2025-12-04 19:11:54 +09:00
eee74edf9e 외벽선 표시 추가 2025-12-04 17:13:34 +09:00
800453ff6b 혼합모듈 회로 대응 작업 추가 2025-12-04 14:28:30 +09:00
eb315cae64 style: 회로설정 팝업 총 회로수 select-box 추가 2025-12-04 10:51:40 +09:00
bf681dccba 저장 시 줌 관련 문제 수정 2025-12-02 11:05:03 +09:00
1fff48bbfa Merge pull request 'canvas 삭제' (#443) from dev_ysCha into dev
Reviewed-on: #443
2025-12-01 11:25:21 +09:00
3c92c2b8d7 canvas 삭제 2025-12-01 11:24:47 +09:00
8fa207a329 Merge pull request 'dev_ysCha' (#442) from dev_ysCha into dev
Reviewed-on: #442
2025-12-01 11:17:02 +09:00
ed9379081c Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev_ysCha
# Conflicts:
#	src/components/floor-plan/modal/object/SizeSetting.jsx
2025-12-01 11:15:22 +09:00
5a1dcc463c 게산기 수정 2025-12-01 11:07:39 +09:00
a9732eb571 Merge pull request 'dev' (#440) from dev into prd-deploy
Reviewed-on: #440
2025-12-01 09:06:36 +09:00
ee33558f2f 개구, 오브젝트 설치 후 길이 안나오는 현상 수정 2025-11-28 15:16:16 +09:00
6800fb035a Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev 2025-11-28 11:22:53 +09:00
3d1e3d2abd 회로설정 수동설정 창크기 조절 관련 수정 2025-11-28 11:22:46 +09:00
11bd1ac009 Merge pull request 'dev' (#438) from dev into prd-deploy
Reviewed-on: #438
2025-11-27 10:35:09 +09:00
d747aee284 Merge pull request 'dev_ysCha' (#436) from dev_ysCha into dev
Reviewed-on: #436
2025-11-27 10:34:28 +09:00
9c2619b99e 실측값입력 2025-11-27 10:33:47 +09:00
924ec70402 실측값입력 2025-11-27 10:28:50 +09:00
dcb9e06a09 Merge pull request 'dev' (#435) from dev into prd-deploy
Reviewed-on: #435
2025-11-26 18:12:53 +09:00
902c6c1eaf Merge pull request 'ref={pitchRef}' (#433) from dev_ysCha into dev
Reviewed-on: #433
2025-11-26 18:11:46 +09:00
5bb49097ef ref={pitchRef} 2025-11-26 18:10:48 +09:00
189dd82c50 모듈, 회로구성 팝업 줄이기 추가 2025-11-26 09:47:06 +09:00
e18842036d Merge pull request '[1308] 촌에 소수점 입력 - {pitchText}' (#432) from dev into prd-deploy
Reviewed-on: #432
2025-11-26 09:31:30 +09:00
c2114efe0e Merge pull request 'dev_ysCha' (#430) from dev_ysCha into dev
Reviewed-on: #430
2025-11-26 09:30:20 +09:00
34871397ee [1308] 촌에 소수점 입력 - {pitchText} 2025-11-26 09:29:17 +09:00
aeb457e5ec [1308] 촌에 소수점 입력 - 처마,케라바 수정 2025-11-26 09:18:01 +09:00
0de3584c70 Merge pull request '필요하지 않은 접힘 버튼 제거' (#429) from dev into prd-deploy
Reviewed-on: #429
2025-11-26 09:05:34 +09:00
7ddbc40cd4 필요하지 않은 접힘 버튼 제거 2025-11-25 13:41:35 +09:00
1f126d169b Merge pull request 'dev' (#427) from dev into prd-deploy
Reviewed-on: #427
2025-11-25 13:34:38 +09:00
97209b6742 Merge pull request 'sessionState?.storeId' (#425) from dev_ysCha into dev
Reviewed-on: #425
2025-11-25 13:33:28 +09:00
f5b68894ea sessionState?.storeId 2025-11-25 13:32:36 +09:00
3dfd8e0ceb Merge pull request 'prd' (#424) from dev into prd-deploy
Reviewed-on: #424
2025-11-25 09:51:04 +09:00
b2799dacb8 Merge pull request '[1308] 촌에 소수점 입력 - 처마,케라바 수정' (#422) from dev_ysCha into dev
Reviewed-on: #422
2025-11-25 09:49:57 +09:00
d9b68f3012 [1308] 촌에 소수점 입력 - 처마,케라바 수정 2025-11-25 09:49:04 +09:00
879ace01c2 Merge pull request 'dev' (#421) from dev into prd-deploy
Reviewed-on: #421
2025-11-24 14:28:06 +09:00
6d56701483 Merge pull request '[1308] 촌에 소수점 입력2' (#419) from dev_ysCha into dev
Reviewed-on: #419
2025-11-24 14:24:45 +09:00
0a97166b06 [1308] 촌에 소수점 입력2 2025-11-24 14:23:45 +09:00
9fa7d2afd4 Merge pull request 'dev' (#418) from dev into prd-deploy
Reviewed-on: #418
2025-11-24 14:10:33 +09:00
3d0df7bc58 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev_ysCha 2025-11-24 14:07:07 +09:00
3071c0ddc5 [1308] 촌에 소수점 입력 2025-11-24 14:06:01 +09:00
43f70f9f79 소수점이하 2자리 추가 2025-11-24 14:03:10 +09:00
ed28871a5b Merge branch 'qcast-pub' of https://git.hanasys.jp/qcast3/qcast-front into dev 2025-11-24 13:56:28 +09:00
Jaeyoung Lee
6c0cc73cb8 중간 저장(반박공지붕 추가) 2025-11-24 10:35:00 +09:00
Jaeyoung Lee
a201d65ebe 변별로 대응 중간 저장 2025-11-21 14:15:52 +09:00
82e4b2d305 Merge pull request 'dev' (#416) from dev into prd-deploy
Reviewed-on: #416
2025-11-18 13:23:45 +09:00
101e086a84 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev_ysCha 2025-11-18 13:21:10 +09:00
f63202877f [1314][HANASYS DESIGN]Simulation내용의 변경에 대해서 2025-11-18 13:18:38 +09:00
Jaeyoung Lee
d148077e6b 변별로 설정 중간 저장 2025-11-12 10:40:57 +09:00
e5551ea0e5 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into qcast-pub 2025-11-11 17:39:46 +09:00
9af16ac047 수동지붕설정 제대로 생성 안되는 현상 수정 2025-11-11 17:20:44 +09:00
2b8de3d0d0 style: 모달 접기 버튼 추가 2025-11-07 15:46:04 +09:00
cacefcc5d0 Merge remote-tracking branch 'origin/dev_ysCha' into dev_ysCha 2025-11-07 09:02:56 +09:00
81f9945b72 Merge pull request 'dev' (#414) from dev into prd-deploy
Reviewed-on: #414
2025-11-07 08:51:45 +09:00
88632a4619 sk제거 2025-11-07 08:45:51 +09:00
575386c526 Merge pull request '[1308] 배치면 초기 설정의 지붕 경사(경사도)에서 소수점 입력이 불가능합니다' (#412) from dev into dev_ysCha
Reviewed-on: #412
2025-11-07 08:37:15 +09:00
3c094b3c5a log 제거 2025-11-07 08:32:41 +09:00
97725c1a3d Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev_ysCha
# Conflicts:
#	src/util/skeleton-utils.js
2025-11-07 08:14:28 +09:00
7377b06e2a log 제거 2025-11-07 07:57:41 +09:00
Cha
ee79c0e680 알고리즘 변경 2025-11-06 23:06:35 +09:00
43ae563b19 Merge pull request 'dev' (#411) from dev into prd-deploy
Reviewed-on: #411
2025-11-03 09:16:59 +09:00
Cha
a3d1704390 baseLine수정 2025-11-02 00:51:49 +09:00
Cha
fecc4e999a baseLine수정 2025-11-02 00:48:53 +09:00
34a1a6d201 Merge pull request 'log 제거' (#408) from dev_ysCha into dev
Reviewed-on: #408
2025-10-31 18:09:13 +09:00
03c31c82ff log 제거 2025-10-31 18:08:23 +09:00
978d74f66a Merge pull request 'dev_ysCha' (#406) from dev_ysCha into dev
Reviewed-on: #406
2025-10-30 16:39:40 +09:00
990c0e7e29 log 제거 2025-10-30 16:38:48 +09:00
a428cc31e8 초기화 변경 2025-10-30 16:36:53 +09:00
baeb2be0a1 동,현이동 동일라인체크 변경, 상우선택시 초기화 문제 2025-10-30 16:35:42 +09:00
89b8af1533 Merge pull request '현의 상(out), 하(in) 의미 변경' (#404) from dev_ysCha into dev
Reviewed-on: #404
2025-10-29 16:03:55 +09:00
a75ea4c98a 현의 상(out), 하(in) 의미 변경 2025-10-29 16:02:28 +09:00
6a47067060 Merge pull request 'dev' (#403) from dev into prd-deploy
Reviewed-on: #403
2025-10-28 16:43:02 +09:00
4a716ad7ad Merge pull request 'Q.PARTNERS 링크 추가' (#401) from dev_ysCha into dev
Reviewed-on: #401
2025-10-28 16:40:46 +09:00
8cd67a92a8 Q.PARTNERS 링크 추가 2025-10-28 16:39:17 +09:00
befa12b00b 길이 위치 수정 2025-10-27 14:38:25 +09:00
d7e35dba40 Merge pull request 'target 의 좌표비교 변경 Math.abs(target.y1 - target.y2) < 0.2' (#400) from dev_ysCha into dev
Reviewed-on: #400
2025-10-27 13:19:34 +09:00
2080c8bf20 target 의 좌표비교 변경 Math.abs(target.y1 - target.y2) < 0.2 2025-10-27 13:17:56 +09:00
affef782f3 roof.moveFlowLine 수정 2025-10-24 18:36:38 +09:00
0ad18e4f15 roof.moveFlowLine 수정, log 제거 2025-10-24 13:04:56 +09:00
82698a5d03 동,현이동 동일라인체크 변경, 상우선택시 초기화 문제 2025-10-23 18:34:11 +09:00
ea179e3ab5 Merge pull request '동,현이동 동일라인체크 변경, 상우선택시 초기화 문제' (#399) from dev into prd-deploy
Reviewed-on: #399
2025-10-22 17:30:03 +09:00
7cc20f33c6 동,현이동 동일라인체크 변경, 상우선택시 초기화 문제 2025-10-22 17:28:16 +09:00
6dc12f3314 Merge pull request '[1258] api/v1/master/pcsMakerList 에 moduleMatlCds 파라미터 추가' (#397) from dev into prd-deploy
Reviewed-on: #397
2025-10-22 13:37:42 +09:00
7baa198501 [1258] api/v1/master/pcsMakerList 에 moduleMatlCds 파라미터 추가 2025-10-22 13:36:37 +09:00
061289133a Merge pull request '모듈시리즈 최초 전체 선택' (#395) from dev into prd-deploy
Reviewed-on: #395
2025-10-22 10:11:13 +09:00
396d8bc708 모듈시리즈 최초 전체 선택 2025-10-22 10:05:38 +09:00
9d49a06a8b Merge pull request 'dev' (#393) from dev into prd-deploy
Reviewed-on: #393
2025-10-21 10:53:13 +09:00
c60da7ddc9 [1305]Re.RISE-NBC AG270 모듈을 북면 설치했을 때 북면 설치용 첨부자료를 불필요하게 만들고 싶다. 2025-10-21 10:50:41 +09:00
a2cd08484d skeleton 2025-10-17 18:57:47 +09:00
25462b6d61 Merge pull request '보조라인 길이 나오지 않음 => obj.name !== 'lengthText' && 추가' (#391) from dev into prd-deploy
Reviewed-on: #391
2025-10-16 18:07:02 +09:00
3a3ff7c156 보조라인 길이 나오지 않음 => obj.name !== 'lengthText' && 추가 2025-10-16 18:04:09 +09:00
d9619599ea Merge pull request '360도 회전시 위치 수정' (#389) from dev into prd-deploy
Reviewed-on: #389
2025-10-16 08:31:07 +09:00
d548b0e1f4 360도 회전시 위치 수정 2025-10-15 16:15:21 +09:00
a64a016b51 Merge pull request '동, 현이동 확인항목 추가' (#387) from dev into prd-deploy
Reviewed-on: #387
2025-10-15 10:16:01 +09:00
ddf326ca6b 동, 현이동 확인항목 추가 2025-10-15 09:54:13 +09:00
1d8e09f1d2 Merge pull request 'dev' (#385) from dev into prd-deploy
Reviewed-on: #385
2025-10-15 09:14:04 +09:00
8b2cf6a9d3 시계방향 대응 추가 2025-10-14 14:16:02 +09:00
41de001986 순서에 따라 abs적용 추가 2025-10-14 13:58:15 +09:00
3cbb59d187 Merge pull request 'hipSize 간혹 오류발생 - 기본값 0 설정' (#383) from dev into prd-deploy
Reviewed-on: #383
2025-10-14 11:07:43 +09:00
1385683bce hipSize 간혹 오류발생 - 기본값 0 설정 2025-10-14 11:05:50 +09:00
30f062e759 Merge pull request '문의게시판 저장수정 글자변경' (#381) from dev into prd-deploy
Reviewed-on: #381
2025-10-14 11:00:43 +09:00
92fd17ed71 문의게시판 저장수정 글자변경 2025-10-14 10:59:10 +09:00
5b92c6c99f Merge pull request '문의사항' (#379) from dev into prd-deploy
Reviewed-on: #379
2025-10-01 11:11:26 +09:00
320080e0c1 문의게시판 상세 api 수정 2025-10-01 11:09:34 +09:00
d32553416c [1309] 문의사항 3 카테고리 문제 2025-10-01 10:14:15 +09:00
10bff1745e Merge pull request '견적서 및 발전량에 페이지에서 canvas가 null임..' (#377) from dev into prd-deploy
Reviewed-on: #377
2025-09-30 18:22:13 +09:00
1aa6bc79a8 견적서 및 발전량에 페이지에서 canvas가 null임.. 2025-09-30 18:19:16 +09:00
8c07acb577 Merge pull request '[1222]견적의 제품 정보 화면에 대해서 - 그룹별 추가' (#375) from dev into prd-deploy
Reviewed-on: #375
2025-09-30 18:14:35 +09:00
b480345b24 [1222]견적의 제품 정보 화면에 대해서 - 그룹별 추가 2025-09-30 18:12:25 +09:00
60588d2184 Merge pull request 'input 계산기추가(전각)' (#373) from dev into prd-deploy
Reviewed-on: #373
2025-09-30 14:26:46 +09:00
5f726bf5db input 계산기추가(전각) 2025-09-30 14:25:31 +09:00
211a81362b Merge pull request '[big.js] Division by zero - actualSize 가 0이 되어 나누기에 문제가 됨' (#371) from dev into prd-deploy
Reviewed-on: #371
2025-09-30 14:24:49 +09:00
9a2c6adb96 [big.js] Division by zero - actualSize 가 0이 되어 나누기에 문제가 됨 2025-09-30 14:22:59 +09:00
c61669d9f2 Merge pull request '스켈레톤.v0.1' (#369) from dev into prd-deploy
Reviewed-on: #369
2025-09-30 13:43:21 +09:00
75312f5ccf 스켈레톤.v0.1 2025-09-30 13:41:34 +09:00
b0fa0888de Merge pull request 'dev' (#367) from dev into prd-deploy
Reviewed-on: #367
2025-09-30 13:40:20 +09:00
ba94dd0579 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev_ysCha 2025-09-30 13:29:38 +09:00
384c68c1ef [big.js] Division by zero - actualSize 가 0이 되어 나누기에 문제가 됨 2025-09-30 13:29:09 +09:00
dfed51a758 각도 계산식 소수점 둘째자리까지 표시하도록 수정 2025-09-30 09:55:01 +09:00
94a60bf196 Merge pull request 'dev' (#365) from dev into prd-deploy
Reviewed-on: #365
2025-09-29 11:40:27 +09:00
01f160fa8b Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev_ysCha 2025-09-29 11:38:33 +09:00
c58c1f2106 [1195] DESIGN 발전 시뮬레이션 색상 변경 2025-09-29 11:37:28 +09:00
Jaeyoung Lee
bf0e1e4cb0 코드 정리 2025-09-29 10:26:22 +09:00
Jaeyoung Lee
340c7669af Merge branch 'dev' into feature/design-remake 2025-09-29 10:22:08 +09:00
Jaeyoung Lee
8cca1e9937 BackwardLines 수정 2025-09-29 10:21:45 +09:00
8bcc4ac8d1 Merge pull request 'dev' (#363) from dev into prd-deploy
Reviewed-on: #363
2025-09-26 18:08:52 +09:00
e8adf1659f Merge pull request '[1306] 가대 설정의 불량' (#361) from dev_ysCha into dev
Reviewed-on: #361
2025-09-26 18:07:43 +09:00
9786c1fbf7 [1306] 가대 설정의 불량 2025-09-26 18:01:08 +09:00
6f689a5321 Merge pull request '[1237] 모듈 자동 선택 문제' (#360) from dev into prd-deploy
Reviewed-on: #360
2025-09-26 10:58:29 +09:00
4469691618 [1237] 모듈 자동 선택 문제 2025-09-26 10:57:03 +09:00
a6d9e0c41b Merge pull request 'trestleState.constTp === data.constTp),' (#358) from dev into prd-deploy
Reviewed-on: #358
2025-09-25 10:56:16 +09:00
9c0403c947 trestleState.constTp === data.constTp), 2025-09-25 10:54:50 +09:00
a1a7dcb1bf Merge pull request 'next trestleState.constTp' (#356) from dev into prd-deploy
Reviewed-on: #356
2025-09-25 10:53:22 +09:00
4df39defc6 next trestleState.constTp 2025-09-25 10:50:02 +09:00
a13995dec1 Merge pull request '[1237] 모듈/가대 목록 - timeout 연장' (#354) from dev into prd-deploy
Reviewed-on: #354
2025-09-24 13:04:16 +09:00
0efc90f135 [1237] 모듈/가대 목록 - timeout 연장 2025-09-24 13:03:18 +09:00
Jaeyoung Lee
bbd8a43864 innerLines 초기화 추가 2025-09-24 10:48:10 +09:00
Jaeyoung Lee
46710533b5 동선이동 수정 2025-09-24 10:46:36 +09:00
1f08fc73f7 Merge pull request 'dev' (#352) from dev into prd-deploy
Reviewed-on: #352
2025-09-24 10:11:15 +09:00
eeab13b9cd 초기ALL문제 해결 2025-09-24 10:02:52 +09:00
0acd9e422f skeleton 변경 2025-09-23 18:40:14 +09:00
8dc8318048 Merge pull request 'dev' (#350) from dev into prd-deploy
Reviewed-on: #350
2025-09-22 18:29:09 +09:00
Jaeyoung Lee
c58146ca53 소수점 문제로 인한 지붕선 찾기 오류 처리 2025-09-22 10:40:35 +09:00
960d31ffdd Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev 2025-09-22 09:59:53 +09:00
93e54812c3 배치면 선택 시 기존 roof stroke, strokeWidth 수정 2025-09-22 09:59:35 +09:00
816e440ba0 skeleton 변경 2025-09-19 18:02:24 +09:00
8cf2acb7a7 Merge pull request '지붕 회전 시 내부 오브젝트(개구, 도머, 그림자) 회전 반영' (#348) from dev into prd-deploy
Reviewed-on: #348
2025-09-19 13:03:59 +09:00
a8d9988f24 지붕 회전 시 내부 오브젝트(개구, 도머, 그림자) 회전 반영 2025-09-19 10:19:49 +09:00
6d508fe314 Merge pull request 'dev' (#346) from dev into prd-deploy
Reviewed-on: #346
2025-09-17 10:32:08 +09:00
21943536c9 skeleton 20% 2025-09-16 18:19:13 +09:00
Cha
99c7759e00 skeleton-utils 2025-09-16 00:14:03 +09:00
d85dcd37ee Merge pull request 'dev' (#344) from dev into prd-deploy
Reviewed-on: #344
2025-09-15 21:26:49 +09:00
7d9b6d5225 object 회전 기능 추가 2025-09-15 14:55:04 +09:00
Cha
36d16069e0 skeleton-utils 2025-09-14 01:09:03 +09:00
f912a8474e fetching error: TypeError: Cannot read properties of undefined (reading 'planNo')
at eval (useRoofAllocationSetting.js:179:26)
    at async fetchBasicSettings (useRoofAllocationSetting.js:112:7)
2025-09-12 16:45:53 +09:00
1393422a4a Merge pull request '[1216] 모듈에 시리즈 추가 - 시리즈가 존재하지 않는 모듈존재' (#340) from dev into prd-deploy
Reviewed-on: #340
2025-09-12 10:45:15 +09:00
d37b191139 [1216] 모듈에 시리즈 추가 - 시리즈가 존재하지 않는 모듈존재 2025-09-12 10:43:13 +09:00
e4c7ab2913 Merge pull request 'dev' (#338) from dev into prd-deploy
Reviewed-on: #338
2025-09-11 09:57:01 +09:00
7c9fd0f698 Merge remote-tracking branch 'origin/dev' into feature/ysCha 2025-09-11 09:52:42 +09:00
650e72d91d Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev 2025-09-11 09:50:45 +09:00
fb20a1676b 계산기의 마지막 입력숫자가 보이게.. 2025-09-11 09:50:42 +09:00
b651aab959 module 이동 후에도 흡착점 작동하도록 수정 2025-09-11 09:50:25 +09:00
e28a37e388 Merge pull request 'dev' (#336) from dev into prd-deploy
Reviewed-on: #336
2025-09-10 18:27:50 +09:00
cf969d6f1c Merge remote-tracking branch 'origin/dev' into feature/ysCha 2025-09-10 18:22:46 +09:00
0666612c0a [1216] 모듈에 시리즈 추가 2025-09-10 18:21:30 +09:00
3811f224d6 plan 저장 전 선택한 setupsurface 선택된 경우 초기화 2025-09-10 17:51:07 +09:00
Jaeyoung Lee
fda35b6927 Merge branch 'dev' into feature/design-remake 2025-09-10 14:34:42 +09:00
a183453390 A,B타입 지붕 대응 2025-09-10 14:33:53 +09:00
Jaeyoung Lee
760becfb0d innerLines 오류 수정 2025-09-10 13:45:01 +09:00
Jaeyoung Lee
098fb8efc6 Merge branch 'dev' into feature/design-remake
# Conflicts:
#	src/components/floor-plan/CanvasFrame.jsx
2025-09-10 13:19:06 +09:00
Jaeyoung Lee
473f4f8d74 케라바 지붕 자동 설정 업데이트 2025-09-10 13:16:23 +09:00
eef21b9023 Merge pull request 'dev' (#334) from dev into prd-deploy
Reviewed-on: #334
2025-09-09 17:16:43 +09:00
c358cba0c5 흡착점 points => getCurrentPoints() 2025-09-09 15:41:10 +09:00
ff5c335c9b 계산기능 추가 2025-09-05 15:34:55 +09:00
69fdaf0ae4 Merge pull request 'dev' (#331) from dev into prd-deploy
Reviewed-on: #331
2025-09-04 17:09:37 +09:00
9999d1bae0 svg 이미지 해상도 경고 2025-09-04 17:07:45 +09:00
c5700c62cc FlowLine contains an input of type text with both value and defaultValue props. Input elements must be either controlled or uncontrolled 2025-09-04 17:06:53 +09:00
46c083b982 Merge pull request '원상복구 prd-deploy checkout' (#328) from dev into prd-deploy
Reviewed-on: #328
2025-09-03 19:20:34 +09:00
08ae9ac7b9 원상복구 prd-deploy checkout 2025-09-03 19:20:03 +09:00
45a353276d Merge pull request 'prd-deploy checkout' (#327) from dev into prd-deploy
Reviewed-on: #327
2025-09-03 19:13:18 +09:00
c4baa119b1 prd-deploy checkout 2025-09-03 19:12:47 +09:00
2c3314715b Merge pull request 'dev' (#326) from dev into prd-deploy
Reviewed-on: #326
2025-09-03 14:52:32 +09:00
acffaa5ebd input 계산기 디자인 수정 2025-09-03 14:49:36 +09:00
87d058fd07 skeleton.ts 라이브러리 2025-09-03 10:18:13 +09:00
61a44704fd Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into feature/batch-angle 2025-09-03 09:48:17 +09:00
ee26f28446 skeleton.ts 라이브러리 2025-09-03 09:26:13 +09:00
71a7fab4ea skeleton.ts 라이브러리 2025-09-03 08:58:34 +09:00
114bf94d92 skeleton.ts 라이브러리 2025-09-03 08:23:03 +09:00
abf25c9d4e Merge pull request 'dev' (#320) from dev into prd-deploy
Reviewed-on: #320
2025-09-02 18:39:34 +09:00
043c369c0e value, defaultValue 동시 존재 안됨 => defaultValue 삭제 2025-09-02 17:31:51 +09:00
1b678ecaa0 <thead><th></th></thead> => <tr></tr>추가 2025-09-02 17:31:51 +09:00
8a61f90971 big.mjs:139 Uncaught Error: [big.js] Invalid number
at klass.setLength (QLine.js:72:36) (NaN => 0 처리)
2025-09-02 17:31:51 +09:00
6dd66ee27d value, defaultValue 동시 존재 안됨 => defaultValue 삭제 2025-09-02 17:28:37 +09:00
662b38aac7 <thead><th></th></thead> => <tr></tr>추가 2025-09-02 17:26:18 +09:00
8f2a78ef1e big.mjs:139 Uncaught Error: [big.js] Invalid number
at klass.setLength (QLine.js:72:36) (NaN => 0 처리)
2025-09-02 17:25:00 +09:00
68680e0042 동일 경사, 동일 방면 체크되어있을 경우에만 정렬 2025-09-02 15:29:28 +09:00
872c27734b 동일 경사, 동일 방면 체크되어있을 경우에만 정렬 2025-09-02 14:30:12 +09:00
098147bc6b module 설치 후에도 setTimeout 적용 2025-09-01 14:40:12 +09:00
cee1567438 배치면 기초작업중 2025-09-01 13:52:41 +09:00
f15ff10bf6 input 계산기 수정 2025-08-29 17:10:05 +09:00
38a1907842 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into feature/ysCha 2025-08-29 15:49:20 +09:00
0c8374b43d input 계산기 추가 및 전각=>반각 2025-08-29 15:48:44 +09:00
bcd73e4f96 Merge pull request 'dev' (#315) from dev into prd-deploy
Reviewed-on: #315
2025-08-29 08:40:08 +09:00
bad50e11b1 wall이 있는 roof는 사진에서 제외 2025-08-28 15:27:24 +09:00
3d67e5eec8 wall이 있는 roof인 경우 roofMaterial 세팅 제외 2025-08-28 15:19:59 +09:00
Jaeyoung Lee
20eccab581 Merge branch 'dev' into feature/design-remake 2025-08-27 10:04:42 +09:00
Jaeyoung Lee
e35cacf520 a,b 패턴 지붕 수정
용마루 생성 끝, 지붕면 생성 중
2025-08-27 10:02:53 +09:00
7846d729c8 Merge pull request 'dev' (#313) from dev into prd-deploy
Reviewed-on: #313
2025-08-26 18:23:21 +09:00
a55fca439c Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev 2025-08-26 16:12:39 +09:00
9a536970c4 배치면에서는 대각선 허용 2025-08-26 16:12:03 +09:00
cc9aa1c347 Merge pull request 'dev' (#311) from dev into prd-deploy
Reviewed-on: #311
2025-08-26 15:29:03 +09:00
9f1649dc0e Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into feature/ysCha 2025-08-26 15:27:34 +09:00
06815fc04a [1237]모듈설정, 공법설정시에 미리 규정의 내용이 입력, 기본지붕설정 <=> 개뱔지붕설정[0] 2025-08-26 15:27:09 +09:00
27164dc3a7 QLine길이 계산 수정 및 흐름방향 화살표 위치 수정 2025-08-26 14:02:04 +09:00
6748f2944d Merge pull request '[1237]모듈설정, 공법설정시에 미리 규정의 내용이 입력, 기본지붕설정 <=> 개뱔지붕설정[0]' (#309) from dev into prd-deploy
Reviewed-on: #309
2025-08-25 17:40:51 +09:00
21634a7a8a [1237]모듈설정, 공법설정시에 미리 규정의 내용이 입력, 기본지붕설정 <=> 개뱔지붕설정[0] 2025-08-25 17:39:06 +09:00
1a7ce2cc09 Merge pull request '[1256] : 【HANASYS DESIGN】ラックレス金具の中間部と端部の色分けをしたい' (#307) from dev into prd-deploy
Reviewed-on: #307
2025-08-25 16:57:14 +09:00
77510148e3 [1256] : 【HANASYS DESIGN】ラックレス金具の中間部と端部の色分けをしたい
지지금구 색 변경
2025-08-25 14:42:07 +09:00
3ab6a1ffe1 Merge pull request '[1252] 일본의 전각 숫자, 반각 함수변경' (#305) from dev into prd-deploy
Reviewed-on: #305
2025-08-22 18:37:31 +09:00
4375666084 [1252] 일본의 전각 숫자, 반각 함수변경 2025-08-22 18:28:48 +09:00
bbbc8cffaa Merge pull request '[1252] 일본의 전각 숫자, 반각 함수변경' (#303) from dev into prd-deploy
Reviewed-on: #303
2025-08-22 18:03:20 +09:00
736328ef0f [1252] 일본의 전각 숫자, 반각 함수변경 2025-08-22 11:18:49 +09:00
5e4b22cdff Merge pull request '[1252] 일본의 전각 숫자, 반각 함수변경' (#301) from dev into prd-deploy
Reviewed-on: #301
2025-08-22 10:44:05 +09:00
848f75bafa [1252] 일본의 전각 숫자, 반각 함수변경 2025-08-22 10:43:16 +09:00
112212170b Merge pull request '[1252] 일본의 전각 숫자, 반각 함수변경' (#299) from dev into prd-deploy
Reviewed-on: #299
2025-08-22 10:05:52 +09:00
4b8c0510ca [1252] 일본의 전각 숫자, 반각 함수변경 2025-08-22 10:05:07 +09:00
441800fc1c Merge pull request '[1252] 일본의 전각 숫자, 반각 숫자 입력' (#297) from dev into prd-deploy
Reviewed-on: #297
2025-08-21 18:46:52 +09:00
301e6c136c [1252] 일본의 전각 숫자, 반각 숫자 입력 2025-08-21 18:45:02 +09:00
78ee3c41ec Merge pull request '[1249] 주소의 상세 주소란의 필수 입력을 중지' (#295) from dev into prd-deploy
Reviewed-on: #295
2025-08-21 09:27:45 +09:00
6c89e6ffa6 [1249] 주소의 상세 주소란의 필수 입력을 중지 2025-08-21 09:26:51 +09:00
475e981a4c Merge pull request '[1267] 500에러 - str => Number 변환 (Number로 한번더)' (#293) from dev into prd-deploy
Reviewed-on: #293
2025-08-20 19:03:17 +09:00
cef764fd0e [1267] 500에러 - str => Number 변환 (Number로 한번더) 2025-08-20 19:01:44 +09:00
fcf1f971ce Merge pull request '[1267] 500에러 - str => Number 변환 (이전으로 복구)' (#291) from dev into prd-deploy
Reviewed-on: #291
2025-08-20 18:31:22 +09:00
a663faf359 [1267] 500에러 - str => Number 변환 (이전으로 복구) 2025-08-20 18:28:47 +09:00
ee54f532cb Merge pull request '[1267] 500에러 - str => Number 변환' (#289) from dev into prd-deploy
Reviewed-on: #289
2025-08-20 17:01:43 +09:00
5cf5ef55af [1267] 500에러 - str => Number 변환 2025-08-20 17:00:17 +09:00
5ec3a1441f Merge pull request 'pcsMaker는 항상 새로 불러오도록 수정' (#287) from dev into prd-deploy
Reviewed-on: #287
2025-08-20 16:17:58 +09:00
d98eba97a7 pcsMaker는 항상 새로 불러오도록 수정 2025-08-20 14:02:09 +09:00
6645568502 Merge pull request 'dev' (#285) from dev into prd-deploy
Reviewed-on: #285
2025-08-20 08:52:41 +09:00
4a914e0aea roof의 중앙 지점에서 모듈 스크린샷 2025-08-19 18:26:11 +09:00
102cc4d672 줌 상태에 따라 mouseLine 수정 2025-08-19 15:32:33 +09:00
2aca4d22ec 줌 상태에 따라 그리드 복사, 이동 시 잘리는 현상 수정 2025-08-19 10:33:18 +09:00
0f2719a2f0 줌 상태 변경에 따라 그리드 상태 수정, 흡착점 모드 시작 시 ON 2025-08-19 10:17:27 +09:00
Jaeyoung Lee
8c153430cd 용마루, 박공 지붕 추가. 2025-08-13 13:23:29 +09:00
Jaeyoung Lee
7175cd0485 지붕 모양 파악 로직 수정 2025-08-13 13:01:43 +09:00
Jaeyoung Lee
c10a46e86f 불필요 라인 제거 2025-08-13 11:29:54 +09:00
Jaeyoung Lee
d8389c1d9f 지붕 덮개 작도 로직 수정 시작. 2025-08-13 11:27:46 +09:00
ebb45740a8 Merge pull request 'dev' (#283) from dev into prd-deploy
Reviewed-on: #283
2025-08-08 16:40:29 +09:00
bdef681898 type 유지 2025-08-08 15:56:17 +09:00
9db8b949ff Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev 2025-08-08 15:05:24 +09:00
6819196d20 보조선 확정시 자르기 제거 2025-08-08 15:05:17 +09:00
59db4d471f Merge pull request 'dev' (#281) from dev into prd-deploy
Reviewed-on: #281
2025-08-08 14:52:34 +09:00
d03937cc92 [1237] 모듈설정, 공법설정시에 미리 규정의 내용이 입력되어있는것과 같은 설정 - 선택하세요 삭제 2025-08-08 14:51:07 +09:00
cb42ad82de innerLine size 보존 2025-08-08 10:57:28 +09:00
7a0db7abfb capture 처리 추가 2025-08-07 18:21:54 +09:00
023bc91c29 Merge pull request '숫자입력 ''=> 0 으로 처리' (#279) from dev into prd-deploy
Reviewed-on: #279
2025-08-05 13:55:23 +09:00
de39eac555 숫자입력 ''=> 0 으로 처리 2025-08-05 13:53:58 +09:00
9b18a8a655 Merge pull request 'dev' (#277) from dev into prd-deploy
Reviewed-on: #277
2025-08-05 11:31:22 +09:00
bab3039d6e 숫자입력 ''=> 0 으로 처리 2025-08-05 11:29:59 +09:00
e285210927 지붕재선택 메뉴의 데이터 동기화 2025-08-05 11:29:06 +09:00
c0fa177f1c Merge pull request 'dev' (#275) from dev into prd-deploy
Reviewed-on: #275
2025-08-04 17:28:22 +09:00
dcd7ace830 [1218] WINDOW를 이동할 때 잡는 영역 - QSelect 제외 2025-08-04 17:27:13 +09:00
959964c876 Merge remote-tracking branch 'origin/dev' into feature/ysCha 2025-08-04 17:09:27 +09:00
f11a7d68e7 [1218] WINDOW를 이동할 때 잡는 영역 - 전체에서 input, button, select, textarea, [contenteditable] 제외 2025-08-04 17:08:47 +09:00
359c7c458f 지붕면 할당 로직 수정 2025-08-04 16:38:24 +09:00
58d35a2881 [1218] WINDOW를 이동할 때 잡는 영역 - 전체로 변경 2025-08-04 15:53:34 +09:00
8d57bf84bd Merge pull request '[1217]줌 50->10%' (#271) from dev into prd-deploy
Reviewed-on: #271
2025-08-04 13:58:10 +09:00
1b57602cb6 [1217]줌 50->10% 2025-08-04 13:56:15 +09:00
668f76c8ed Merge pull request '[1226]브라우저를 다시 로드하면 지붕재가 리셋' (#269) from dev into prd-deploy
Reviewed-on: #269
2025-08-04 13:11:28 +09:00
fa9663f123 [1226]브라우저를 다시 로드하면 지붕재가 리셋 2025-08-04 11:37:38 +09:00
fd203aeb10 Merge pull request 'dev' (#267) from dev into prd-deploy
Reviewed-on: #267
2025-08-01 17:41:54 +09:00
856086a271 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev 2025-08-01 16:44:16 +09:00
591b24974c 방향 설정 수정 2025-08-01 16:44:10 +09:00
c58f230607 Merge pull request '[1226]브라우저를 다시 로드하면 지붕재가 리셋' (#265) from dev into prd-deploy
Reviewed-on: #265
2025-08-01 13:30:25 +09:00
0a258c3807 [1226]브라우저를 다시 로드하면 지붕재가 리셋 2025-08-01 13:28:51 +09:00
2a3f3bb568 Merge pull request 'globalPitch 추가' (#263) from dev into prd-deploy
Reviewed-on: #263
2025-07-31 17:43:24 +09:00
cc7f1ad6af globalPitch 추가 2025-07-31 16:04:56 +09:00
d49fdc8948 Merge pull request 'dev' (#261) from dev into prd-deploy
Reviewed-on: #261
2025-07-31 16:02:34 +09:00
984836d523 zoom 0.1로 수정 2025-07-31 14:32:12 +09:00
d15e4ebd53 roof.pitch 확인 제거 2025-07-31 13:23:35 +09:00
6e65d5fa6c Merge pull request 'dev' (#259) from dev into prd-deploy
Reviewed-on: #259
2025-07-31 10:23:15 +09:00
95dcc69ab4 Merge remote-tracking branch 'origin/dev' into feature/ysCha 2025-07-31 10:07:42 +09:00
b3254d1e5d [1219] 견적서 엑셀에 방위각도 추가 2025-07-31 10:06:34 +09:00
992d87412c 보조선 겹치는 부분 자르기 추가 2025-07-31 09:52:35 +09:00
6a5ba73274 degree 계산식 수정 2025-07-30 18:01:45 +09:00
51e693ac91 Merge pull request 'dev' (#257) from dev into prd-deploy
Reviewed-on: #257
2025-07-30 17:47:41 +09:00
31751c26b9 모든 점이 같은 직선상일 경우 제거 2025-07-30 16:37:42 +09:00
f82a355f01 메뉴변경시 팝업 닫기 2025-07-30 16:36:31 +09:00
e5e3efef49 Merge pull request 'dev' (#255) from dev into prd-deploy
Reviewed-on: #255
2025-07-30 15:05:45 +09:00
Jaeyoung Lee
4e14e1d5bf Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev 2025-07-30 10:36:33 +09:00
Jaeyoung Lee
5e017ccbb7 동선이동, 형이동 시 좌표 계산 문제 수정 2025-07-30 10:36:25 +09:00
f95c414b31 Merge pull request 'dev' (#253) from dev into prd-deploy
Reviewed-on: #253
2025-07-30 10:18:05 +09:00
445270bb80 외벽선 반올림 제거 2025-07-30 09:52:39 +09:00
9ec5249122 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev 2025-07-29 17:09:40 +09:00
7e3820e6b1 hipandgable 추가 2025-07-29 17:09:33 +09:00
ae3087de14 Merge pull request 'dev' (#251) from dev into prd-deploy
Reviewed-on: #251
2025-07-29 16:51:23 +09:00
1e678cfd81 Merge remote-tracking branch 'origin/dev' into feature/ysCha 2025-07-29 16:49:33 +09:00
2b956bbc3b [1218] WINDOW를 이동할 때 잡는 영역 2025-07-29 16:49:06 +09:00
37e50c14cc 기준 좌표 수정 2025-07-29 16:44:27 +09:00
5f034c1652 Merge pull request 'dev' (#249) from dev into prd-deploy
Reviewed-on: #249
2025-07-29 16:33:19 +09:00
718fbc16fa Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev 2025-07-29 14:55:30 +09:00
123657431d 좌우로 넓어진 형상은 스케일 조정 x 2025-07-29 14:55:23 +09:00
e8dab08423 Merge pull request '[1191] T01에 한정 엑셀에 영업점 주소, 전화번호, fax' (#247) from dev into prd-deploy
Reviewed-on: #247
2025-07-29 10:06:04 +09:00
ce1fab884d [1191] T01에 한정 엑셀에 영업점 주소, 전화번호, fax 2025-07-29 10:04:42 +09:00
d92168316f Merge pull request '운영 소스 반영 확인을 위한 commit' (#245) from dev into prd-deploy
Reviewed-on: #245
2025-07-28 14:54:56 +09:00
Jaeyoung Lee
699bfa9af4 운영 소스 반영 확인을 위한 commit 2025-07-28 14:27:05 +09:00
0dc7b62fae Merge pull request '[1205] DXF => PNG 추가, 메시지 일본어 추가' (#243) from dev into prd-deploy
Reviewed-on: #243
2025-07-28 14:27:04 +09:00
5469037de9 [1205] DXF => PNG 추가, 메시지 일본어 추가 2025-07-28 14:06:59 +09:00
81b8e4ff3a Merge pull request '보조선 이동, 복사 시 innerLines에 추가' (#241) from dev into prd-deploy
Reviewed-on: #241
2025-07-25 17:23:32 +09:00
81fd0168e7 보조선 이동, 복사 시 innerLines에 추가 2025-07-25 16:22:58 +09:00
cfe3f416c7 Merge pull request '개구, 도머 설치시 sizesetting popup open' (#239) from dev into prd-deploy
Reviewed-on: #239
2025-07-25 14:26:06 +09:00
d1da73849f 개구, 도머 설치시 sizesetting popup open 2025-07-25 14:17:36 +09:00
0b246cb9a5 Merge pull request '[]' (#237) from dev into prd-deploy
Reviewed-on: #237
2025-07-24 15:57:06 +09:00
40970c5580 [] 2025-07-24 15:54:02 +09:00
764be3f594 Merge pull request '보조선 처리 추가' (#235) from dev into prd-deploy
Reviewed-on: #235
2025-07-24 15:25:40 +09:00
cdda10c30c 보조선 처리 추가 2025-07-24 13:58:13 +09:00
c5e2a56e08 Merge pull request 'dev' (#233) from dev into prd-deploy
Reviewed-on: #233
2025-07-24 13:30:49 +09:00
59d3bd61e2 지붕면 할당 알고리즘 수정 2025-07-24 13:19:09 +09:00
4684b45883 보조선 innerLines에 포함 안되는 현상 수정 2025-07-24 13:14:30 +09:00
e14e43f778 round처리 수정 2025-07-24 12:46:47 +09:00
5e86c98afd 지붕면 할당 로직 수정 2025-07-24 11:30:54 +09:00
f969cdba6a Merge pull request 'dev' (#231) from dev into prd-deploy
Reviewed-on: #231
2025-07-23 17:05:10 +09:00
Jaeyoung Lee
45a4ac84de Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev 2025-07-23 14:59:33 +09:00
Jaeyoung Lee
a64eaae57d 케라바 지붕 8각이상 동선, 형이동 시 문제점 수정 2025-07-23 14:59:25 +09:00
fdbedb23a4 Merge pull request 'dev' (#229) from dev into prd-deploy
Reviewed-on: #229
2025-07-22 15:12:41 +09:00
ca0620bd23 내부 확인 로직 수정 2025-07-22 14:03:48 +09:00
d52fdb23a1 오브젝트 배치 오류 수정 2025-07-22 13:53:06 +09:00
9655d2dc59 Merge pull request 'dev' (#227) from dev into prd-deploy
Reviewed-on: #227
2025-07-22 13:23:17 +09:00
b979e6cfa4 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev 2025-07-22 09:53:50 +09:00
eb03054804 주석 수정 2025-07-22 09:53:43 +09:00
aae7b21d08 Merge pull request 'dev' (#225) from dev into prd-deploy
Reviewed-on: #225
2025-07-22 09:09:37 +09:00
ef607afcf3 [1196] 자동회로구성시 회로구성후 남는 모듈에대해 얼럿 2025-07-22 09:07:16 +09:00
2d8a7034d8 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into feature/ysCha 2025-07-22 08:59:19 +09:00
1a52f1b9e6 Merge pull request 'dev' (#223) from dev into prd-deploy
Reviewed-on: #223
2025-07-21 15:37:13 +09:00
fb9ed9949d [1196] 일시적으로 2차점 숨김처리 2025-07-21 15:36:23 +09:00
4875985685 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev 2025-07-21 15:05:15 +09:00
a319df0120 캡쳐 로직 수정 2025-07-21 15:05:08 +09:00
Jaeyoung Lee
8e3a3d7c06 동선이동 관련 오류 수정 2025-07-21 14:24:25 +09:00
0bea975130 지붕면 할당 시 문제 수정 2025-07-21 11:38:17 +09:00
8fa230e91a Merge pull request '보조선 작성 완료 시 선택 안되는 현상 수정' (#221) from dev into prd-deploy
Reviewed-on: #221
2025-07-21 10:02:20 +09:00
553a9340f4 보조선 작성 완료 시 선택 안되는 현상 수정 2025-07-21 09:42:31 +09:00
07f8fb24e4 Merge pull request 'dev' (#219) from dev into prd-deploy
Reviewed-on: #219
2025-07-21 09:13:25 +09:00
eb255d5148 arrow에 따라 축소지점 수정 2025-07-18 18:26:35 +09:00
031643c092 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev 2025-07-18 18:24:28 +09:00
edb0783e26 지붕면 할당 수정 2025-07-18 18:24:22 +09:00
c35faea196 Merge pull request 'dev' (#217) from dev into prd-deploy
Reviewed-on: #217
2025-07-18 15:09:21 +09:00
Jaeyoung Lee
2f6a4a3ee8 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev 2025-07-18 14:05:01 +09:00
Jaeyoung Lee
7a019730f5 동선이동 로직 변경 2025-07-18 14:04:57 +09:00
d11a626321 Merge pull request '지붕면 할당 로직 수정' (#215) from dev into prd-deploy
Reviewed-on: #215
2025-07-17 17:02:40 +09:00
911ec78055 지붕면 할당 로직 수정 2025-07-17 16:11:42 +09:00
7ca01985b6 Merge pull request 'dev' (#212) from dev into prd-deploy
Reviewed-on: #212
2025-07-16 13:57:57 +09:00
e60989d318 modulePoints 추가 2025-07-16 13:11:30 +09:00
Jaeyoung Lee
44508faf04 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev 2025-07-15 15:01:07 +09:00
Jaeyoung Lee
59539e4a60 [1186, 1178] 동이동, 형이동 대응 2025-07-15 15:01:03 +09:00
e9f3cb8e53 Merge pull request 'dev' (#210) from dev into prd-deploy
Reviewed-on: #210
2025-07-15 14:45:26 +09:00
ea34d469bb wall 길이 표시 제거 2025-07-15 11:22:13 +09:00
10d7a6476a 보조선 관련된 흡착점 추가 2025-07-15 10:26:52 +09:00
8226f6cf3d 보조선 흡착 관련 작업 추가 2025-07-15 09:53:53 +09:00
c58ff87a4c 흡착점 추가 2025-07-14 15:32:22 +09:00
af850fc742 Merge pull request '캡쳐 전 제거항목 추가' (#208) from dev into prd-deploy
Reviewed-on: #208
2025-07-14 13:02:14 +09:00
dc6b268b74 캡쳐 전 제거항목 추가 2025-07-14 10:35:38 +09:00
b432c8792c Merge pull request '[1182] 추가 견적 출력 메시지 표시 변경 및 POPUP 메시지 추가 - 사이즈 변경' (#206) from dev into prd-deploy
Reviewed-on: #206
2025-07-11 17:03:57 +09:00
b29b76a158 [1182] 추가 견적 출력 메시지 표시 변경 및 POPUP 메시지 추가 - 사이즈 변경 2025-07-11 17:02:56 +09:00
60f8539d3f Merge pull request 'dev' (#204) from dev into prd-deploy
Reviewed-on: #204
2025-07-11 16:00:56 +09:00
9f7fedab79 [1182] 추가 견적 출력 메시지 표시 변경 및 POPUP 메시지 추가 2025-07-11 15:59:30 +09:00
47a8274fd0 QPolygon 선택영역 원복 2025-07-11 14:34:52 +09:00
929304456a Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev 2025-07-11 14:09:57 +09:00
5102fa2ec0 polygon 선택이 잘 안되는 현상 수정중 2025-07-11 14:09:50 +09:00
25bb9ede08 Merge pull request 'dev' (#202) from dev into prd-deploy
Reviewed-on: #202
2025-07-11 13:10:58 +09:00
Jaeyoung Lee
3d4040e06c Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev 2025-07-11 11:23:43 +09:00
7e0e4643e2 QPolygon containsPoint 계산 수정 2025-07-11 11:23:33 +09:00
Jaeyoung Lee
ac9d2b593e Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev 2025-07-11 11:23:27 +09:00
Jaeyoung Lee
016395ef9b 확인 용 라인 생성 코드 주석 2025-07-11 11:23:24 +09:00
849ba0e0d5 보조선 작성 시 x, 선택안됨 수정 2025-07-11 11:07:37 +09:00
c70ab4e7e9 NaN일 경우 처리 추가 2025-07-11 10:10:55 +09:00
149842547c Merge pull request '보조선 여부에 따라 3개 이상 연속점 처리 수정' (#200) from dev into prd-deploy
Reviewed-on: #200
2025-07-10 16:21:38 +09:00
b2e5055ed1 보조선 여부에 따라 3개 이상 연속점 처리 수정 2025-07-10 14:26:44 +09:00
d35230cb9b Merge pull request 'dev' (#198) from dev into prd-deploy
Reviewed-on: #198
2025-07-10 09:18:47 +09:00
9c05cde0b0 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev 2025-07-09 18:14:10 +09:00
079f3a7ff2 보조선 지붕재 할당 시 로직 수정 2025-07-09 18:13:53 +09:00
7472a8f36e Merge pull request 'dev' (#196) from dev into prd-deploy
Reviewed-on: #196
2025-07-09 17:35:53 +09:00
a093d368f2 Merge pull request '줌 확대, 축소 시 오브젝트 선택 안되는 현상 수정' (#194) from dev into prd-deploy
Reviewed-on: #194
2025-07-09 17:27:30 +09:00
30061ab167 Merge remote-tracking branch 'origin/dev' into feature/ysCha 2025-07-09 17:26:13 +09:00
0badbef52c 줌 확대, 축소 시 오브젝트 선택 안되는 현상 수정 2025-07-09 17:25:36 +09:00
6dc2b8a037 줌 확대, 축소 시 오브젝트 선택 안되는 현상 수정 2025-07-09 16:02:55 +09:00
71d92f12b8 Merge pull request 'dev' (#192) from dev into prd-deploy
Reviewed-on: #192
2025-07-08 17:21:03 +09:00
a05a63ebdc 연속된 3point 체크 로직 수정 2025-07-08 15:48:08 +09:00
f79d94c1b7 3개 이상 연속점 체크 로직 수정 2025-07-08 15:41:19 +09:00
d72444199f Merge pull request 'dev' (#190) from dev into prd-deploy
Reviewed-on: #190
2025-07-08 15:25:04 +09:00
fb9e8386fc 문자열 수정 2025-07-08 15:07:30 +09:00
f6f385e597 모듈 그림자 위로 그려지도록 수정 2025-07-08 14:59:53 +09:00
0cb3b13519 [1187] 【HANASYS DESIGN】モジュール移動について
모듈 이동 이상 수정
2025-07-08 14:33:14 +09:00
dc685f8594 Merge pull request 'dev' (#188) from dev into prd-deploy
Reviewed-on: #188
2025-07-08 13:43:59 +09:00
Jaeyoung Lee
b130d844c2 [1177] 지붕면 작성 불량 2025-07-07 17:57:40 +09:00
Jaeyoung Lee
624815ba0d Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev 2025-07-07 17:56:57 +09:00
71d774bf88 Merge pull request 'dev' (#186) from dev into prd-deploy
Reviewed-on: #186
2025-07-07 17:48:33 +09:00
8354770a9e 발전 시뮬레이션 각도 계산 수정 2025-07-07 17:36:33 +09:00
7ccdda6405 캡쳐 전 처리 수정 2025-07-07 17:05:59 +09:00
a4d74bb41b 보조선 자르기 작동 이상 수정 2025-07-07 16:15:38 +09:00
6347b99ba9 Merge pull request 'dev' (#184) from dev into prd-deploy
Reviewed-on: #184
2025-07-07 14:52:59 +09:00
2b0df28ab9 스크린샷 찍을 경우 0,0 을 기준으로 50% 축소 2025-07-07 13:42:50 +09:00
16432f94cd [1179] : 【HANASYS DESIGN】補助線について
보조선 선택 로직 수정
2025-07-07 13:19:38 +09:00
4bd21c1662 [1169] : 【HANASYS DESIGN】補助線を作成し、屋根面を割り当てしたときの表記
번역 추가
2025-07-07 11:31:48 +09:00
530ee1dba7 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev 2025-07-07 11:11:18 +09:00
9c54c135b7 발전 시뮬레이션 각도 계산 수정 2025-07-07 11:10:56 +09:00
936cc7bc92 Merge pull request '[1165] 도면을 열고 있을 때, 상부 메뉴를 눌렀을 때의 거동 재수정' (#182) from dev into prd-deploy
Reviewed-on: #182
2025-07-07 11:10:35 +09:00
Jaeyoung Lee
2b6f679f75 [1153]외줄 지붕과 맞배 지붕의 조합 2025-07-07 11:09:53 +09:00
1750ba53fd [1165] 도면을 열고 있을 때, 상부 메뉴를 눌렀을 때의 거동 재수정 2025-07-07 11:07:31 +09:00
cf290b0bc8 Merge pull request 'dev' (#180) from dev into prd-deploy
Reviewed-on: #180
2025-07-04 16:25:30 +09:00
3ab5aec767 [1164] 【HANASYS DESIGN】実測値入力で斜め線を引くときの不具合
- 배치면 그리기 직각 이상 수정
2025-07-04 14:58:48 +09:00
7286ddc97b 발전시뮬레이션 각도계산 수정 2025-07-04 14:51:23 +09:00
3a93aed738 Merge pull request '[1142] : 【HANASYS DESIGN】方位選択時の+,-の表記が異なる' (#178) from dev into prd-deploy
Reviewed-on: #178
2025-07-04 14:18:12 +09:00
d0b75f27fb [1142] : 【HANASYS DESIGN】方位選択時の+,-の表記が異なる
모듈/가대 선택 방위의 +, - 표기를 변경
2025-07-04 14:12:53 +09:00
93cda139de Merge pull request 'dev' (#176) from dev into prd-deploy
Reviewed-on: #176
2025-07-04 11:12:06 +09:00
69fa493428 Merge pull request '[1165] 도면을 열고 있을 때, 상부 메뉴를 눌렀을 때의 거동' (#174) from feature/ysCha into dev
Reviewed-on: #174
2025-07-04 11:11:17 +09:00
b46ce583ad [1165] 도면을 열고 있을 때, 상부 메뉴를 눌렀을 때의 거동 2025-07-04 11:08:44 +09:00
178d53ef14 1172 : 【HANASYS DESIGN】影を配置した時のモジュール
그림자 배치 위로 모듈 추가
2025-07-04 10:07:01 +09:00
516b323a1e 모듈 설치 계산 로직 수정 2025-07-03 18:08:29 +09:00
Jaeyoung Lee
6397de0e76 지붕 형상 로직 변경 및 시계방향 외벽선 작도에 대한 대응 2025-07-03 16:43:27 +09:00
5dae6c3738 Merge pull request '[1146] 발전 시뮬레이션에서 견적서로 이동할 수 없습니다.' (#173) from dev into prd-deploy
Reviewed-on: #173
2025-07-03 15:47:22 +09:00
213b020a3d Merge pull request '[1146] 발전 시뮬레이션에서 견적서로 이동할 수 없습니다.' (#171) from feature/ysCha into dev
Reviewed-on: #171
2025-07-03 15:46:25 +09:00
9957dae967 [1146] 발전 시뮬레이션에서 견적서로 이동할 수 없습니다. 2025-07-03 15:44:58 +09:00
e3ae71a161 Merge pull request 'dev' (#170) from dev into prd-deploy
Reviewed-on: #170
2025-07-03 15:14:57 +09:00
c4c773f929 1156 : 【HANASYS DESIGN】伏図入力と実測値入力の2種類で屋根を作成した時、寸法の整合性が取れない
지붕덮개, 배치면에 따라 모듈 크기 바뀌어야함
2025-07-03 10:29:39 +09:00
be881b656f 가대 길이 조건 수정 2025-07-02 17:09:01 +09:00
9036502f2d offset 조건 수정 2025-07-02 17:01:47 +09:00
3a076b216b Merge pull request 'dev' (#168) from dev into prd-deploy
Reviewed-on: #168
2025-07-02 14:42:09 +09:00
f24715b5d4 50% 원복 2025-07-02 11:00:35 +09:00
422a331d90 50% 제거 2025-07-02 10:52:47 +09:00
2a3ae5b0be 50% 제거 2025-07-02 10:51:57 +09:00
cc3bf02b17 capture 이미지 전 처리 추가 및 문구 수정 2025-07-02 10:47:36 +09:00
a8e0eaadbd Merge pull request '[1136]하제비치값 변경 안되는 현상 수정' (#165) from dev into prd-deploy
Reviewed-on: #165
2025-07-01 14:56:33 +09:00
09fd910b8b 하제비치값 변경 안되는 현상 수정 2025-07-01 14:53:07 +09:00
2c63b22507 Merge pull request 'dev' (#163) from dev into prd-deploy
Reviewed-on: #163
2025-06-27 12:41:03 +09:00
f9fb5b7b50 동면 정렬 로직 추가 2025-06-27 10:46:09 +09:00
3dd1053eff 대각이 아닌경우는 기본값 적용 2025-06-27 09:52:29 +09:00
249d254622 Merge pull request 'dev' (#161) from dev into prd-deploy
Reviewed-on: #161
2025-06-26 14:24:16 +09:00
468296e139 Merge pull request '운영 링크 경로 변경 (개발 -> 운영)' (#159) from feature/ysCha into dev
Reviewed-on: #159
2025-06-26 14:23:33 +09:00
3504e09ba2 운영 링크 경로 변경 (개발 -> 운영) 2025-06-26 14:22:21 +09:00
c3e4570f05 Merge pull request '한쪽흐름인 경우 설치면 이상 수정' (#158) from dev into prd-deploy
Reviewed-on: #158
2025-06-26 14:09:06 +09:00
adaa25dd53 한쪽흐름인 경우 설치면 이상 수정 2025-06-26 13:36:03 +09:00
3ac9e56def Merge pull request '[1130] 【HANASYS DESIGN】ラックレス工法で千鳥配置できない' (#156) from dev into prd-deploy
Reviewed-on: #156
2025-06-26 11:08:14 +09:00
66b626b3ea [1130] 【HANASYS DESIGN】ラックレス工法で千鳥配置できない
랙리스 공법인 경우 설치 안되는 현상 수정
2025-06-26 09:52:04 +09:00
61081475f7 Merge pull request 'dev' (#154) from dev into prd-deploy
Reviewed-on: #154
2025-06-26 09:31:56 +09:00
88a22c1ed1 Merge pull request '[1122]DESINGの見積excel様式の追加 - 견적서 추가' (#152) from feature/ysCha into dev
Reviewed-on: #152
2025-06-25 17:36:52 +09:00
1255d30c3a [1122]DESINGの見積excel様式の追加 - 견적서 추가 2025-06-25 17:29:29 +09:00
afa8d5d62a Merge pull request '양단 케이블 count 수정' (#151) from dev into prd-deploy
Reviewed-on: #151
2025-06-25 14:40:13 +09:00
b2f639a461 양단 케이블 count 수정 2025-06-25 13:46:15 +09:00
0d73df5b0e Merge pull request 'prd 팔작지붕 생성시 마루의 길이 판단 수정' (#149) from dev into prd-deploy
Reviewed-on: #149
2025-06-25 13:19:17 +09:00
Jaeyoung Lee
50cf8f6fc8 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev 2025-06-24 18:22:11 +09:00
Jaeyoung Lee
8508ce143a 팔작지붕 생성시 마루의 길이 판단 수정 2025-06-24 18:22:01 +09:00
1ce58af019 Merge pull request 'prd logging 설정 logger.log('a', a)' (#147) from dev into prd-deploy
Reviewed-on: #147
2025-06-24 17:52:55 +09:00
dd5580a3a4 Merge pull request 'logging 설정 logger.log('a', a)' (#145) from feature/ysCha into dev
Reviewed-on: #145
2025-06-24 17:51:38 +09:00
9da2a71668 logging 설정 logger.log('a', a) 2025-06-24 17:49:54 +09:00
e659a385bd Merge pull request 'dev' (#144) from dev into prd-deploy
Reviewed-on: #144
2025-06-24 16:55:57 +09:00
05d4fc3fd8 Merge pull request '적설o 피코커 x 와 적설x 피코커 o 서로 바뀜' (#142) from feature/ysCha into dev
Reviewed-on: #142
2025-06-24 16:55:05 +09:00
3cd1d0fec4 적설o 피코커 x 와 적설x 피코커 o 서로 바뀜 2025-06-24 16:54:21 +09:00
a151046943 Merge pull request 'dev' (#141) from dev into prd-deploy
Reviewed-on: #141
2025-06-24 16:07:46 +09:00
Jaeyoung Lee
74c956bf1e 저장데이터 불러올때 배치면이 없는 지붕덮개 있는 경우 메뉴를 지붕덮개로 이동 2025-06-24 14:39:31 +09:00
Jaeyoung Lee
80272b1ffd Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev 2025-06-24 14:27:48 +09:00
Jaeyoung Lee
1dfd6bbdc9 지붕 덮개 이동시 오류 사항에 대한 수정 2025-06-24 14:26:57 +09:00
3c1bfbc110 Merge pull request 'dev' (#139) from dev into prd-deploy
Reviewed-on: #139
2025-06-23 17:51:09 +09:00
93e3036aff 1110 : 【HANASYS DESIGN】外周離隔について
모듈 설치영역 offset 수정
2025-06-23 17:12:15 +09:00
Jaeyoung Lee
aa5cc6aea3 지붕덮개 변별로 형상 설정 하는 부분 오류 수정 2025-06-23 16:32:52 +09:00
4596f03d88 Merge pull request 'dev' (#137) from dev into prd-deploy
Reviewed-on: #137
2025-06-20 17:07:42 +09:00
Jaeyoung Lee
3168a29ecd Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev 2025-06-20 16:44:25 +09:00
Jaeyoung Lee
4f84221dea 지붕덮개 계산식 수정 2025-06-20 16:44:22 +09:00
c888103fef Merge pull request '랙 없음의 경우 모듈 최대 단수 적용' (#135) from dev into prd-deploy
Reviewed-on: #135
2025-06-20 16:06:08 +09:00
8feefb0b7e 랙 없음의 경우 모듈 최대 단수 적용 2025-06-20 14:22:23 +09:00
16de0d8533 Merge pull request 'debugger 제거' (#133) from dev into prd-deploy
Reviewed-on: #133
2025-06-19 17:32:13 +09:00
36211f04bd debugger 제거 2025-06-19 17:26:48 +09:00
1276230fcc Merge pull request '[1121] 【HANASYS DESIGN】両端ケーブルの積算条件' (#131) from dev into prd-deploy
Reviewed-on: #131
2025-06-19 17:21:49 +09:00
5c592e95cc [1121] 【HANASYS DESIGN】両端ケーブルの積算条件
양단케이블 견적 계산 수정
2025-06-19 17:02:29 +09:00
a41092b896 Merge pull request 'dev' (#129) from dev into prd-deploy
Reviewed-on: #129
2025-06-19 15:42:56 +09:00
a37bf6e4f7 Merge pull request '[1116] 양단케이블 오작동 => M12, S13 추가' (#127) from feature/ysCha into dev
Reviewed-on: #127
2025-06-19 15:41:41 +09:00
c25e8b2a4a [1116] 양단케이블 오작동 => M12, S13 추가 2025-06-19 15:39:23 +09:00
9b8727a63a QPolygon 기본 속성 추가(isMultipleOf45) - polygon이 이루는 모든 각도가 45도의 배수인지 확인하는 컬럼 2025-06-19 15:04:29 +09:00
8853b7ae86 모든 각이 45도의 배수인지 확인하는 함수 추가 2025-06-19 14:56:14 +09:00
9a543c4b90 Merge branch 'dev' into feature/dev-yj 2025-06-19 13:04:27 +09:00
af29c83eb4 [1115] : [見積書作成後の図面の大きさを調整して欲しい]
[작업내용] : 엑셀 다운로드 pdf 다운로드 이미지 비율 수정
2025-06-19 13:04:02 +09:00
6e87357466 원복 2025-06-19 11:22:24 +09:00
0a6d58cba8 보조선의 경우 actualSize planeSize 동일하게 수정 2025-06-19 11:21:53 +09:00
김민식
4056206af7 api: getModuleTypeItemList에 storeId 추가 2025-06-19 10:15:01 +09:00
김민식
bddc753e52 [1118] : [【HANASYS DESIGN】CST053 - DTスリムの強化施工・多雪施工が設置出来ない。]
[작업내용] : 하제비치 수정시 하위 항목 초기화
2025-06-19 10:14:34 +09:00
김민식
c464632b1a [1114] : [【HANASYS DESIGN】HuaweiPCSで併設用のPCSを積算できない。]
[작업내용] : pscItem 비교시 series조건 추가
2025-06-19 10:13:43 +09:00
fd962219dd Merge pull request 'dev' (#126) from dev into prd-deploy
Reviewed-on: #126
2025-06-18 10:43:21 +09:00
e4d6d3ab0d 오른쪽 아래 없는 경우 계산 수정 2025-06-17 15:28:37 +09:00
be399a1b52 [1099] 【HANASYS DESIGN】強化施工・多雪施工で千鳥配置できるモジュールの積算がおかしい。
견적데이터 이상 수정
2025-06-17 15:16:51 +09:00
Jaeyoung Lee
7c25ad0452 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev 2025-06-17 10:39:28 +09:00
Jaeyoung Lee
c869d6c44d 지붕덮개 계산식 수정 2025-06-17 10:39:22 +09:00
d867a8dda7 Merge pull request 'dev' (#124) from dev into prd-deploy
Reviewed-on: #124
2025-06-16 17:13:48 +09:00
1c6071b67c Merge pull request '[1104] 견적서에서 Plan 이동이후 모듈가대로 오면 이전Plan으로 되는 오류 + 시뮬레이션에서 모듈가대로 오면 plan 없어지는 오류' (#122) from feature/ysCha into dev
Reviewed-on: #122
2025-06-16 17:12:56 +09:00
fe8723fd8c [1104] 견적서에서 Plan 이동이후 모듈가대로 오면 이전Plan으로 되는 오류 + 시뮬레이션에서 모듈가대로 오면 plan 없어지는 오류 2025-06-16 17:11:53 +09:00
Jaeyoung Lee
f45862568f 지붕덮개 버그 수정 2025-06-16 14:03:50 +09:00
32a9ade07c Merge pull request 'dev' (#121) from dev into prd-deploy
Reviewed-on: #121
2025-06-13 15:32:54 +09:00
Jaeyoung Lee
7d5a235b95 불필요한 로그 및 확인용 포인트 제거 2025-06-13 14:59:28 +09:00
Jaeyoung Lee
0430be1945 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev 2025-06-13 14:44:16 +09:00
Jaeyoung Lee
f1d2cfea5b 지붕덮개 처마.케라바 변경 대응 2025-06-13 14:43:58 +09:00
d4f633fb91 Merge pull request 'dev' (#119) from dev into prd-deploy
Reviewed-on: #119
2025-06-12 13:26:39 +09:00
2e6fef951c Merge pull request 'feature/ysCha' (#117) from feature/ysCha into dev
Reviewed-on: #117
2025-06-12 13:10:45 +09:00
2315bb5eca Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into feature/ysCha 2025-06-12 13:09:00 +09:00
13547f8a2a [1104] 우편번호를 검색 2025-06-12 13:08:31 +09:00
158f8226b0 Merge pull request 'merge 하면서 빠진 부분 수정' (#116) from dev into prd-deploy
Reviewed-on: #116
2025-06-12 10:11:23 +09:00
김민식
aafa5f5948 merge 하면서 빠진 부분 수정 2025-06-11 18:29:54 +09:00
17100beb8c Merge pull request '[1105] : [【HANASYS DESIGN】混合モジュールでの回路構成について]' (#114) from dev into prd-deploy
Reviewed-on: #114
2025-06-11 18:24:57 +09:00
김민식
b74f847f76 [1105] : [【HANASYS DESIGN】混合モジュールでの回路構成について]
[작업내용] : sub module 일 경우 0.66으로 계산하게 수정
2025-06-11 18:18:12 +09:00
8f782e7b17 Merge pull request '설치면에 module이 없는 경우 처리' (#112) from dev into prd-deploy
Reviewed-on: #112
2025-06-11 14:55:25 +09:00
fed817c6d0 설치면에 module이 없는 경우 처리 2025-06-11 11:13:18 +09:00
0005643c94 Merge pull request '사진 찍기 전후 처리 추가' (#110) from dev into prd-deploy
Reviewed-on: #110
2025-06-11 09:31:19 +09:00
87e70d51c1 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into feature/ysCha 2025-06-10 18:17:50 +09:00
657d971a2b 사진 찍기 전후 처리 추가 2025-06-10 17:02:21 +09:00
c25af0e186 Merge pull request 'dev' (#108) from dev into prd-deploy
Reviewed-on: #108
2025-06-10 14:59:12 +09:00
daf5ec9ce2 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into feature/ysCha 2025-06-10 14:45:02 +09:00
765cc9c43f 처마커버 최대 단수 적용 2025-06-10 14:04:19 +09:00
08606adae6 처마커버 최대 단수 적용 2025-06-10 13:58:59 +09:00
68980a8aca seq는 1부터 시작 2025-06-10 13:16:41 +09:00
4ca3e4eae6 [1085] : 【HANASYS DESIGN】架台表示の位置について
가대의 경우도 25% 적용할 수 있도록 수정
2025-06-10 11:06:17 +09:00
6d78a45ebf [1099] : 【HANASYS DESIGN】強化施工・多雪施工で千鳥配置できるモジュールの積算がおかしい。
지지금구 설치 이상 수정
2025-06-10 11:01:53 +09:00
a0cca5fbca [1092] : [特定の組み合わせの時、回路割付後のデータローディングが終わらない]
[작업내용] : 가대 생성시 api 호출 부분에 try catch로 로딩바 제거 로직 추가
2025-06-10 10:34:51 +09:00
e19395f612 Merge pull request 'dev' (#106) from dev into prd-deploy
Reviewed-on: #106
2025-06-09 17:10:54 +09:00
b2ff662335 Merge pull request '물건정보에서 저장이후 그림으로 클릭시 에러발생' (#104) from feature/ysCha into dev
Reviewed-on: #104
2025-06-09 17:09:26 +09:00
351df68762 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into feature/ysCha 2025-06-09 17:08:10 +09:00
6f755b8f07 물건정보에서 저장이후 그림으로 클릭시 에러발생 2025-06-09 17:07:51 +09:00
김민식
a708f28c36 적용버튼 수행 후 팝업 닫는 로직 수정 2025-06-09 11:29:55 +09:00
711df07aa6 회로번호 항상 보이도록 수정 2025-06-05 18:35:30 +09:00
bb7b9c020f 이미지 캡쳐 전처리 후처리 추가 2025-06-05 18:28:58 +09:00
a0c64bdaa8 jimp-test 2025-06-05 17:49:37 +09:00
22deb24fa2 jimp-test 2025-06-05 17:46:10 +09:00
b6a2334fb8 jimp-test 2025-06-05 17:25:45 +09:00
63f12021e4 test
test
2025-06-05 17:17:40 +09:00
9f2fe5f3c8 Merge pull request 'dev' (#103) from dev into prd-deploy
Reviewed-on: #103
2025-06-05 17:05:36 +09:00
5c1c50bd46 resetCommonUtils 위치 수정 2025-06-05 15:22:14 +09:00
c65615465a 팝업 전 처리 추가 2025-06-05 15:16:11 +09:00
faf4c62328 Merge pull request 'dev' (#101) from dev into prd-deploy
Reviewed-on: #101
2025-06-05 11:32:18 +09:00
946aa231a3 견적시 이미지 운영, 개발 분리 2025-06-05 11:28:08 +09:00
c6d2904cfe Update environment configuration files to include S3_PROFILE variable for development and production environments; modify image upload path to incorporate S3_PROFILE. 2025-06-05 11:14:22 +09:00
8d3305cd04 캔버스 이벤트 오류 수정
[작업내용] : 캔버스 화면에서 이벤트 발생 후 캔버스 없는 페이지로 이동시 발생하는 오류 수정
2025-06-05 10:49:44 +09:00
c6e864d4c4 Merge pull request 'dev' (#99) from dev into dev-deploy
Reviewed-on: #99
2025-06-04 19:19:56 +09:00
0923383a42 Merge pull request 'selectedPlan?.planNo?? currentPid or pid' (#98) from feature/ysCha into dev
Reviewed-on: #98
2025-06-04 19:19:13 +09:00
31bf752970 selectedPlan?.planNo?? currentPid or pid 2025-06-04 19:17:53 +09:00
9d91533922 배치면 초기설정 시 서까래 값 이상 현상 수정 2025-06-04 17:31:42 +09:00
32884830c8 배치면 초기설정 시 서까래 값 이상 현상 수정 2025-06-04 17:21:03 +09:00
acdec6d2f2 mouseMove해당 안되는 메뉴 추가 2025-06-04 15:55:35 +09:00
5395dff081 해당 메뉴가 mouseMove 이벤트를 갖고 있는 경우 제외 2025-06-04 15:52:20 +09:00
bcb8a11d5a zoom값에 따라 mouseLine 안그려지는 부분 수정 2025-06-04 15:45:30 +09:00
ab93c5ffe5 실측치 입력 전환 안되는 현상 수정 2025-06-04 11:06:28 +09:00
904b7afd38 #1085 【HANASYS DESIGN】架台表示の位置について
지지금구 위치 수정
2025-06-04 10:35:14 +09:00
190d5cc19c Merge pull request '복사 로직 오류 수정' (#97) from dev into dev-deploy
Reviewed-on: #97
2025-06-04 09:04:38 +09:00
514545cf65 복사 로직 오류 수정
[작업내용] : 복사 로직 오류 수정
2025-06-04 09:03:21 +09:00
713b773dc0 Merge pull request '견적서 복사 로직 수정' (#96) from dev into dev-deploy
Reviewed-on: #96
2025-06-04 08:56:37 +09:00
387a1fd577 견적서 복사 로직 수정
[작업내용] : 이미지 존재 여부 확인 후 복사 진행
2025-06-04 08:54:00 +09:00
72bbf33317 Merge pull request 'dev' (#95) from dev into dev-deploy
Reviewed-on: #95
2025-06-04 08:37:35 +09:00
5412aff4ef Merge branch 'dev' into feature/dev-yj 2025-06-03 10:26:36 +09:00
16c97ae041 플랜 복사시 견적서 이미지 복사 로직 추가 2025-06-03 10:26:08 +09:00
ac00777cf1 Merge pull request 'dev' (#94) from dev into dev-deploy
Reviewed-on: #94
2025-06-02 17:46:01 +09:00
3d52889924 Merge pull request '[1080]플랜번호 미표시' (#93) from feature/ysCha into dev
Reviewed-on: #93
2025-06-02 17:44:47 +09:00
6e811c9cd4 [1080]플랜번호 미표시 2025-06-02 17:43:37 +09:00
639c057e0c 견적서 복사 -> 이미지 복사 로직 추가 2025-06-02 17:40:38 +09:00
84c18c80f0 [1079] : [ID申請画面翻訳修正]
[작업내용] : 아이디 신청 번역 수정
2025-06-02 15:00:33 +09:00
f61e50284c Merge pull request 'dev' (#92) from dev into dev-deploy
Reviewed-on: #92
2025-06-02 14:01:05 +09:00
b85a9a8aa0 Merge branch 'dev' into feature/dev-yj 2025-06-02 13:49:42 +09:00
8dafc239b3 [1076] : [金属縦葺・ハゼ式折板・重ね式折板・瓦棒葺(芯木なし)の屋根とラックレス工法の組み合わせの時]
[작업내용] : 버그 수정
2025-06-02 13:48:53 +09:00
cf462dc4a3 Merge pull request 'dev' (#91) from dev into dev-deploy
Reviewed-on: #91
2025-06-02 11:10:51 +09:00
c37ba65c80 Merge pull request '[1074]HANASYS 설계 로그인 ID 발급 신청 화면의 표시 변경' (#90) from feature/ysCha into dev
Reviewed-on: #90
2025-06-02 11:10:32 +09:00
49c3cae9fa [1074]HANASYS 설계 로그인 ID 발급 신청 화면의 표시 변경 2025-06-02 11:09:43 +09:00
aec7bb8c03 Merge pull request 'dev' (#89) from dev into dev-deploy
Reviewed-on: #89
2025-06-02 10:52:11 +09:00
806c829ada 지붕면 할당 시 alert 제거 2025-06-02 10:42:18 +09:00
286dad7021 [1076] : [金属縦葺・ハゼ式折板・重ね式折板・瓦棒葺(芯木なし)の屋根とラックレス工法の組み合わせの時、働き幅によるモジュール設置制限ができていない。]
[작업내용] : 망둥어 피치 값 변경
2025-06-02 10:16:23 +09:00
bc607d4828 Merge pull request 'dev' (#88) from dev into dev-deploy
Reviewed-on: #88
2025-06-02 09:06:41 +09:00
042e42c2cf Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev 2025-05-30 17:56:48 +09:00
8b81998f8c [1068] : [견적서 생성된 플랜복사 --> 복사된 플랜에서 모듈삭제 --> 견적서 진입이 됨.. ]
[작업내용] : 가대까지 완성 -> 삭제 안함, 그 전상태면 무조건 삭제 로직으로 구현
2025-05-30 17:56:39 +09:00
8b5299366f Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev 2025-05-30 17:33:08 +09:00
a58604478d Merge pull request 'dev' (#87) from dev into dev-deploy
Reviewed-on: #87
2025-05-30 17:30:40 +09:00
94985feed8 length가 없는경우 따로 계산 추가 2025-05-30 17:29:37 +09:00
김민식
fc5b111c6e Merge remote-tracking branch 'origin/dev' into dev 2025-05-30 17:24:15 +09:00
김민식
401d752ac7 오타 수정 2025-05-30 17:23:57 +09:00
5986fe51ff 대표라인 설정 수정 2025-05-30 17:23:16 +09:00
김민식
65b778d9e5 Merge remote-tracking branch 'origin/qcast-pub' into dev
# Conflicts:
#	src/components/floor-plan/modal/basic/step/Placement.jsx
2025-05-30 17:20:57 +09:00
김민식
66827d4c87 [1069] : [【HANASYS DESIGN】不正な配置について[アラートの表示]]
[작업내용] : 모듈 배치 화면에 안내 문구 추가
2025-05-30 17:19:35 +09:00
김민식
022125759f 🚨chore: Sync Sass 2025-05-30 17:18:22 +09:00
b71fb0992f Merge pull request 'dev' (#86) from dev into dev-deploy
Reviewed-on: #86
2025-05-30 15:48:09 +09:00
48dc3d6328 Merge branch 'dev' into feature/dev-yj 2025-05-30 14:10:50 +09:00
51d892ed29 모듈 흡착 수정, 모듈창 토글 수정 2025-05-30 14:10:31 +09:00
d16c3869d1 path 변경되면 FloorPlan state값 수정 2025-05-30 13:42:48 +09:00
8be2e3e232 Merge branch 'dev' into feature/dev-yj 2025-05-30 13:06:17 +09:00
9302ba216f 견적서 -> 메뉴 이동 후 캔버스 미노출 오류 수정 2025-05-30 13:06:07 +09:00
0ed1ed5623 견적서 사진 잘리는 현상 수정 2025-05-30 11:29:05 +09:00
2bbe4ea29e Update base URL in development configuration from 'dev.hanssys.jp' to 'dev.hanasys.jp' 2025-05-29 19:20:35 +09:00
7641148fce Add development start script to execute yarn command 2025-05-29 19:14:10 +09:00
f323d312d7 Add development start script to execute 'yarn start:dev' 2025-05-29 19:13:12 +09:00
2be0cd341a Add start scripts for cluster1 and cluster2; log run mode in config export 2025-05-29 19:11:32 +09:00
9c0c851dcb Add start script for cluster1 to execute yarn command 2025-05-29 19:05:37 +09:00
37f35c3a13 Add cluster start scripts and remove unused Config import in Footer 2025-05-29 19:04:11 +09:00
42238db254 Import configuration module in Footer component to display current mode 2025-05-29 18:35:44 +09:00
27e923ecd4 Fix build script in package.json to correctly reference the production environment file. 2025-05-29 16:49:17 +09:00
7c610c8487 Fix build script in package.json to correctly reference the production environment file 2025-05-29 16:48:55 +09:00
cccf74e2eb Merge remote-tracking branch 'origin/dev-deploy' into dev-deploy 2025-05-29 16:48:31 +09:00
4de742d1a3 Fix background image path in useRefFiles hook to use AWS S3 URL instead of local file path 2025-05-29 16:48:09 +09:00
308e41ac26 Fix background image path in useRefFiles hook to use AWS S3 URL format. 2025-05-29 16:47:17 +09:00
72cded314b Merge pull request 'dev' (#85) from dev into dev-deploy
Reviewed-on: #85
2025-05-29 16:45:27 +09:00
a17d547252 Merge pull request 'qna selectbox hidden 처리, alert변경' (#84) from feature/ysCha into dev
Reviewed-on: #84
2025-05-29 16:44:28 +09:00
df323ffb38 qna selectbox hidden 처리, alert변경 2025-05-29 16:42:34 +09:00
313be228be [1072] : [伏図入力の"屋根形状の手動設定"について]
[작업내용] : 메뉴 삭제
2025-05-29 16:37:30 +09:00
da69eaa00a Import configuration module in useRefFiles hook for enhanced background image management. 2025-05-29 16:27:09 +09:00
1a8dfa628b Import configuration module in useRefFiles hook for background image management 2025-05-29 16:26:33 +09:00
52cee232d0 Add environment configuration files for local and development modes, including API and AWS settings 2025-05-29 16:23:47 +09:00
bcf0e3d1bf Update ecosystem configuration for production environment and modify package scripts to use env-cmd for environment management 2025-05-29 16:04:14 +09:00
1cd24aeca9 Refactor image upload process: set fixed ContentType for uploaded images and convert Base64 to Blob for file handling in useRefFiles hook. 2025-05-29 15:55:53 +09:00
1fe86746c1 지붕 흐름방향 수정 2025-05-29 15:47:49 +09:00
2c475ddbdd Merge pull request 'Remove unused environment configuration display from Main component.' (#82) from feature/chore-env into dev
Reviewed-on: #82
2025-05-29 15:39:12 +09:00
5e8542efe5 Remove unused environment configuration display from Main component. 2025-05-29 15:38:46 +09:00
63930d0966 Merge pull request 'Update environment configuration files to include NEXT_PUBLIC_RUN_MODE for development, local.dev, local, and production environments. Adjust package.json script to rename build command for local development.' (#81) from feature/chore-env into dev
Reviewed-on: #81
2025-05-29 15:28:23 +09:00
772cccce89 Update environment configuration files to include NEXT_PUBLIC_RUN_MODE for development, local.dev, local, and production environments. Adjust package.json script to rename build command for local development. 2025-05-29 15:27:52 +09:00
81b9d659f6 Merge pull request 'Fix local development script in package.json to reference the correct .env.local.dev file.' (#80) from feature/chore-env into dev
Reviewed-on: #80
2025-05-29 15:14:07 +09:00
3594e96a87 Fix local development script in package.json to reference the correct .env.local.dev file. 2025-05-29 15:13:36 +09:00
272ff4f93f 텍스트 모드시 config 창은 안닫히도록 수정 2025-05-29 15:00:34 +09:00
a47db47489 Merge pull request 'Add local environment configuration for development: create .env.local.dev file and update package.json with new build script for local development.' (#79) from feature/chore-env into dev
Reviewed-on: #79
2025-05-29 14:55:59 +09:00
3034e66c4f Add local environment configuration for development: create .env.local.dev file and update package.json with new build script for local development. 2025-05-29 14:55:37 +09:00
09e5b17fe4 Merge pull request 'Add local development ecosystem configuration for Next.js application.' (#78) from feature/chore-env into dev
Reviewed-on: #78
2025-05-29 14:48:40 +09:00
2ed020bbcc Add local development ecosystem configuration for Next.js application. 2025-05-29 14:48:11 +09:00
112de1f7b4 Merge pull request 'Update startscript to use local development command for consistency with new environment configuration.' (#77) from feature/chore-env into dev
Reviewed-on: #77
2025-05-29 14:41:35 +09:00
c4e5aa1b01 Update startscript to use local development command for consistency with new environment configuration. 2025-05-29 14:41:03 +09:00
bb76dc9ad4 Merge pull request 'Add local development configuration: introduce config.local.dev.js and update config export logic in config.export.js. Enhance package.json with a new local:dev script for environment management.' (#76) from feature/chore-env into dev
Reviewed-on: #76
2025-05-29 14:39:25 +09:00
ada62ae8ec Add local development configuration: introduce config.local.dev.js and update config export logic in config.export.js. Enhance package.json with a new local:dev script for environment management. 2025-05-29 14:38:20 +09:00
febea389c9 Merge pull request 'Refactor environment configuration: update package.json scripts to use env-cmd for different environments and replace process.env.NEXT_PUBLIC_API_HOST_URL with Config().baseUrl in API calls across multiple components.' (#75) from feature/chore-env into dev
Reviewed-on: #75
2025-05-29 13:09:32 +09:00
3dc205cb46 Refactor environment configuration: update package.json scripts to use env-cmd for different environments and replace process.env.NEXT_PUBLIC_API_HOST_URL with Config().baseUrl in API calls across multiple components. 2025-05-29 13:05:47 +09:00
b8bbaba37f Update environment configuration: change API host URL to production and set development port to 5010 2025-05-29 11:17:23 +09:00
86a67420ea Update API host URL in development environment to use HTTPS 2025-05-29 11:14:35 +09:00
00d87f143f Merge pull request 'dev' (#74) from dev into dev-deploy
Reviewed-on: #74
2025-05-29 10:33:36 +09:00
9c35900ce3 Merge pull request 'Update ecosystem configuration files to differentiate production instances with unique names' (#73) from hotfix/eco-system into dev-deploy
Reviewed-on: #73
2025-05-28 18:54:06 +09:00
18c47cca2c Update ecosystem configuration files to differentiate production instances with unique names 2025-05-28 18:53:43 +09:00
52d25341de Merge pull request 'Add ecosystem configuration files for production environments with specified PORTs' (#72) from hotfix/eco-system into dev-deploy
Reviewed-on: #72
2025-05-28 18:48:47 +09:00
60449017d8 Add ecosystem configuration files for production environments with specified PORTs 2025-05-28 18:48:07 +09:00
71d6639ebd Merge pull request 'Remove NODE_ENV from environment configuration in development and production setups' (#71) from hotfix/eco-system into dev-deploy
Reviewed-on: #71
2025-05-28 18:40:24 +09:00
342b5a37f1 Remove NODE_ENV from environment configuration in development and production setups 2025-05-28 18:39:55 +09:00
e94542f15c Merge pull request 'Update environment configuration to specify PORT for development and production environments' (#70) from hotfix/eco-system into dev-deploy
Reviewed-on: #70
2025-05-28 18:24:02 +09:00
636ac163dd Update environment configuration to specify PORT for development and production environments 2025-05-28 18:23:16 +09:00
dae19a026f Merge pull request 's3운영으로변경' (#69) from feature/ysCha into dev
Reviewed-on: #69
2025-05-28 14:13:04 +09:00
acc873afcb s3운영으로변경 2025-05-28 14:11:53 +09:00
15b1cc84fd Merge pull request 'dev' (#68) from dev into dev-deploy
Reviewed-on: #68
2025-05-28 11:41:35 +09:00
4aec4e389f Merge pull request '1. 첨부파일 체크 문구 변경, 2. [995] module.circuit.fix.not.same.roof.error 일어추가, zipYn: N => NO' (#67) from feature/ysCha into dev
Reviewed-on: #67
2025-05-28 11:40:31 +09:00
cd65d80cce 1. 첨부파일 체크 문구 변경, 2. [995] module.circuit.fix.not.same.roof.error 일어추가, zipYn: N => NO 2025-05-28 11:38:46 +09:00
96eb4f25ae [1046] : 【HANASYS DESIGN】グリッド線の機能追加についての要望
임의 그리드 작동 이상 수정
2025-05-28 11:30:40 +09:00
김민식
f8a46c30fa [1070] : [[도면] 신규물건 작성할 때 이전 도면값을 유지하고 있음]
[작업내용] : 도면 이동시 canvas 데이터 초기화
2025-05-28 11:17:23 +09:00
김민식
74e65ec5c6 [995] : [【HANASYS DESIGN】回路組について]
[작업내용] : 다국어 추가
2025-05-28 10:50:47 +09:00
d7b17e491f Merge pull request 'dev' (#66) from dev into dev-deploy
Reviewed-on: #66
2025-05-28 10:43:41 +09:00
김민식
2ee0dc45f0 [1068] : [[오류] 견적서 생성된 플랜복사 --> 복사된 플랜에서 모듈삭제 --> 견적서 진입이 됨..]
[작업내용] : 회로설정 -> 수동 할당 시 회로 및 가대 삭제
2025-05-28 10:37:02 +09:00
4e8cc409d4 #1068 [오류] 견적서 생성된 플랜복사 --> 복사된 플랜에서 모듈삭제 --> 견적서 진입이 됨.. 2025-05-28 10:26:38 +09:00
a83f0d7017 Merge pull request 'dev' (#65) from dev into dev-deploy
Reviewed-on: #65
2025-05-28 09:09:53 +09:00
8ab41c8eda Merge pull request 'https://www.hanasys.jp/ => https://www.hanasys.jp' (#64) from feature/ysCha into dev
Reviewed-on: #64
2025-05-28 09:08:33 +09:00
7fb232d9ae https://www.hanasys.jp/ => https://www.hanasys.jp 2025-05-28 09:07:09 +09:00
3a37d637ed console 제거 2025-05-27 17:48:56 +09:00
c42c36501a Merge pull request 'dev' (#63) from dev into dev-deploy
Reviewed-on: #63
2025-05-27 16:58:29 +09:00
17e88e5d3c Merge pull request '[1004] 1:1 1) 버튼 색 변경 2) 상세내용 위치 변경' (#62) from feature/ysCha into dev
Reviewed-on: #62
2025-05-27 16:55:22 +09:00
5d3a7aa9cc [1004] 1:1 1) 버튼 색 변경 2) 상세내용 위치 변경 2025-05-27 16:52:42 +09:00
7a005bb161 Merge pull request 'Update environment configuration for development and production: change API host URL and update AWS credentials for Japan server.' (#61) from hotfix/convert-key into dev-deploy
Reviewed-on: #61
2025-05-27 16:01:31 +09:00
44b016dc50 Update environment configuration for development and production: change API host URL and update AWS credentials for Japan server. 2025-05-27 16:00:53 +09:00
5429e5474d Merge pull request 'Update converter API URL in development and production environment files' (#60) from hotfix/convert-key into dev-deploy
Reviewed-on: #60
2025-05-27 15:21:48 +09:00
cb14b3bb78 Update converter API URL in development and production environment files 2025-05-27 15:20:30 +09:00
c8823712a4 Merge pull request 'dev' (#59) from dev into dev-deploy
Reviewed-on: #59
2025-05-26 17:42:03 +09:00
d886bb135f Merge branch 'dev' into feature/dev-yj 2025-05-26 17:34:16 +09:00
ed68a93b20 [1059] : [HANASYS DESIGN】文字作成について]
[작업내용] : 텍스트 입력 모드 토글로 종료 모드 기능 추가
2025-05-26 17:33:45 +09:00
d8a7986120 [1046] - 댓글
1. 임의그리드 우클릭 흡착점에 생성
2. 임의그리드 생성 후 mouseLine 안그려지는 현상 수정
2025-05-26 17:30:03 +09:00
a5a1370135 Merge pull request 'dev' (#58) from dev into dev-deploy
Reviewed-on: #58
2025-05-26 16:56:30 +09:00
058a3eabc6 Merge pull request '[1004] 1:1 1) no data 추가 2)이메일 수정가능' (#57) from feature/ysCha into dev
Reviewed-on: #57
2025-05-26 16:55:36 +09:00
5101fab027 [1004] 1:1 1) no data 추가 2)이메일 수정가능 2025-05-26 16:53:43 +09:00
50aa5ca556 [1066] 새로고침 시, 지붕재 미표시 오류 2025-05-26 15:57:27 +09:00
김민식
af627035cd [938] : [【HANASYS DESIGN】 복도 입력 자릿수 상향, 자릿수 하향에 대해서 伏図入力 桁上げ・桁下げについて]
[작업내용] : 다국어 수정 및 퍼블 변경
2025-05-26 14:44:41 +09:00
김민식
4af6e13db4 🚨chore: Sync Sass 2025-05-26 14:44:04 +09:00
41ab66eb10 Merge pull request 'dev' (#56) from dev into dev-deploy
Reviewed-on: #56
2025-05-26 10:26:59 +09:00
dc6989b303 지붕재 목록 Header에서 조회 2025-05-26 09:52:30 +09:00
745665782c - 동선 이동 시 라인 안잡히는 현상 수정 2025-05-23 16:33:42 +09:00
김민식
765855b361 외벽선 작성 팝업 안내 문구 추가 및 관련 다국어 추가 2025-05-23 16:33:42 +09:00
2cbcd19b65 - 동선 이동 시 라인 안잡히는 현상 수정 2025-05-23 15:55:26 +09:00
김민식
c029560c4a 외벽선 작성 팝업 안내 문구 추가 및 관련 다국어 추가 2025-05-23 15:49:33 +09:00
b4733ebfa6 Merge pull request 'chore: convert api key 수정' (#55) from hotfix/convert-key into dev-deploy
Reviewed-on: #55
2025-05-23 15:38:43 +09:00
a4af9dc1ea chore: convert api key 수정 2025-05-23 15:37:50 +09:00
eda80451a1 Merge remote-tracking branch 'origin/dev' into dev 2025-05-23 15:33:19 +09:00
f8621d2d78 fix: update NEXT_PUBLIC_CONVERTER_API_URL in environment files for secure API access 2025-05-23 15:33:13 +09:00
d0eb50313a Merge pull request 'dev' (#54) from dev into dev-deploy
Reviewed-on: #54
2025-05-23 14:51:20 +09:00
d59883b9a6 Merge pull request '[1004] 1:1 문의게시판 변경' (#53) from feature/ysCha into dev
Reviewed-on: #53
2025-05-23 14:50:40 +09:00
6cb91a26c9 [1004] 1:1 문의게시판 변경 2025-05-23 14:49:34 +09:00
03ec99dbb4 Merge pull request 'dev' (#52) from dev into dev-deploy
Reviewed-on: #52
2025-05-23 14:33:52 +09:00
a7f572562e Merge pull request '[1004] 1:1 문의게시판 추가' (#51) from feature/ysCha into dev
Reviewed-on: #51
2025-05-23 14:31:09 +09:00
bbb8bef37d [1004] 1:1 문의게시판 추가 2025-05-23 14:30:05 +09:00
cf3e64deb5 지붕면 할당 시 순서 재 정렬 되는 현상 수정 2025-05-23 13:17:33 +09:00
68d961858f Merge pull request 'dev' (#50) from dev into dev-deploy
Reviewed-on: #50
2025-05-23 10:38:05 +09:00
37b12b5deb 번역 오류 수정
[작업내용] : alert 문구 ? 삭제
2025-05-23 10:24:21 +09:00
8f693478b8 [995] : [回路組について]
[작업내용] : 번역 누락 수정
2025-05-23 10:20:42 +09:00
ec8d767809 Merge pull request 'a,b 타입 버그 수정' (#49) from dev into dev-deploy
Reviewed-on: #49
2025-05-22 13:45:54 +09:00
Jaeyoung Lee
5ab58de70b a,b 타입 버그 수정 2025-05-22 13:31:55 +09:00
c52d372148 Merge pull request '지붕덮개 버그 수정' (#48) from dev into dev-deploy
Reviewed-on: #48
2025-05-21 17:33:32 +09:00
Jaeyoung Lee
6a0144826c 지붕덮개 버그 수정 2025-05-21 16:34:14 +09:00
ec93257720 Merge pull request 'feature/jaeyoung' (#46) from feature/jaeyoung into dev
Reviewed-on: #46
2025-05-21 13:53:44 +09:00
Jaeyoung Lee
966cc0517b Merge branch 'feature/jaeyoung' of https://git.hanasys.jp/qcast3/qcast-front into feature/jaeyoung 2025-05-21 13:43:27 +09:00
Jaeyoung Lee
2a8d373f67 마루지붕, 케라바지붕 그리기 첫번째 정리 작업본 2025-05-21 13:43:01 +09:00
364d79fafd [1054] - 혼합모듈일 경우 지지금구 설치 수정 2025-05-21 11:44:44 +09:00
18b7abdfd5 Merge pull request '#1052 물건 우편번호 검색팝업 적용기능' (#45) from feature/qcast-1052 into dev
Reviewed-on: #45
2025-05-21 10:07:36 +09:00
27ce59316d #1052 물건 우편번호 검색팝업 적용기능 2025-05-21 10:06:52 +09:00
0295cf6c7a 배치면 그리기 - 흡착점 ON/OFF 작동 오류 수정 2025-05-21 10:04:24 +09:00
56ae730efa Merge pull request '#1053 전각/반각 length체크 수정' (#44) from feature/qcast-1053 into dev
Reviewed-on: #44
2025-05-21 09:40:46 +09:00
a0b3f7fbe5 #1053 전각/반각 length체크 수정 2025-05-21 09:39:58 +09:00
16e9ce173a Merge pull request '#1053 물건화면 담당자 자리수' (#43) from feature/qcast-1053 into dev
Reviewed-on: #43
2025-05-21 09:32:41 +09:00
99886ad61e #1053 물건화면 담당자 자리수 2025-05-21 09:29:16 +09:00
ee2417cf6f 배치면 그리기 - 수직 수평모드 작동 수정 2025-05-20 18:37:38 +09:00
4ec191dcb0 1. 외벽선그리기, 배치면그리기 에서 흡착점 동작
2. 외벽선그리기, 배치면그리기 에서 그리드, 흡착점 선택 불가
3. 이동, 복사 시 길이 줄어듬
4. 옵션 설정 닫을 때, 임의그리드,흡착점 선택  해제
2025-05-20 17:02:56 +09:00
김민식
4bbe99bda8 Merge remote-tracking branch 'origin/qcast-pub' into dev 2025-05-20 15:36:44 +09:00
김민식
afd59e580f 그리드 이동, 복사 버그 수정 2025-05-20 15:36:20 +09:00
cec871fb70 그리드 이동 단위 수정 2025-05-20 10:52:22 +09:00
46dc8123df 임의그리드 이벤트 수정 2025-05-20 10:34:24 +09:00
d4ae092ae5 흡착점 작동 관련 수정 2025-05-20 10:23:36 +09:00
21d4f48109 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into feature/jaeyoung 2025-05-19 15:19:20 +09:00
김민식
70a4c85149 [1049] : [【HANASYS DESIGN】文字修正]
[작업내용] : 다국어 수정
2025-05-19 13:57:34 +09:00
김민식
51acb561b8 Merge remote-tracking branch 'origin/qcast-pub' into dev 2025-05-19 13:52:45 +09:00
김민식
e482538dec [995] : [【HANASYS DESIGN】回路組について]
[작업내용] : 수동 회로 할당 시 동일면/경사인 지붕면 일 경우 불가하게 수정 및 관련 다국어 메시지 추가
2025-05-19 13:50:39 +09:00
김민식
ec48350c05 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into qcast-pub
# Conflicts:
#	src/locales/ja.json
2025-05-19 13:48:15 +09:00
김민식
69c48cecf8 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev
# Conflicts:
#	src/locales/ja.json
2025-05-19 10:32:43 +09:00
김민식
46cc507e6d Merge remote-tracking branch 'origin/qcast-pub' into dev 2025-05-19 10:30:47 +09:00
김민식
98f87553e6 [1049] : [【HANASYS DESIGN】文字修正]
[작업내용] : 일본어 번역 수정
2025-05-19 10:30:15 +09:00
97d20321ed [1049] : [文字修正]
[작업내용] : 번역 작업 진행
2025-05-19 10:29:49 +09:00
79e33880ac direction 설정 시 default값 추가 2025-05-16 14:39:11 +09:00
0c67a6b212 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into feature/jaeyoung 2025-05-16 10:37:39 +09:00
46f9385163 enterCheck 전부 추가 2025-05-16 10:37:30 +09:00
59a55e4c88 961 : 외벽선 그리기 및 배치면 그리기에 대해서 外壁線を描く・配置面の描画について
1. "외벽선 그리기", "배치면 그리기"에서 선을 그을때 우클릭으로 "undo" 할 수 있도록 해주세요.
2. 마찬가지로 "외벽선 그리기", "배치면 그리기"로 지붕면을 확정할 때 Enter Key로 지붕면을 확정할 수 있도록 해주시기 바랍니다.
2025-05-16 10:31:23 +09:00
b5b2ac2104 보조선 , 지붕선 만나는 경우 지붕선부터 작업 완료 2025-05-16 10:09:22 +09:00
aa49e4c075 Merge pull request 'feature/ysCha' (#42) from feature/ysCha into dev
Reviewed-on: #42
2025-05-15 16:51:42 +09:00
96e989aa40 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into feature/ysCha 2025-05-15 16:15:22 +09:00
Jaeyoung Lee
c8d806b220 Merge branch 'feature/jaeyoung' of https://git.hanasys.jp/qcast3/qcast-front into feature/jaeyoung 2025-05-15 16:00:49 +09:00
Jaeyoung Lee
410da6f541 지붕 보조선 작성 수정 중 2025-05-15 16:00:40 +09:00
24d8e14235 Merge pull request 'fix: update API server path in development environment to use secure HTTPS URL' (#39) from hotfix/api-server-path into dev
Reviewed-on: #39
2025-05-15 14:13:36 +09:00
e6da798e43 fix: update API server path in development environment to use secure HTTPS URL 2025-05-15 14:13:06 +09:00
00ed6f250b Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into feature/ysCha 2025-05-14 15:20:00 +09:00
9609cad8f5 ?? => ? 변경
Reviewed-on: #38
2025-05-14 15:18:50 +09:00
80bf9263fd ?? => ? 변경 2025-05-14 15:16:28 +09:00
457253f0f6 범위로 수정 2025-05-14 14:41:23 +09:00
5c03e631ae Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into feature/ysCha 2025-05-14 14:08:06 +09:00
5b322d0143 feature/ysCha (#37)
[948] - 견적서 양단케이블 selectbox 추가 케이블 체크 가능. 단어 추가
2025-05-14 14:06:45 +09:00
8a49f7169b [948] - 견적서 양단케이블 selectbox 추가 케이블 체크 가능. 단어 추가 2025-05-14 14:00:29 +09:00
6dcf9b6836 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into feature/ysCha 2025-05-14 12:58:20 +09:00
4bff451dc4 refactor: enhance useRefFiles hook with additional comments and improve file handling logic for background images 2025-05-14 11:22:50 +09:00
e26673b78a fix: update background image URL in useRefFiles hook to utilize AWS S3 base URL 2025-05-14 11:03:43 +09:00
a8c57b1c53 지붕재 할당후 처리 추가 2025-05-14 10:59:29 +09:00
ad12f9b817 fix: update API host URL in development environment file 2025-05-14 10:59:15 +09:00
5e359bb0ba fix: update API host URL and S3 base URL in environment files; refactor useRefFiles hook to utilize environment variables for API endpoints 2025-05-14 10:54:16 +09:00
e8457da05f 거리 0이 아닌 1 미만으로 범위 수정 2025-05-14 10:50:04 +09:00
55d37af144 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into feature/ysCha 2025-05-14 10:41:59 +09:00
4b120326db length가 1 미만인 경우 actualSize 작성할 필요 없음 2025-05-14 10:30:08 +09:00
2f5f49c69c canvas 시작시 오류 수정 2025-05-14 09:59:46 +09:00
nalpari
3fefb4b22f feat: update environment variables and API endpoint in useImgLoader hook 2025-05-13 16:33:37 +09:00
c9e0650e6a Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into feature/ysCha 2025-05-13 16:28:23 +09:00
a631f5ccb6 Merge pull request 'feature/aws-s3-upload' (#36) from feature/aws-s3-upload into dev
Reviewed-on: #36
2025-05-13 16:21:10 +09:00
01dda7e43e Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into feature/ysCha 2025-05-13 16:12:16 +09:00
ad2aa50d83 feat: 캔버스 s3 저장 테스트 및 확장자 수정 2025-05-13 15:24:57 +09:00
a3b25c3403 Merge branch 'feature/jaeyoung' of https://git.hanasys.jp/qcast3/qcast-front into feature/jaeyoung 2025-05-13 15:22:19 +09:00
19ec9ad874 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into feature/jaeyoung
# Conflicts:
#	src/common/common.js
#	src/components/floor-plan/CanvasFrame.jsx
#	src/hooks/roofcover/useWallLineOffsetSetting.js
#	src/util/qpolygon-utils.js
2025-05-13 15:22:11 +09:00
Jaeyoung Lee
ead3429912 Merge branch 'feature/jaeyoung' of https://git.hanasys.jp/qcast3/qcast-front into feature/jaeyoung 2025-05-13 15:21:42 +09:00
Jaeyoung Lee
3574ed3a9b 지붕형상 분할 대응 중 2025-05-13 15:21:33 +09:00
9d3ddd3ec1 Merge branch 'dev' into feature/aws-s3-upload 2025-05-13 15:17:57 +09:00
1d4100a579 chore: nextjs version update 2025-05-13 15:13:59 +09:00
bca3100a12 commonCode get in hook => page 한번만 호출하도록 2025-05-13 15:11:12 +09:00
001c1cfd95 useEffect내부 async 제거 2025-05-13 14:46:57 +09:00
d06733ed4c 원복 2025-05-13 14:36:11 +09:00
bac3fbef70 addLengthText 추가 2025-05-13 14:34:10 +09:00
7bf548de2b 주석 수정 2025-05-13 14:22:25 +09:00
555671ade7 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into feature/ysCha 2025-05-13 13:03:59 +09:00
c52d914c6e validCheck 수정 2025-05-13 10:55:58 +09:00
a652d09b8d [1036] : [プランをコピー/移動する時の自動保存について]
[작업내용] : 탭간 이동, 복사시 저장 여부 확인 로직 추가
2025-05-13 10:46:26 +09:00
72c020acea pointOnLine 수정 2025-05-13 09:57:36 +09:00
976396cd78 valid에 해당하지않는 polygon이 2개 이상일 경우 처리 2025-05-12 18:18:31 +09:00
f4ba3058c7 range 계산 수정 2025-05-12 16:04:53 +09:00
8b80277884 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into feature/ysCha 2025-05-12 16:02:00 +09:00
f48a8b31d3 지붕재 분할 시 addLengthText 제거 2025-05-12 14:09:48 +09:00
4b8287579c Merge branch 'dev' into feature/dev-yj 2025-05-12 13:49:29 +09:00
f03d35ec8d [1037] : [モジュール1枚を削除する時の表示]
[작업내용] : 모듈 선택시 아웃라인 두께 조정
2025-05-12 11:11:09 +09:00
fb297b9a0a Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into feature/ysCha 2025-05-12 09:04:59 +09:00
553cbd44db 오차 적용 2025-05-09 16:52:04 +09:00
56b8917345 [1014] : [819の追加 レイアウト指定配置の自動配置]
[작업내용] : 멀티모듈일 경우에 북면 모듈이 같이 계산되는 로직 수정
2025-05-09 15:48:08 +09:00
김민식
dbae3380d7 Merge remote-tracking branch 'origin/qcast-pub' into dev 2025-05-09 15:36:55 +09:00
김민식
35708f65e9 지붕재별 가대정보 저장 로직수정 2025-05-09 15:36:28 +09:00
aa85de5280 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into feature/ysCha 2025-05-09 15:30:47 +09:00
391fe39a2d [1014] : [819の追加 レイアウト指定配置の自動配置]
[작업내용] : 하단방향 오류 수정
2025-05-09 15:04:21 +09:00
김민식
d1af28987b Merge remote-tracking branch 'origin/qcast-pub' into dev 2025-05-09 14:19:47 +09:00
김민식
b8dad62116 [929] : [翻訳変更]
[작업내용] : 다국어 수정 및 validation 로직 추가
2025-05-09 14:19:05 +09:00
d54103943d 0번째일 경우 처리 2025-05-09 13:45:10 +09:00
9b6c6cdfae Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into feature/ysCha 2025-05-09 09:07:15 +09:00
Jaeyoung Lee
f0c2f0bad2 Merge branch 'feature/jaeyoung' of https://git.hanasys.jp/qcast3/qcast-front into feature/jaeyoung 2025-05-08 18:05:44 +09:00
Jaeyoung Lee
2f261cb0c0 박공지붕 동선이동 대응 정리 (혼합 처리 중) 2025-05-08 18:05:38 +09:00
6949236960 [1014] : [819の追加 レイアウト指定配置の自動配置]
[작업내용] : 북면설치 모듈일 경우, 북면과, 북면이 아닐때 분기 처리
2025-05-08 18:02:41 +09:00
e961648d85 [1014] : [819の追加 レイアウト指定配置の自動配置]
[작업내용] : 멀티모듈일때 북면 모듈 포함일 경우 북면이 아닌 면에 설치되는 오류 수정
2025-05-08 16:55:39 +09:00
ae789672b4 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into feature/ysCha 2025-05-08 16:03:44 +09:00
cbde82b0ab Merge branch 'dev' into feature/dev-yj-layout 2025-05-08 15:16:41 +09:00
김민식
b2065e7699 [1030] : [【HANASYS DESIGN】文字修正]
[작업내용] : 다국어 수정
2025-05-08 14:48:05 +09:00
dc743cee8f getRoofMaterialList 한번만 호출하도록 수정 2025-05-08 14:21:44 +09:00
bb6a796b9f 지붕재 할당 시 추가 작업 및 보조선 제거 조건 수정 2025-05-08 13:44:31 +09:00
105158e298 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into feature/ysCha 2025-05-08 13:33:35 +09:00
ef75ad0ef8 chore: update environment variables for Japanese server configuration 2025-05-08 13:29:13 +09:00
김민식
c0c3295b04 [1027] : [【HANASYS DESIGN】横葺に使用する金具の離隔について]
[작업내용] : length 변경시 하위 필드 초기화
2025-05-08 13:21:54 +09:00
772f465f9d Merge pull request 'dev' (#35) from dev into dev-deploy
Reviewed-on: #35
2025-05-08 12:37:05 +09:00
cbdbade89f Merge pull request '939 - 배치면 그리기에 흡착점 추가' (#34) from feature/ysCha into dev
Reviewed-on: #34
2025-05-08 10:13:16 +09:00
704fe887f1 939 - 배치면 그리기에 흡착점 추가 2025-05-08 10:09:45 +09:00
14764d295c polygonLines중 intersection 포인트가 있는 경우 처리 추가 2025-05-08 09:59:09 +09:00
47245e438c [1014] : [819の追加 レイアウト指定配置の自動配置]
[작업내용] : 불필요 소스 삭제
2025-05-08 09:48:31 +09:00
0fc7ed857d [공통] : [선택면 최상위 이동]
[작업내용] : canvas 객체 선택시 선택된 객체 최상위 레이어으로 이동
2025-05-07 17:56:36 +09:00
f3b8ee40fa [1014] : [819の追加 レイアウト指定配置の自動配置]
[작업내용] : 문구 수정 및 alert icon 정리
2025-05-07 16:56:28 +09:00
3a66920104 Merge branch 'dev' into feature/dev-yj-layout 2025-05-07 15:19:25 +09:00
d244ba3b97 [1014] : [819の追加 レイアウト指定配置の自動配置]
[작업내용] : 고도화 단수 열수 자동 배치 작업
2025-05-07 15:18:59 +09:00
78258fc9c1 roof 나눌때 point 중복 제거 2025-05-07 15:05:44 +09:00
d6b0e4994a # [1014] : 819の追加 レイアウト指定配置の自動配置
# [작업내용] : http://1.248.227.176:43333/issues/1014
2025-05-07 14:47:08 +09:00
8da2ab0820 오차 범위 수정 2025-05-07 13:31:00 +09:00
김민식
f846644145 모듈 간격 영역 추가 2025-05-07 11:34:22 +09:00
김민식
655f0781a2 🚨chore: Sync Sass 2025-05-07 11:34:03 +09:00
381c639d18 roofPoints valid 체크 추가 2025-05-07 11:21:26 +09:00
92c33c9c11 polygonLines가 없는 경우 처리 2025-05-07 10:46:57 +09:00
c01ed83b40 Merge branch 'dev' into feature/dev-yj-layout 2025-05-02 17:01:22 +09:00
김민식
76dd1aaa43 Merge remote-tracking branch 'origin/qcast-pub' into dev 2025-05-02 16:43:15 +09:00
김민식
9a800d307b - 다설시공 다국어 변경
- trestle에서 참조하는 지붕정보에 참조하는 정보중 raftBaseCd -> raft 로 변경
2025-05-02 16:42:38 +09:00
e3df21f827 http://1.248.227.176:43333/issues/1022 상단 유틸리티 title 추가 2025-05-02 16:16:23 +09:00
f5c098546c Merge branch 'dev' into feature/dev-yj-layout 2025-05-02 15:50:37 +09:00
08722e8b51 모듈 레이아웃 설치 작업중 2025-05-02 15:50:27 +09:00
김민식
7f1ef1ace4 Merge remote-tracking branch 'origin/qcast-pub' into dev 2025-05-02 10:01:59 +09:00
김민식
f52fe72d2a 시공법 버튼명 api 데이터로 수정 2025-05-02 10:01:26 +09:00
d5bba49ca9 console.log 삭제 2025-05-02 09:23:59 +09:00
a7eb2f7598 지붕재 할당 함수 수정 2025-04-30 18:05:25 +09:00
902d13b273 오차 범위 수정 2025-04-30 16:17:03 +09:00
937f2eb3fe x가 전부 같거나, y가 전부 같은경우 제거 2025-04-30 13:26:05 +09:00
adc0c41a67 오류 수정 2025-04-30 13:18:48 +09:00
8412462a8b 라인 위에 있는지 확인 함수 수정
지붕재 할당 시 오류 수정
2025-04-30 13:08:58 +09:00
9f88880ac2 주석 내용 추가 2025-04-29 17:50:36 +09:00
9eda4aa834 제대로 안나눠지는 현상 수정 2025-04-29 17:48:38 +09:00
234e7f1cf7 Merge branch 'dev' into feature/dev-yj-layout 2025-04-29 15:58:16 +09:00
c40ba2586f Merge pull request '886 - 견적서 정가 다운로드 : 0 => "open"' (#33) from feature/ysCha into dev
Reviewed-on: #33
2025-04-29 15:04:53 +09:00
fc8e85b76c 886 - 견적서 정가 다운로드 : 0 => "open" 2025-04-29 15:02:19 +09:00
a45bba3a7b 오차범위 계산 추가 2025-04-29 14:26:21 +09:00
ed27f2ed93 모듈 자동 레이아웃 설치 validate 작업중 2025-04-29 13:09:46 +09:00
69763dd413 Merge branch 'feature/jaeyoung' of https://git.hanasys.jp/qcast3/qcast-front into feature/jaeyoung 2025-04-28 18:08:07 +09:00
5f648632dd 최단거리 roof 나누기 추가 2025-04-28 18:07:54 +09:00
Jaeyoung Lee
95e6f4c0a4 박공지붕 6각 대응 추가 후 정리. 2025-04-28 18:06:55 +09:00
1c7b81c99f 모듈 자동 레이아웃 수정 2025-04-28 14:09:58 +09:00
fe957102d9 모듈 자동 레이아웃 설치 2025-04-28 13:20:35 +09:00
034bfb374e Merge pull request '1019 - ja, ko 변경' (#32) from feature/ysCha into dev
Reviewed-on: #32
2025-04-25 14:47:44 +09:00
71d3a33bf4 1019 - ja, ko 변경 2025-04-25 14:46:00 +09:00
0f079080a1 Merge pull request '1018 - 캔버스팝업상태 복사저장, 조회 : unescapeString() if => while 변경' (#31) from feature/ysCha into dev
Reviewed-on: #31
2025-04-25 14:05:14 +09:00
9bf961441b 1018 - 캔버스팝업상태 복사저장, 조회 : unescapeString() if => while 변경 2025-04-25 14:02:35 +09:00
3b96344943 index 세팅 추가 2025-04-24 13:30:58 +09:00
4be94e41a2 Merge pull request '1006 - ja 추가변경' (#30) from feature/ysCha into dev
Reviewed-on: #30
2025-04-24 11:36:19 +09:00
bd62bf7028 1006 - ja 추가변경 2025-04-24 11:34:59 +09:00
93c8bad986 Merge pull request '1012 - top select box link name 변경' (#29) from feature/ysCha into dev
Reviewed-on: #29
2025-04-24 10:48:54 +09:00
203ddfbdf3 1012 - top select box link name 변경 2025-04-24 10:39:47 +09:00
b11424e70e 육지붕일 경우 값 고정 2025-04-24 10:29:36 +09:00
49d85d437b Merge branch 'dev' into feature/dev-yj 2025-04-24 10:27:42 +09:00
88e2c8ed05 Merge pull request '1003 - calcLineActualSize() degree가 undefined => 0 처리' (#28) from feature/ysCha into dev
Reviewed-on: #28
2025-04-24 09:51:20 +09:00
6b56af6119 1003 - calcLineActualSize() degree가 undefined => 0 처리
showLine 함수의 canvas.getObjects().find()가 undefined => Optional chaining (?.) 처리
2025-04-24 09:45:36 +09:00
287ce5c85a 보조선 이동, 복사 시 나누기 10 적용 2025-04-23 17:09:33 +09:00
737fcf0b30 Merge pull request '986 - 시공사번호 추가, 시공사(userId) 데이터만 조회' (#27) from feature/ysCha into dev
Reviewed-on: #27
2025-04-23 16:16:42 +09:00
f3dfc50f6e 986 - 시공사번호 추가, 시공사(userId) 데이터만 조회 2025-04-23 16:13:07 +09:00
5b74a8ee6b Merge branch 'dev' into feature/dev-yj 2025-04-23 15:45:43 +09:00
김민식
77e34d849d Merge remote-tracking branch 'origin/qcast-pub' into dev 2025-04-23 15:44:32 +09:00
김민식
b6549e06b2 setManagementState 삭제 2025-04-23 15:44:05 +09:00
abe0ddb731 Merge branch 'dev' into feature/dev-yj
# Conflicts:
#	src/components/floor-plan/modal/circuitTrestle/CircuitTrestleSetting.jsx
#	src/components/floor-plan/modal/circuitTrestle/step/type/PassivityCircuitAllocation.jsx
2025-04-23 15:05:56 +09:00
4a8428f9f7 #1008 보조선 관련 오류 수정 2025-04-23 13:39:32 +09:00
김민식
7ff0178b19 roofConstruction -> roofConstructions 수정 2025-04-23 11:11:53 +09:00
김민식
6d310a6a32 Merge remote-tracking branch 'origin/qcast-pub' into dev 2025-04-23 10:55:30 +09:00
김민식
ebd84967f2 moduleSelectionDataLoad 삭제 및 setData 로직 수정 2025-04-23 10:54:58 +09:00
8de8416160 managementStateLoaded삭제 2025-04-23 10:10:57 +09:00
c6b96bec23 chore: update environment variables to use protocol-relative URLs for host 2025-04-23 10:03:25 +09:00
82c8b25895 Update ecosystem configuration for production deployment 2025-04-23 10:02:13 +09:00
김민식
2b81cc192e Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into qcast-pub 2025-04-22 17:09:41 +09:00
김민식
028fa7f9bf LocalStorage -> API 변경 2025-04-22 17:09:18 +09:00
e0d0eaf017 Merge pull request '991-견적서 pricing 버튼 클릭, confirm 노출' (#26) from feature/ysCha into dev
Reviewed-on: #26
2025-04-22 13:57:12 +09:00
04d49e6aa6 991-견적서 pricing 버튼 클릭, confirm 노출 2025-04-22 13:54:40 +09:00
e938feec2f Merge pull request '1006-오타수정 및 변경' (#25) from feature/ysCha into dev
Reviewed-on: #25
2025-04-22 13:24:00 +09:00
d4a83c4ec5 1006-오타수정 및 변경 2025-04-22 13:21:30 +09:00
3d8e8dfed1 Merge pull request 'feature/ysCha' (#24) from feature/ysCha into dev
Reviewed-on: #24
2025-04-21 13:15:43 +09:00
ac4449f3f3 993 - HANASYS設計 => HANASYS DESIGN 변경및 메인 로고 디자인 변경 2025-04-21 13:13:26 +09:00
159bbf2e6e 993 - HANASYS設計 => HANASYS DESIGN 변경및 메인 로고 디자인 변경 2025-04-21 13:12:43 +09:00
김민식
98d5af6d50 Merge remote-tracking branch 'origin/qcast-pub' into dev 2025-04-21 11:33:47 +09:00
김민식
11a7061e0a roof 변경했을때 setupCover 값 안 넣는 버그 수정 2025-04-21 11:33:08 +09:00
61d8c083b2 Merge pull request '975 - 케이블 선택 버그 수정' (#22) from feature/ysCha into dev
Reviewed-on: #22
2025-04-18 16:41:58 +09:00
479f5d342a 975 - 케이블 선택 버그 수정 2025-04-18 16:40:52 +09:00
da162ac259 모듈,회로구성 => 모듈/가대 설정 메뉴에서 roof 선택 안되도록 수정 2025-04-18 16:03:52 +09:00
김민식
c0b38a00b3 Merge remote-tracking branch 'origin/qcast-pub' into dev 2025-04-18 14:52:55 +09:00
김민식
bee4ee3e74 다국어 개행문자 추가 2025-04-18 14:52:29 +09:00
김민식
0fb3a0bb2a Merge remote-tracking branch 'origin/qcast-pub' into dev 2025-04-18 14:41:33 +09:00
김민식
b12b7aaa3a input type text-> number 수정 2025-04-18 14:41:11 +09:00
김민식
cb3e5dfc47 Merge remote-tracking branch 'origin/qcast-pub' into dev 2025-04-18 14:39:30 +09:00
김민식
77b473c0a1 input type text-> number 수정 2025-04-18 14:39:07 +09:00
김민식
253ddc258a Merge remote-tracking branch 'origin/qcast-pub' into dev 2025-04-18 14:25:14 +09:00
김민식
cb960baa0c Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev 2025-04-18 14:25:08 +09:00
김민식
51a2782d3c plan 추가 로직 삼항연산자 -> if 로 변경 2025-04-18 14:24:46 +09:00
d1ea6229a2 치수표시 selector로 변경 2025-04-18 11:44:58 +09:00
김민식
e717c28758 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev
# Conflicts:
#	README.md
2025-04-18 10:23:33 +09:00
a08c609caa Merge branch 'dev' into feature/yj-layoutSetup 2025-04-18 09:24:53 +09:00
76938af446 지붕면 할당 시에도 실치수 적용 추가 2025-04-17 16:40:40 +09:00
a3b62db4fa 지붕덮개 메뉴 - 복도치수 적용
그 외 메뉴 - 실치수 적용
2025-04-17 16:35:06 +09:00
김민식
21dcdfb3ef 다국어 수정 2025-04-17 14:22:58 +09:00
김민식
29ccf72c5e 수동으로 방위설정 안되던 버그 수정 2025-04-17 14:13:05 +09:00
김민식
4c219ddce9 validation 방식 수정 2025-04-17 14:12:50 +09:00
김민식
ed721f36d0 Merge branch 'feature/yj-layoutSetup' of https://git.hanasys.jp/qcast3/qcast-front into qcast-pub 2025-04-17 14:12:16 +09:00
김민식
30414568e0 다국어 추가 2025-04-17 14:11:55 +09:00
06812b77af Merge branch 'dev' into feature/yj-layoutSetup 2025-04-16 17:17:50 +09:00
bcc91e7609 반대 2025-04-16 15:53:54 +09:00
bdcacd5542 변별로 설정 후 한쪽흐름과 같이 변 속성을 선택할 경우 처리 추가 2025-04-16 15:39:48 +09:00
51e1f70c24 Merge pull request '견적서 저장시 체크' (#21) from feature/qcast-886 into dev
Reviewed-on: #21
2025-04-15 18:08:55 +09:00
3c14f5de52 견적서 저장시 체크 2025-04-15 18:08:05 +09:00
8f4e53c759 #982 표시 문자 변경 2025-04-14 18:14:49 +09:00
a1871e105f Merge pull request '975- 아이템(양단케이블) 타입 코드 추가에 따른 아이템 목록 수정' (#20) from feature/ysCha into dev
Reviewed-on: #20
2025-04-14 15:03:34 +09:00
ed69943dc6 975- 아이템(양단케이블) 타입 코드 추가에 따른 아이템 목록 수정 2025-04-14 15:01:18 +09:00
김민식
2897dc26e3 Orientation에서 nextstep시 compas 매개변수 추가 2025-04-11 16:41:28 +09:00
61f1036a24 변별로 설정 - 한쪽흐름 출폭 적용 추가 2025-04-11 10:58:04 +09:00
김민식
08a29aec26 cvrCheck, snowCheck 수정 2025-04-10 14:23:43 +09:00
김민식
a742fc1f9c Merge remote-tracking branch 'origin/qcast-pub' into feature/yj-layoutSetup
# Conflicts:
#	src/hooks/module/useModuleTrestle.js
2025-04-10 13:55:13 +09:00
김민식
cf900c3e92 lenBase -> length 로 수정 2025-04-10 13:54:05 +09:00
김민식
e2b8bc19b1 lenBase 버그 수정 2025-04-10 13:33:42 +09:00
김민식
14aa4227f4 Merge branch 'feature/yj-layoutSetup' of https://git.hanasys.jp/qcast3/qcast-front into qcast-pub 2025-04-10 13:24:41 +09:00
김민식
af63891df2 지붕재 수정시 모듈.가대 정보 초기화 2025-04-10 13:24:29 +09:00
070e9d933f api 오류 시 처리 추가 2025-04-10 11:36:44 +09:00
e67fc749f4 마우스 휠 줌 이벤트 보정 2025-04-10 10:55:00 +09:00
김민식
adf9febc8c Merge branch 'feature/yj-layoutSetup' of https://git.hanasys.jp/qcast3/qcast-front into feature/yj-layoutSetup 2025-04-10 10:54:29 +09:00
김민식
089ffed7af 키 명 수정 2025-04-10 10:54:21 +09:00
김민식
990de88caf 키 값 수정 2025-04-10 10:51:59 +09:00
d53d7f4220 Merge branch 'dev' into feature/yj-layoutSetup 2025-04-10 10:48:13 +09:00
김민식
134becaa93 중복코드 제거 2025-04-10 10:47:00 +09:00
97f0749f5c Merge pull request '견적서 오픈플래그 텍스트' (#19) from feature/qcast-886 into dev
Reviewed-on: #19
2025-04-10 10:46:57 +09:00
김민식
74b3c6dac7 처마력 커버 설치여부 , 적설방지금구설치 여부 값 추가 2025-04-10 10:46:01 +09:00
82210a99c9 견적서 오픈플래그 텍스트 2025-04-10 10:45:36 +09:00
76693640cf Merge branch 'dev' into feature/yj-layoutSetup 2025-04-09 17:39:13 +09:00
546133c27e 오류 문구 수정 2025-04-09 17:29:37 +09:00
8662a37177 # 974 - 지붕형상 설정 전 팝업 제거 2025-04-09 17:22:55 +09:00
defebb21d7 chore: 내부 개발서버용 실행 스크립트 추가 2025-04-09 17:20:05 +09:00
11cf3b8403 chore: 내부 개발 서버 기동을 위한 실행 파일 추가
- 추후에 제거 예정
2025-04-09 17:18:58 +09:00
c869e9d5a3 육지붕 수정 2025-04-09 16:56:43 +09:00
김민식
e71c655d05 Merge branch 'feature/yj-layoutSetup' of https://git.hanasys.jp/qcast3/qcast-front into feature/yj-layoutSetup 2025-04-09 14:36:18 +09:00
김민식
29e8ca25a5 BasicSetting 하단 버튼 리펙터링 2025-04-09 14:35:59 +09:00
0673567900 Merge branch 'feature/yj-layoutSetup' of https://git.hanasys.jp/qcast3/qcast-front into feature/yj-layoutSetup 2025-04-09 14:13:58 +09:00
c42e244f24 좌표가 음수일때 보정 로직 추가 2025-04-09 14:13:55 +09:00
김민식
e940884312 육지붕일때 moduleSelectionData 에 margin 추가 2025-04-09 13:34:34 +09:00
115544edb4 번역추가 2025-04-09 11:04:10 +09:00
김민식
9e9bd8f9c4 withDraggable spinner 추가 2025-04-09 10:39:57 +09:00
2da7ea0c78 Merge branch 'dev' into feature/yj-layoutSetup 2025-04-08 17:43:46 +09:00
00a201704b trestleDetail 작업 2025-04-08 17:43:19 +09:00
62517870b7 Merge pull request 'QCast Front #886 견적서 OPEN 제품 단가필드에 OPEN 텍스트 추가' (#18) from feature/qcast-886 into dev
Reviewed-on: #18
2025-04-08 16:33:41 +09:00
7ff52a7b19 QCast Front #886 견적서 OPEN 제품 단가필드에 OPEN 텍스트 추가 2025-04-08 16:33:06 +09:00
김민식
597aaa6d42 orientation module validation 추가 2025-04-08 16:14:57 +09:00
김민식
468980abbc roofConstruction 첫번째 trestleDetail null로 들어가는 버그 수정 2025-04-08 15:54:31 +09:00
김민식
91ccfcb20e DB에서 불러온 값 roof에 적용 2025-04-08 15:16:44 +09:00
김민식
c35f3c1adb moduleSelectionData trestleDetail 없는 버그 수정 2025-04-08 14:19:03 +09:00
김민식
468c7fda6f moduleSelectionData 수정 2025-04-08 13:03:23 +09:00
김민식
9976ff59d6 Merge remote-tracking branch 'origin/qcast-pub' into feature/yj-layoutSetup 2025-04-07 18:22:01 +09:00
김민식
f548506179 moduleSelectionData 수정 2025-04-07 18:21:31 +09:00
d6d626f2d7 양단 케이블 파라미터 추가 2025-04-07 14:35:23 +09:00
Jaeyoung Lee
46f46d734a 지붕덮개 작성 관련 작업중 (마루지붕 1차 정리) 2025-04-07 10:05:55 +09:00
c0cbcd18a2 Merge branch 'dev' into feature/yj-layoutSetup 2025-04-04 16:58:07 +09:00
2e86c793c3 Merge pull request '955-견적서 2차 SAP 판매점 노출' (#17) from feature/ysCha into dev
Reviewed-on: #17
2025-04-04 16:39:05 +09:00
d38acba7c9 955-견적서 2차 SAP 판매점 노출 2025-04-04 16:34:09 +09:00
김민식
f3a0504d0b Merge branch 'feature/yj-layoutSetup' of https://git.hanasys.jp/qcast3/qcast-front into qcast-pub 2025-04-04 13:21:11 +09:00
b8f05a6ff0 fix: promise.all 형태로 변경 2025-04-04 13:15:21 +09:00
0a493884c6 Merge branch 'feature/yj-layoutSetup' of https://git.hanasys.jp/qcast3/qcast-front into feature/yj-layoutSetup 2025-04-04 12:48:58 +09:00
김민식
a316664b20 addRoof, index 데이터 추가 2025-04-04 12:47:50 +09:00
804911af3e 모듈 혼합 수 동적 대응 2025-04-04 12:38:49 +09:00
김민식
59f9377cc9 trestleDetail 호출시 trestle 데이터 추가 2025-04-04 12:28:42 +09:00
김민식
15cb2cf270 roofIndex 추가 2025-04-04 11:32:32 +09:00
김민식
1cad8eaf76 Merge remote-tracking branch 'origin/qcast-pub' into feature/yj-layoutSetup 2025-04-04 10:58:38 +09:00
김민식
9400ee7707 trestle 데이터 버그 수정 2025-04-04 10:58:02 +09:00
22f8cd3fa7 모듈 선택 초기화 수정 2025-04-04 10:18:52 +09:00
e872df680d 모듈 설치면 초기화 기능 2025-04-03 17:09:17 +09:00
김민식
02a0e4a67f Merge remote-tracking branch 'origin/qcast-pub' into feature/yj-layoutSetup 2025-04-03 16:32:13 +09:00
김민식
d524b33f56 모듈 관련 데이터 수정시 roofs 데이터 초기화 버그 수정 2025-04-03 16:31:12 +09:00
e7b50ca642 Merge branch 'feature/yj-layoutSetup' of https://git.hanasys.jp/qcast3/qcast-front into feature/module-grouping
# Conflicts:
#	src/locales/ja.json
2025-04-03 16:30:36 +09:00
3f40725647 3중 멀티모듈 대응 2025-04-03 16:28:58 +09:00
aa2685e558 레이아웃설치 수정 2025-04-03 14:33:54 +09:00
f32e772141 모듈 표 추가 2025-04-03 13:36:46 +09:00
7d76929689 고도화 작업 레이아웃 모듈 설치 기능 2025-04-03 11:23:37 +09:00
김민식
6c6e5845ef 적설량, 풍속 등 수정했을때 설정한 roof 초기화 2025-04-03 10:22:48 +09:00
김민식
001143954e QSelectbox value 없을때 title 부분 수정 2025-04-03 10:22:14 +09:00
265593f6dd 모듈 그룹화 작업중 2025-04-02 17:36:25 +09:00
김민식
ce7c90eb6f Merge branch 'feature/yj-layoutSetup' of https://git.hanasys.jp/qcast3/qcast-front into feature/yj-layoutSetup
# Conflicts:
#	src/components/floor-plan/modal/basic/BasicSetting.jsx
#	src/components/floor-plan/modal/basic/step/Trestle.jsx
2025-04-02 17:04:29 +09:00
2e60e848c8 basicsetting 수정 2025-04-02 17:01:37 +09:00
김민식
51aaf342fa Merge remote-tracking branch 'origin/qcast-pub' into feature/yj-layoutSetup
# Conflicts:
#	src/components/floor-plan/modal/basic/BasicSetting.jsx
2025-04-02 16:57:48 +09:00
김민식
82632b962e Trestle 데이터 추가 2025-04-02 16:55:23 +09:00
김민식
bfc7b3fe32 Revert "Trestle 데이터 추가 수정"
This reverts commit 648c2e208a89c0080e49d9c570ac2b3700374b2a.
2025-04-02 16:54:21 +09:00
김민식
648c2e208a Trestle 데이터 추가 수정 2025-04-02 16:54:04 +09:00
d386c42c08 Merge branch 'qcast-pub' into dev 2025-04-02 16:39:19 +09:00
ee6f62b4ba chore: nextjs hotfix 로 버전 수정 2025-04-02 16:32:19 +09:00
de8ba00e55 chore: Update startscript to run development server on port 5000 2025-04-02 16:17:52 +09:00
김민식
ebbf010e6e Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev
# Conflicts:
#	README.md
2025-04-02 16:08:42 +09:00
b7cbc3ec47 test: deploy test 2025-04-02 16:00:55 +09:00
8c33611440 Merge branch 'dev' into feature/yj-layoutSetup
# Conflicts:
#	src/components/floor-plan/modal/basic/BasicSetting.jsx
2025-04-02 15:21:15 +09:00
김민식
d8e43b0d81 BasicSetting 버그 수정 2025-04-02 15:19:00 +09:00
cb87d6cd23 Merge branch 'dev' into feature/yj-layoutSetup 2025-04-02 14:51:54 +09:00
김민식
8ba79d6a06 Merge remote-tracking branch 'origin/qcast-pub' into dev 2025-04-02 14:50:45 +09:00
김민식
ba06fde9cc useModuleTrestle 추가 2025-04-02 14:50:20 +09:00
1588b8cda7 Merge branch 'dev' into feature/yj-layoutSetup 2025-04-02 14:48:26 +09:00
김민식
9c1df779e4 Merge remote-tracking branch 'origin/qcast-pub' into dev 2025-04-02 14:47:53 +09:00
김민식
2dbbd3a957 roofAtom 추가 2025-04-02 14:47:21 +09:00
김민식
d165267ec1 🚨chore: Sync Sass 2025-04-02 14:47:09 +09:00
da8e0856cb Merge branch 'dev' into feature/yj-layoutSetup
# Conflicts:
#	src/components/floor-plan/modal/basic/BasicSetting.jsx
#	src/locales/ja.json
2025-04-02 14:46:08 +09:00
김민식
78ce43969a Merge remote-tracking branch 'origin/qcast-pub' into dev 2025-04-02 14:23:23 +09:00
김민식
fe10ecf476 PlacementShapeSetting merge 2025-04-02 14:22:15 +09:00
김민식
b16174ec0d Merge remote-tracking branch 'origin/qcast-pub' into dev
# Conflicts:
#	src/components/floor-plan/modal/placementShape/PlacementShapeSetting.jsx
#	src/locales/ja.json
2025-04-02 14:08:21 +09:00
김민식
964d9bdcc7 basicsetting 로직 수정 2025-04-02 13:56:03 +09:00
김민식
4b6f0b1b06 plan tab 좌측 objectNo 영역 추가 2025-04-02 13:54:44 +09:00
김민식
9df16cad02 모듈 열/단 버퍼 추가 2025-04-02 13:54:06 +09:00
김민식
8354508a2b 다국어 추가 2025-04-02 13:53:34 +09:00
김민식
ced0eb9fc6 🚨chore: Sync Sass 2025-04-02 13:53:24 +09:00
655fef744c 모듈 선택 표시 2025-04-02 11:12:23 +09:00
0a5b6ce132 test: deploy test 2025-04-01 17:59:57 +09:00
d6b9634a89 레이아웃 설치 validate 추가 2025-04-01 17:18:38 +09:00
66f1293b95 Merge pull request '955-견적서 상품검색할때 이미지명(형명)도 같이 표시' (#15) from feature/ysCha into dev
Reviewed-on: #15
2025-04-01 17:14:43 +09:00
167740f33d 955-견적서 상품검색할때 이미지명(형명)도 같이 표시 2025-04-01 17:12:02 +09:00
87ef010ae6 레이아웃 문구 추가 2025-04-01 15:40:20 +09:00
7d37deb048 모듈 설치 페이지 변경 2025-04-01 15:31:12 +09:00
97ca91aa29 Merge branch 'dev' into feature/yj-layoutSetup
# Conflicts:
#	src/hooks/module/useModuleBasicSetting.js
2025-04-01 13:26:08 +09:00
be54df76f7 통합테스트(Integration Test) #956 이동 기능 수정 2025-04-01 13:19:12 +09:00
c2fbc83485 Merge branch 'dev' into feature/dev-yj 2025-04-01 13:07:31 +09:00
09e985e9b5 Merge branch 'dev' into feature/yj-layoutSetup
# Conflicts:
#	src/locales/ja.json
#	src/locales/ko.json
2025-03-31 14:57:32 +09:00
d10642d6d5 메뉴에서 안쓰는 내용 제거 2025-03-31 13:50:06 +09:00
826739fb0e 혼합 가능 여부 확인 validate 추가 2025-03-31 10:44:56 +09:00
482b6b5477 수동 설치 혼합일 경우 설치 여부 확인 2025-03-31 10:20:45 +09:00
3509e5fbb6 Update startscript to run development server on port 5000 2025-03-28 17:13:46 +09:00
a5fb44ad36 Merge branch 'dev' into feature/dev-yj 2025-03-28 15:25:20 +09:00
c88a07a227 모듈 남쪽설치 치조시 좌측으로 붙는 오류 수정 2025-03-28 15:25:03 +09:00
5982a2aa10 Merge pull request '그리드 테이블 경계표시 추가' (#14) from feature/ysCha into dev
Reviewed-on: #14
2025-03-28 15:15:40 +09:00
3fd81e771d 그리드 테이블 경계표시 추가 2025-03-28 15:13:05 +09:00
c9a53b7159 Merge pull request '#944 물건번호 생성시 시작번호 R->S 변경에따른 주석정리' (#13) from feature/qcast-944 into dev
Reviewed-on: #13
2025-03-28 13:29:13 +09:00
24ba8bfb14 #944 물건번호 생성시 시작번호 R->S 변경에따른 주석정리 2025-03-28 13:27:23 +09:00
b82901271d Merge pull request 'HANASYS 개선건 #950' (#12) from feature/qcast-950 into dev
Reviewed-on: #12
2025-03-28 12:38:58 +09:00
25957a37e1 HANASYS 개선건 #950 2025-03-28 12:37:54 +09:00
83f0eef9eb Merge pull request 'HANASYS 개선건 #947' (#11) from feature/qcast-947 into dev
Reviewed-on: #11
2025-03-28 12:29:13 +09:00
46f90aff1f HANASYS 개선건 #947 2025-03-28 12:27:59 +09:00
11f69a01bb 번역 수정 2025-03-28 11:15:12 +09:00
c8f70e0746 번역 수정 2025-03-28 10:58:34 +09:00
2bb1b71a0b 레이아웃 설치 작업 2025-03-28 09:40:19 +09:00
de5901492b 기존 저장된 지붕재 index 수정 2025-03-27 15:17:42 +09:00
6f5b70342d - pcs 모델 추가 버그 수정 2025-03-27 14:41:29 +09:00
b88299b78d Merge branch 'dev' into feature/yj-layoutSetup 2025-03-27 14:11:40 +09:00
63d8ae092c 레이아웃 모듈 설치 validate 추가 2025-03-27 14:11:23 +09:00
7b1c9b681e Merge pull request '견적서::주문분류에서 이미등록된 YJSS 노출' (#10) from feature/ysCha into dev
Reviewed-on: #10
2025-03-27 13:41:25 +09:00
25e8dcc050 견적서::주문분류에서 이미등록된 YJSS 노출 2025-03-27 13:38:15 +09:00
3027f47d5d chore: nextjs 버전 fix & pm2 실행 스크립트 수정 2025-03-27 13:33:29 +09:00
1f723c9ce7 fix: 임포트 구문 추가 2025-03-27 11:05:30 +09:00
5e9c22a928 Merge pull request '📌 feat: Implement usePlan hook for managing floor plan state and interactions, including canvas data handling, plan creation, deletion, and context management.' (#9) from feature/plan-add-dont-dbclick into dev
Reviewed-on: #9
2025-03-27 10:37:14 +09:00
54d06f7d51 가로, 세로 선 없을 경우 return 2025-03-27 10:34:40 +09:00
7f402d5b45 수동 설정 시 text 밖으로 수정 2025-03-27 10:34:15 +09:00
2473cfac17 📌 feat: Implement usePlan hook for managing floor plan state and interactions, including canvas data handling, plan creation, deletion, and context management. 2025-03-27 10:33:34 +09:00
e293d5dfde Merge pull request 'feature/ysCha' (#8) from feature/ysCha into dev
Reviewed-on: #8
2025-03-27 09:58:44 +09:00
cf9acde872 Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into feature/ysCha
# Conflicts:
#	src/locales/ja.json
2025-03-26 17:00:11 +09:00
93b645e9e8 지붕면 할당 전 제거 시 오류 수정 2025-03-26 15:08:37 +09:00
25778a099f 팝업 닫고 시작 2025-03-26 11:10:42 +09:00
ba9a49501c 지붕면 할당 => 마지막으로 추가된 지붕재 자동 선택 2025-03-26 10:59:29 +09:00
a2d192084b usePlan 내부 currentObject 초기화 제거 2025-03-26 10:09:40 +09:00
7fcaaece53 Merge branch 'dev' into feature/yj-layoutSetup 2025-03-25 16:08:16 +09:00
cdaeab1d42 Merge branch 'dev' into feature/dev-yj 2025-03-25 16:07:54 +09:00
47e3ae7d29 배치면 타입 상하 반전 추가 2025-03-25 16:07:15 +09:00
yjnoh
5597f8ad70 지붕라인 색칠하기 2025-03-25 15:48:18 +09:00
65ec3d5153 지붕면 할당 시 오류 수정 2025-03-25 15:12:01 +09:00
yjnoh
a7e9aba26c Merge branch 'dev' into feature/yj-layoutSetup
# Conflicts:
#	src/locales/ja.json
#	src/locales/ko.json
2025-03-25 15:09:40 +09:00
yjnoh
592275c0de 배치면 도형 재배치 및 처마,용마루,케라바 타입 변경 로직 변경 2025-03-25 14:49:07 +09:00
c7de33b8b9 fix: popup spinner 동작 추가 2025-03-25 14:20:28 +09:00
8d645d08dc refactor: API Router 코드 일부 리팩토링
- 패턴에 어긋나는 응답 포맷 수정
- 주석 작성
2025-03-25 13:33:36 +09:00
5e27ab282c Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into dev 2025-03-25 11:11:15 +09:00
75549b66b5 지붕재 변경 시 canvas 저장 추가 2025-03-25 11:10:56 +09:00
yjnoh
437d552b3a 배치면 방향 수정 2025-03-25 11:05:54 +09:00
553a259c1f chore: 도면 이미지 크롭을 위한 sharp lib 추가 2025-03-25 11:02:08 +09:00
97389f7141 feat: 캔버스 이미지 S3로 저장 기능 추가 2025-03-25 11:01:40 +09:00
11edbe1988 feat: cad파일 컨버터 추가 2025-03-24 19:01:09 +09:00
9a9fa522f9 feat: 구글 맵 컨버트 기능 수정 2025-03-24 17:54:32 +09:00
2492b45a66 feat: 구글 맵 컨버트 기능 추가 2025-03-24 17:08:31 +09:00
yjnoh
c631c6344e 레이아웃 설치 작업중 2025-03-24 17:07:19 +09:00
85d9aca6d3 병렬수 0 이상인 경우에만 추가 2025-03-24 14:50:06 +09:00
yjnoh
0ec917b09f 레이어 팝업 수정 2025-03-24 14:07:14 +09:00
a7b9062154 feat: s3 업로드 기본 기능 추가 2025-03-24 13:58:29 +09:00
Jaeyoung Lee
25240bc3c2 동이동, 형이동 기능 추가. 이에 따른 보조선 작성 수정 중 2025-03-24 13:38:46 +09:00
cha
82e3527432 견적서::주문분류에서 storePriceList의 pkgRank의 값이 없으면 YJSS 선택불가, 기등록은 변경 가능 2025-03-24 13:27:30 +09:00
yjnoh
7ec9854173 Merge branch 'dev' into feature/dev-yj 2025-03-24 11:26:42 +09:00
yjnoh
b458b4e853 캔버스 확시대 오브젝트 클릭 수정 2025-03-24 11:26:26 +09:00
1ba9853a32 #935 전반/글자표기 2025-03-24 11:06:35 +09:00
f1d976521d # 932 - pcs 한국어 기재 수정 2025-03-24 10:55:53 +09:00
yjnoh
dc8f033e9f validation 수정 2025-03-24 10:49:56 +09:00
yjnoh
943fd16e4b 클릭시 배치면 데이터 validation 추가 2025-03-24 10:23:35 +09:00
yjnoh
70c706341e Merge branch 'dev' into feature/yj-layoutSetup 2025-03-24 09:35:53 +09:00
yjnoh
c704207d2f 레이아웃설치 validation 2025-03-24 09:35:21 +09:00
a7ddfacdd2 Merge pull request '견적서 복사 미사용 파라미터 정리' (#7) from feature/qcast-925 into dev
Reviewed-on: #7
2025-03-24 09:28:27 +09:00
b51dacf421 견적서 복사 미사용 파라미터 정리 2025-03-24 09:27:28 +09:00
f25dac0ae3 Merge pull request '견적특이사항 상품 pkgYn 대응' (#6) from feature/qcast-925 into dev
Reviewed-on: #6
2025-03-21 17:48:24 +09:00
c99deaf93f 견적특이사항 상품 pkgYn 대응 2025-03-21 17:47:41 +09:00
20cef812ae chore: 브라우저용 환경변수 분리 2025-03-21 16:47:14 +09:00
d8341385a4 chore: 환경변수 추가 2025-03-21 16:44:01 +09:00
8d65765daf Merge pull request '견적특이사항 공통코드 PROD 사용유무 변경에 따른 대응..' (#5) from feature/qcast-925 into dev
Reviewed-on: #5
2025-03-21 16:04:37 +09:00
2f8ca712c9 견적특이사항 공통코드 PROD 사용유무 변경에 따른 대응.. 2025-03-21 16:03:48 +09:00
yjnoh
9285caf422 Merge branch 'dev' into feature/yj-layoutSetup
# Conflicts:
#	src/locales/ja.json
2025-03-21 15:55:58 +09:00
f2470b346c feat: 핫키 이벤트 등록시 구문 수정 2025-03-21 14:23:46 +09:00
1a5f78a970 Merge pull request '견적서 복사 실패 메세지' (#4) from feature/qcast-925 into dev
Reviewed-on: #4
2025-03-21 13:27:21 +09:00
5979555bcb 견적서 복사 실패 메세지 2025-03-21 13:26:14 +09:00
26047df3c8 feat: 핫키 이벤트 등록 샘플 추가 2025-03-21 11:28:16 +09:00
yjnoh
12936ec1f9 Merge branch 'dev' into feature/dev-yj 2025-03-21 11:24:08 +09:00
yjnoh
1bb92a975e 마우스 우클릭 이벤트 조정 2025-03-21 11:23:58 +09:00
f2a083f022 지붕재 할당 수정 추가 2025-03-21 10:23:46 +09:00
786c35e656 intersection 검색 범위 수정 2025-03-21 09:57:51 +09:00
yjnoh
88bcf27bfb 수정 2025-03-20 18:09:40 +09:00
yjnoh
617afb8b1f Merge branch 'dev' into feature/dev-yj-surface 2025-03-20 18:00:30 +09:00
yjnoh
414d6fa0c5 처마 용마루 방향에 따른 타입 작업 2025-03-20 17:59:58 +09:00
6919dac8f1 getCurrentPoints로 수정 2025-03-20 16:48:35 +09:00
yjnoh
79d873c135 진짜 처마면인지 확인하는 로직 추가 2025-03-20 16:00:02 +09:00
yjnoh
0e8ce8b2e2 Merge branch 'dev' into feature/dev-yj-surface 2025-03-20 15:30:47 +09:00
yjnoh
c4d17d2147 배치면 라인 타입 팝업 삭제 및 로직 수정 2025-03-20 15:30:03 +09:00
e75db5ace1 Merge remote-tracking branch 'origin/dev' into dev 2025-03-20 14:18:26 +09:00
8848713c72 Merge pull request '견적서 일본어 변경 누락분 재반영' (#3) from feature/qcast-925 into dev
Reviewed-on: #3
2025-03-20 14:17:30 +09:00
39d48e61f3 test: push test 2025-03-20 13:48:07 +09:00
e624aa0de0 메시지 내용 수정 2025-03-20 13:42:07 +09:00
e7410e5373 견적서 일본어 변경 누락분 재반영 2025-03-20 13:35:06 +09:00
05dd069e53 modified 시 lines 위치 새로 수정 2025-03-20 10:02:09 +09:00
yjnoh
ad14bf091f Merge branch 'dev' into dev-yj-layoutSetup 2025-03-19 17:49:28 +09:00
yjnoh
c34a7fc54d 수동시 치조 여부에 따라 모듈 가운데 흡착 2025-03-19 17:49:17 +09:00
ac015123cd Merge pull request '#925 견적서 상세 가격표시 옵션 & 프라이싱 호출 파라미터 변경' (#2) from feature/qcast-925 into dev
Reviewed-on: #2
2025-03-19 17:40:55 +09:00
f7fe0f6528 #925 견적서 상세 가격표시 옵션 & 프라이싱 호출 파라미터 변경 2025-03-19 17:40:13 +09:00
6b76108d8b Merge pull request '자동로그인 id 입력 수정 #913' (#1) from feature/qcast-913 into dev
Reviewed-on: #1
2025-03-19 16:23:45 +09:00
BOOK-BKT8UBVE0A\dhfkd
590040fa1d 자동로그인 id 입력 수정 #913 2025-03-19 16:23:19 +09:00
yjnoh
9bb72bfa3a 그림자 제외 처리 2025-03-19 13:38:35 +09:00
yjnoh
8ea6f43ddb Merge branch 'dev' into dev-yj-layoutSetup
# Conflicts:
#	src/hooks/module/useModuleBasicSetting.js
#	src/locales/ja.json
#	src/locales/ko.json
2025-03-19 13:38:16 +09:00
yjnoh
325c2c1cc0 번역수정 2025-03-19 13:28:20 +09:00
yjnoh
521bfd4303 반각만 입력가능 문구 추가 2025-03-19 11:10:12 +09:00
cha
b136bc213c Merge branch 'dev' of https://git.jetbrains.space/nalpari/q-cast-iii/qcast-front into feature/ysCha 2025-03-19 10:00:40 +09:00
b6e70f6eb0 innerLines 초기 셋팅 추가 2025-03-19 09:52:35 +09:00
Jaeyoung Lee
3b41952070 지붕 덮개 계산 오류 수정 2025-03-18 16:23:38 +09:00
84e8af50b8 QSelectbox key 중복 제거 2025-03-18 15:55:11 +09:00
yjnoh
8a5bd9f505 단수배치 작업 2025-03-18 15:44:41 +09:00
Jaeyoung Lee
509c307e56 Merge branch 'dev' into feature/jaeyoung 2025-03-18 14:04:48 +09:00
11438773a1 견적서 상세 헤더메뉴 문서다운로드 버튼 명 수정 2025-03-18 13:28:58 +09:00
2e762537fc #918 견적서 상세화면 견적일 -> 견적작성일 메세지 변경 2025-03-18 13:24:58 +09:00
57446fa6d8 주택 원복 2025-03-18 12:54:36 +09:00
1fa02de62f 지붕면 할당 순서 수정 2025-03-18 11:33:45 +09:00
06fa1766d6 파라미터 없는 경우 같은 포인트 검사 못함 2025-03-18 10:56:37 +09:00
63297328ed
Merge Q-CAST-III-MR-783: YJSS 금액 2025-03-18 01:30:08 +00:00
7c15da2b4c YJSS 금액 2025-03-18 10:29:26 +09:00
Jaeyoung Lee
f4e7896bf8 Merge branch 'dev' into feature/jaeyoung 2025-03-18 10:24:54 +09:00
3432d64a3c 8각 안나눠지는 현상 작업 추가 2025-03-18 10:23:42 +09:00
9be21fc2b2 번역 수정 #914 2025-03-18 09:52:14 +09:00
Jaeyoung Lee
2537240bb0 중복된 마루 제거 2025-03-18 09:36:48 +09:00
yjnoh
c467fc7fa8 흡착점 작업 2025-03-18 09:36:11 +09:00
Jaeyoung Lee
10c2668a67 마루 형상 버그 수정 및 innerLines 수정 2025-03-17 19:05:56 +09:00
yjnoh
63db694efa 단수지정배치 작업중 2025-03-17 18:36:39 +09:00
yoosangwook
f915dab239 chore: ecosystem 추가 2025-03-17 18:25:28 +09:00
Jaeyoung Lee
9f76fa97c5 Merge branch 'dev' into feature/jaeyoung
# Conflicts:
#	src/util/qpolygon-utils.js
2025-03-17 17:39:17 +09:00
Jaeyoung Lee
99a584f8cf 지붕덮개 그리기 로직 수정 중 2025-03-17 17:35:43 +09:00
김민식
16423de079 배치면 초기설정 다국어 적용 2025-03-17 15:44:12 +09:00
김민식
e99a1a3854 배치면 초기설정 화면 다국어 추가 2025-03-17 15:43:57 +09:00
김민식
8ca01757c9 🚨chore: Sync Sass 2025-03-17 15:43:37 +09:00
yjnoh
15ff2989f4 Merge branch 'dev' into dev-yj-layoutSetup 2025-03-17 15:03:27 +09:00
yjnoh
83b27582f7 레이아웃 작업 2025-03-17 15:01:25 +09:00
yjnoh
9e66b3ef7d 레이아웃 설치 동서남북 작업 2025-03-17 11:33:44 +09:00
yjnoh
ae2171c633 레이아웃 설치 2025-03-14 18:35:13 +09:00
yjnoh
f9766a7d21 Merge branch 'dev' into dev-yj-layoutSetup
# Conflicts:
#	src/components/floor-plan/modal/basic/BasicSetting.jsx
#	src/hooks/module/useModuleBasicSetting.js
2025-03-14 18:22:24 +09:00
yjnoh
83038a7c81 Merge branch 'dev' into dev-yj-layoutSetup 2025-03-12 17:27:25 +09:00
yjnoh
3df466a717 레이아웃 설치 데모 작업 2025-03-12 16:44:41 +09:00
Jaeyoung Lee
b3af3cd0e0 외벽선 편집, 오프셋 기능 적용 및 정리 2025-02-25 13:46:10 +09:00
416 changed files with 58914 additions and 10365 deletions

View File

@ -0,0 +1,153 @@
---
name: next-best-practices
description: Next.js best practices - file conventions, RSC boundaries, data patterns, async APIs, metadata, error handling, route handlers, image/font optimization, bundling
user-invocable: false
---
# Next.js Best Practices
Apply these rules when writing or reviewing Next.js code.
## File Conventions
See [file-conventions.md](./file-conventions.md) for:
- Project structure and special files
- Route segments (dynamic, catch-all, groups)
- Parallel and intercepting routes
- Middleware rename in v16 (middleware → proxy)
## RSC Boundaries
Detect invalid React Server Component patterns.
See [rsc-boundaries.md](./rsc-boundaries.md) for:
- Async client component detection (invalid)
- Non-serializable props detection
- Server Action exceptions
## Async Patterns
Next.js 15+ async API changes.
See [async-patterns.md](./async-patterns.md) for:
- Async `params` and `searchParams`
- Async `cookies()` and `headers()`
- Migration codemod
## Runtime Selection
See [runtime-selection.md](./runtime-selection.md) for:
- Default to Node.js runtime
- When Edge runtime is appropriate
## Directives
See [directives.md](./directives.md) for:
- `'use client'`, `'use server'` (React)
- `'use cache'` (Next.js)
## Functions
See [functions.md](./functions.md) for:
- Navigation hooks: `useRouter`, `usePathname`, `useSearchParams`, `useParams`
- Server functions: `cookies`, `headers`, `draftMode`, `after`
- Generate functions: `generateStaticParams`, `generateMetadata`
## Error Handling
See [error-handling.md](./error-handling.md) for:
- `error.tsx`, `global-error.tsx`, `not-found.tsx`
- `redirect`, `permanentRedirect`, `notFound`
- `forbidden`, `unauthorized` (auth errors)
- `unstable_rethrow` for catch blocks
## Data Patterns
See [data-patterns.md](./data-patterns.md) for:
- Server Components vs Server Actions vs Route Handlers
- Avoiding data waterfalls (`Promise.all`, Suspense, preload)
- Client component data fetching
## Route Handlers
See [route-handlers.md](./route-handlers.md) for:
- `route.ts` basics
- GET handler conflicts with `page.tsx`
- Environment behavior (no React DOM)
- When to use vs Server Actions
## Metadata & OG Images
See [metadata.md](./metadata.md) for:
- Static and dynamic metadata
- `generateMetadata` function
- OG image generation with `next/og`
- File-based metadata conventions
## Image Optimization
See [image.md](./image.md) for:
- Always use `next/image` over `<img>`
- Remote images configuration
- Responsive `sizes` attribute
- Blur placeholders
- Priority loading for LCP
## Font Optimization
See [font.md](./font.md) for:
- `next/font` setup
- Google Fonts, local fonts
- Tailwind CSS integration
- Preloading subsets
## Bundling
See [bundling.md](./bundling.md) for:
- Server-incompatible packages
- CSS imports (not link tags)
- Polyfills (already included)
- ESM/CommonJS issues
- Bundle analysis
## Scripts
See [scripts.md](./scripts.md) for:
- `next/script` vs native script tags
- Inline scripts need `id`
- Loading strategies
- Google Analytics with `@next/third-parties`
## Hydration Errors
See [hydration-error.md](./hydration-error.md) for:
- Common causes (browser APIs, dates, invalid HTML)
- Debugging with error overlay
- Fixes for each cause
## Suspense Boundaries
See [suspense-boundaries.md](./suspense-boundaries.md) for:
- CSR bailout with `useSearchParams` and `usePathname`
- Which hooks require Suspense boundaries
## Parallel & Intercepting Routes
See [parallel-routes.md](./parallel-routes.md) for:
- Modal patterns with `@slot` and `(.)` interceptors
- `default.tsx` for fallbacks
- Closing modals correctly with `router.back()`
## Self-Hosting
See [self-hosting.md](./self-hosting.md) for:
- `output: 'standalone'` for Docker
- Cache handlers for multi-instance ISR
- What works vs needs extra setup
## Debug Tricks
See [debug-tricks.md](./debug-tricks.md) for:
- MCP endpoint for AI-assisted debugging
- Rebuild specific routes with `--debug-build-paths`

View File

@ -0,0 +1,87 @@
# Async Patterns
In Next.js 15+, `params`, `searchParams`, `cookies()`, and `headers()` are asynchronous.
## Async Params and SearchParams
Always type them as `Promise<...>` and await them.
### Pages and Layouts
```tsx
type Props = { params: Promise<{ slug: string }> }
export default async function Page({ params }: Props) {
const { slug } = await params
}
```
### Route Handlers
```tsx
export async function GET(
request: Request,
{ params }: { params: Promise<{ id: string }> }
) {
const { id } = await params
}
```
### SearchParams
```tsx
type Props = {
params: Promise<{ slug: string }>
searchParams: Promise<{ query?: string }>
}
export default async function Page({ params, searchParams }: Props) {
const { slug } = await params
const { query } = await searchParams
}
```
### Synchronous Components
Use `React.use()` for non-async components:
```tsx
import { use } from 'react'
type Props = { params: Promise<{ slug: string }> }
export default function Page({ params }: Props) {
const { slug } = use(params)
}
```
### generateMetadata
```tsx
type Props = { params: Promise<{ slug: string }> }
export async function generateMetadata({ params }: Props): Promise<Metadata> {
const { slug } = await params
return { title: slug }
}
```
## Async Cookies and Headers
```tsx
import { cookies, headers } from 'next/headers'
export default async function Page() {
const cookieStore = await cookies()
const headersList = await headers()
const theme = cookieStore.get('theme')
const userAgent = headersList.get('user-agent')
}
```
## Migration Codemod
```bash
npx @next/codemod@latest next-async-request-api .
```

View File

@ -0,0 +1,180 @@
# Bundling
Fix common bundling issues with third-party packages.
## Server-Incompatible Packages
Some packages use browser APIs (`window`, `document`, `localStorage`) and fail in Server Components.
### Error Signs
```
ReferenceError: window is not defined
ReferenceError: document is not defined
ReferenceError: localStorage is not defined
Module not found: Can't resolve 'fs'
```
### Solution 1: Mark as Client-Only
If the package is only needed on client:
```tsx
// Bad: Fails - package uses window
import SomeChart from 'some-chart-library'
export default function Page() {
return <SomeChart />
}
// Good: Use dynamic import with ssr: false
import dynamic from 'next/dynamic'
const SomeChart = dynamic(() => import('some-chart-library'), {
ssr: false,
})
export default function Page() {
return <SomeChart />
}
```
### Solution 2: Externalize from Server Bundle
For packages that should run on server but have bundling issues:
```js
// next.config.js
module.exports = {
serverExternalPackages: ['problematic-package'],
}
```
Use this for:
- Packages with native bindings (sharp, bcrypt)
- Packages that don't bundle well (some ORMs)
- Packages with circular dependencies
### Solution 3: Client Component Wrapper
Wrap the entire usage in a client component:
```tsx
// components/ChartWrapper.tsx
'use client'
import { Chart } from 'chart-library'
export function ChartWrapper(props) {
return <Chart {...props} />
}
// app/page.tsx (server component)
import { ChartWrapper } from '@/components/ChartWrapper'
export default function Page() {
return <ChartWrapper data={data} />
}
```
## CSS Imports
Import CSS files instead of using `<link>` tags. Next.js handles bundling and optimization.
```tsx
// Bad: Manual link tag
<link rel="stylesheet" href="/styles.css" />
// Good: Import CSS
import './styles.css'
// Good: CSS Modules
import styles from './Button.module.css'
```
## Polyfills
Next.js includes common polyfills automatically. Don't load redundant ones from polyfill.io or similar CDNs.
Already included: `Array.from`, `Object.assign`, `Promise`, `fetch`, `Map`, `Set`, `Symbol`, `URLSearchParams`, and 50+ others.
```tsx
// Bad: Redundant polyfills
<script src="https://polyfill.io/v3/polyfill.min.js?features=fetch,Promise,Array.from" />
// Good: Next.js includes these automatically
```
## ESM/CommonJS Issues
### Error Signs
```
SyntaxError: Cannot use import statement outside a module
Error: require() of ES Module
Module not found: ESM packages need to be imported
```
### Solution: Transpile Package
```js
// next.config.js
module.exports = {
transpilePackages: ['some-esm-package', 'another-package'],
}
```
## Common Problematic Packages
| Package | Issue | Solution |
|---------|-------|----------|
| `sharp` | Native bindings | `serverExternalPackages: ['sharp']` |
| `bcrypt` | Native bindings | `serverExternalPackages: ['bcrypt']` or use `bcryptjs` |
| `canvas` | Native bindings | `serverExternalPackages: ['canvas']` |
| `recharts` | Uses window | `dynamic(() => import('recharts'), { ssr: false })` |
| `react-quill` | Uses document | `dynamic(() => import('react-quill'), { ssr: false })` |
| `mapbox-gl` | Uses window | `dynamic(() => import('mapbox-gl'), { ssr: false })` |
| `monaco-editor` | Uses window | `dynamic(() => import('@monaco-editor/react'), { ssr: false })` |
| `lottie-web` | Uses document | `dynamic(() => import('lottie-react'), { ssr: false })` |
## Bundle Analysis
Analyze bundle size with the built-in analyzer (Next.js 16.1+):
```bash
next experimental-analyze
```
This opens an interactive UI to:
- Filter by route, environment (client/server), and type
- Inspect module sizes and import chains
- View treemap visualization
Save output for comparison:
```bash
next experimental-analyze --output
# Output saved to .next/diagnostics/analyze
```
Reference: https://nextjs.org/docs/app/guides/package-bundling
## Migrating from Webpack to Turbopack
Turbopack is the default bundler in Next.js 15+. If you have custom webpack config, migrate to Turbopack-compatible alternatives:
```js
// next.config.js
module.exports = {
// Good: Works with Turbopack
serverExternalPackages: ['package'],
transpilePackages: ['package'],
// Bad: Webpack-only - migrate away from this
webpack: (config) => {
// custom webpack config
},
}
```
Reference: https://nextjs.org/docs/app/building-your-application/upgrading/from-webpack-to-turbopack

View File

@ -0,0 +1,297 @@
# Data Patterns
Choose the right data fetching pattern for each use case.
## Decision Tree
```
Need to fetch data?
├── From a Server Component?
│ └── Use: Fetch directly (no API needed)
├── From a Client Component?
│ ├── Is it a mutation (POST/PUT/DELETE)?
│ │ └── Use: Server Action
│ └── Is it a read (GET)?
│ └── Use: Route Handler OR pass from Server Component
├── Need external API access (webhooks, third parties)?
│ └── Use: Route Handler
└── Need REST API for mobile app / external clients?
└── Use: Route Handler
```
## Pattern 1: Server Components (Preferred for Reads)
Fetch data directly in Server Components - no API layer needed.
```tsx
// app/users/page.tsx
async function UsersPage() {
// Direct database access - no API round-trip
const users = await db.user.findMany();
// Or fetch from external API
const posts = await fetch('https://api.example.com/posts').then(r => r.json());
return (
<ul>
{users.map(user => <li key={user.id}>{user.name}</li>)}
</ul>
);
}
```
**Benefits**:
- No API to maintain
- No client-server waterfall
- Secrets stay on server
- Direct database access
## Pattern 2: Server Actions (Preferred for Mutations)
Server Actions are the recommended way to handle mutations.
```tsx
// app/actions.ts
'use server';
import { revalidatePath } from 'next/cache';
export async function createPost(formData: FormData) {
const title = formData.get('title') as string;
await db.post.create({ data: { title } });
revalidatePath('/posts');
}
export async function deletePost(id: string) {
await db.post.delete({ where: { id } });
revalidateTag('posts');
}
```
```tsx
// app/posts/new/page.tsx
import { createPost } from '@/app/actions';
export default function NewPost() {
return (
<form action={createPost}>
<input name="title" required />
<button type="submit">Create</button>
</form>
);
}
```
**Benefits**:
- End-to-end type safety
- Progressive enhancement (works without JS)
- Automatic request handling
- Integrated with React transitions
**Constraints**:
- POST only (no GET caching semantics)
- Internal use only (no external access)
- Cannot return non-serializable data
## Pattern 3: Route Handlers (APIs)
Use Route Handlers when you need a REST API.
```tsx
// app/api/posts/route.ts
import { NextRequest, NextResponse } from 'next/server';
// GET is cacheable
export async function GET(request: NextRequest) {
const posts = await db.post.findMany();
return NextResponse.json(posts);
}
// POST for mutations
export async function POST(request: NextRequest) {
const body = await request.json();
const post = await db.post.create({ data: body });
return NextResponse.json(post, { status: 201 });
}
```
**When to use**:
- External API access (mobile apps, third parties)
- Webhooks from external services
- GET endpoints that need HTTP caching
- OpenAPI/Swagger documentation needed
**When NOT to use**:
- Internal data fetching (use Server Components)
- Mutations from your UI (use Server Actions)
## Avoiding Data Waterfalls
### Problem: Sequential Fetches
```tsx
// Bad: Sequential waterfalls
async function Dashboard() {
const user = await getUser(); // Wait...
const posts = await getPosts(); // Then wait...
const comments = await getComments(); // Then wait...
return <div>...</div>;
}
```
### Solution 1: Parallel Fetching with Promise.all
```tsx
// Good: Parallel fetching
async function Dashboard() {
const [user, posts, comments] = await Promise.all([
getUser(),
getPosts(),
getComments(),
]);
return <div>...</div>;
}
```
### Solution 2: Streaming with Suspense
```tsx
// Good: Show content progressively
import { Suspense } from 'react';
async function Dashboard() {
return (
<div>
<Suspense fallback={<UserSkeleton />}>
<UserSection />
</Suspense>
<Suspense fallback={<PostsSkeleton />}>
<PostsSection />
</Suspense>
</div>
);
}
async function UserSection() {
const user = await getUser(); // Fetches independently
return <div>{user.name}</div>;
}
async function PostsSection() {
const posts = await getPosts(); // Fetches independently
return <PostList posts={posts} />;
}
```
### Solution 3: Preload Pattern
```tsx
// lib/data.ts
import { cache } from 'react';
export const getUser = cache(async (id: string) => {
return db.user.findUnique({ where: { id } });
});
export const preloadUser = (id: string) => {
void getUser(id); // Fire and forget
};
```
```tsx
// app/user/[id]/page.tsx
import { getUser, preloadUser } from '@/lib/data';
export default async function UserPage({ params }) {
const { id } = await params;
// Start fetching early
preloadUser(id);
// Do other work...
// Data likely ready by now
const user = await getUser(id);
return <div>{user.name}</div>;
}
```
## Client Component Data Fetching
When Client Components need data:
### Option 1: Pass from Server Component (Preferred)
```tsx
// Server Component
async function Page() {
const data = await fetchData();
return <ClientComponent initialData={data} />;
}
// Client Component
'use client';
function ClientComponent({ initialData }) {
const [data, setData] = useState(initialData);
// ...
}
```
### Option 2: Fetch on Mount (When Necessary)
```tsx
'use client';
import { useEffect, useState } from 'react';
function ClientComponent() {
const [data, setData] = useState(null);
useEffect(() => {
fetch('/api/data')
.then(r => r.json())
.then(setData);
}, []);
if (!data) return <Loading />;
return <div>{data.value}</div>;
}
```
### Option 3: Server Action for Reads (Works But Not Ideal)
Server Actions can be called from Client Components for reads, but this is not their intended purpose:
```tsx
'use client';
import { getData } from './actions';
import { useEffect, useState } from 'react';
function ClientComponent() {
const [data, setData] = useState(null);
useEffect(() => {
getData().then(setData);
}, []);
return <div>{data?.value}</div>;
}
```
**Note**: Server Actions always use POST, so no HTTP caching. Prefer Route Handlers for cacheable reads.
## Quick Reference
| Pattern | Use Case | HTTP Method | Caching |
|---------|----------|-------------|---------|
| Server Component fetch | Internal reads | Any | Full Next.js caching |
| Server Action | Mutations, form submissions | POST only | No |
| Route Handler | External APIs, webhooks | Any | GET can be cached |
| Client fetch to API | Client-side reads | Any | HTTP cache headers |

View File

@ -0,0 +1,105 @@
# Debug Tricks
Tricks to speed up debugging Next.js applications.
## MCP Endpoint (Dev Server)
Next.js exposes a `/_next/mcp` endpoint in development for AI-assisted debugging via MCP (Model Context Protocol).
- **Next.js 16+**: Enabled by default, use `next-devtools-mcp`
- **Next.js < 16**: Requires `experimental.mcpServer: true` in next.config.js
Reference: https://nextjs.org/docs/app/guides/mcp
**Important**: Find the actual port of the running Next.js dev server (check terminal output or `package.json` scripts). Don't assume port 3000.
### Request Format
The endpoint uses JSON-RPC 2.0 over HTTP POST:
```bash
curl -X POST http://localhost:<port>/_next/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{
"jsonrpc": "2.0",
"id": "1",
"method": "tools/call",
"params": {
"name": "<tool-name>",
"arguments": {}
}
}'
```
### Available Tools
#### `get_errors`
Get current errors from dev server (build errors, runtime errors with source-mapped stacks):
```json
{ "name": "get_errors", "arguments": {} }
```
#### `get_routes`
Discover all routes by scanning filesystem:
```json
{ "name": "get_routes", "arguments": {} }
// Optional: { "name": "get_routes", "arguments": { "routerType": "app" } }
```
Returns: `{ "appRouter": ["/", "/api/users/[id]", ...], "pagesRouter": [...] }`
#### `get_project_metadata`
Get project path and dev server URL:
```json
{ "name": "get_project_metadata", "arguments": {} }
```
Returns: `{ "projectPath": "/path/to/project", "devServerUrl": "http://localhost:3000" }`
#### `get_page_metadata`
Get runtime metadata about current page render (requires active browser session):
```json
{ "name": "get_page_metadata", "arguments": {} }
```
Returns segment trie data showing layouts, boundaries, and page components.
#### `get_logs`
Get path to Next.js development log file:
```json
{ "name": "get_logs", "arguments": {} }
```
Returns path to `<distDir>/logs/next-development.log`
#### `get_server_action_by_id`
Locate a Server Action by ID:
```json
{ "name": "get_server_action_by_id", "arguments": { "actionId": "<action-id>" } }
```
### Example: Get Errors
```bash
curl -X POST http://localhost:<port>/_next/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":"1","method":"tools/call","params":{"name":"get_errors","arguments":{}}}'
```
## Rebuild Specific Routes (Next.js 16+)
Use `--debug-build-paths` to rebuild only specific routes instead of the entire app:
```bash
# Rebuild a specific route
next build --debug-build-paths "/dashboard"
# Rebuild routes matching a glob
next build --debug-build-paths "/api/*"
# Dynamic routes
next build --debug-build-paths "/blog/[slug]"
```
Use this to:
- Quickly verify a build fix without full rebuild
- Debug static generation issues for specific pages
- Iterate faster on build errors

View File

@ -0,0 +1,73 @@
# Directives
## React Directives
These are React directives, not Next.js specific.
### `'use client'`
Marks a component as a Client Component. Required for:
- React hooks (`useState`, `useEffect`, etc.)
- Event handlers (`onClick`, `onChange`)
- Browser APIs (`window`, `localStorage`)
```tsx
'use client'
import { useState } from 'react'
export function Counter() {
const [count, setCount] = useState(0)
return <button onClick={() => setCount(count + 1)}>{count}</button>
}
```
Reference: https://react.dev/reference/rsc/use-client
### `'use server'`
Marks a function as a Server Action. Can be passed to Client Components.
```tsx
'use server'
export async function submitForm(formData: FormData) {
// Runs on server
}
```
Or inline within a Server Component:
```tsx
export default function Page() {
async function submit() {
'use server'
// Runs on server
}
return <form action={submit}>...</form>
}
```
Reference: https://react.dev/reference/rsc/use-server
---
## Next.js Directive
### `'use cache'`
Marks a function or component for caching. Part of Next.js Cache Components.
```tsx
'use cache'
export async function getCachedData() {
return await fetchData()
}
```
Requires `cacheComponents: true` in `next.config.ts`.
For detailed usage including cache profiles, `cacheLife()`, `cacheTag()`, and `updateTag()`, see the `next-cache-components` skill.
Reference: https://nextjs.org/docs/app/api-reference/directives/use-cache

View File

@ -0,0 +1,227 @@
# Error Handling
Handle errors gracefully in Next.js applications.
Reference: https://nextjs.org/docs/app/getting-started/error-handling
## Error Boundaries
### `error.tsx`
Catches errors in a route segment and its children:
```tsx
'use client'
export default function Error({
error,
reset,
}: {
error: Error & { digest?: string }
reset: () => void
}) {
return (
<div>
<h2>Something went wrong!</h2>
<button onClick={() => reset()}>Try again</button>
</div>
)
}
```
**Important:** `error.tsx` must be a Client Component.
### `global-error.tsx`
Catches errors in root layout:
```tsx
'use client'
export default function GlobalError({
error,
reset,
}: {
error: Error & { digest?: string }
reset: () => void
}) {
return (
<html>
<body>
<h2>Something went wrong!</h2>
<button onClick={() => reset()}>Try again</button>
</body>
</html>
)
}
```
**Important:** Must include `<html>` and `<body>` tags.
## Server Actions: Navigation API Gotcha
**Do NOT wrap navigation APIs in try-catch.** They throw special errors that Next.js handles internally.
Reference: https://nextjs.org/docs/app/api-reference/functions/redirect#behavior
```tsx
'use server'
import { redirect } from 'next/navigation'
import { notFound } from 'next/navigation'
// Bad: try-catch catches the navigation "error"
async function createPost(formData: FormData) {
try {
const post = await db.post.create({ ... })
redirect(`/posts/${post.id}`) // This throws!
} catch (error) {
// redirect() throw is caught here - navigation fails!
return { error: 'Failed to create post' }
}
}
// Good: Call navigation APIs outside try-catch
async function createPost(formData: FormData) {
let post
try {
post = await db.post.create({ ... })
} catch (error) {
return { error: 'Failed to create post' }
}
redirect(`/posts/${post.id}`) // Outside try-catch
}
// Good: Re-throw navigation errors
async function createPost(formData: FormData) {
try {
const post = await db.post.create({ ... })
redirect(`/posts/${post.id}`)
} catch (error) {
if (error instanceof Error && error.message === 'NEXT_REDIRECT') {
throw error // Re-throw navigation errors
}
return { error: 'Failed to create post' }
}
}
```
Same applies to:
- `redirect()` - 307 temporary redirect
- `permanentRedirect()` - 308 permanent redirect
- `notFound()` - 404 not found
- `forbidden()` - 403 forbidden
- `unauthorized()` - 401 unauthorized
Use `unstable_rethrow()` to re-throw these errors in catch blocks:
```tsx
import { unstable_rethrow } from 'next/navigation'
async function action() {
try {
// ...
redirect('/success')
} catch (error) {
unstable_rethrow(error) // Re-throws Next.js internal errors
return { error: 'Something went wrong' }
}
}
```
## Redirects
```tsx
import { redirect, permanentRedirect } from 'next/navigation'
// 307 Temporary - use for most cases
redirect('/new-path')
// 308 Permanent - use for URL migrations (cached by browsers)
permanentRedirect('/new-url')
```
## Auth Errors
Trigger auth-related error pages:
```tsx
import { forbidden, unauthorized } from 'next/navigation'
async function Page() {
const session = await getSession()
if (!session) {
unauthorized() // Renders unauthorized.tsx (401)
}
if (!session.hasAccess) {
forbidden() // Renders forbidden.tsx (403)
}
return <Dashboard />
}
```
Create corresponding error pages:
```tsx
// app/forbidden.tsx
export default function Forbidden() {
return <div>You don't have access to this resource</div>
}
// app/unauthorized.tsx
export default function Unauthorized() {
return <div>Please log in to continue</div>
}
```
## Not Found
### `not-found.tsx`
Custom 404 page for a route segment:
```tsx
export default function NotFound() {
return (
<div>
<h2>Not Found</h2>
<p>Could not find the requested resource</p>
</div>
)
}
```
### Triggering Not Found
```tsx
import { notFound } from 'next/navigation'
export default async function Page({ params }: { params: Promise<{ id: string }> }) {
const { id } = await params
const post = await getPost(id)
if (!post) {
notFound() // Renders closest not-found.tsx
}
return <div>{post.title}</div>
}
```
## Error Hierarchy
Errors bubble up to the nearest error boundary:
```
app/
├── error.tsx # Catches errors from all children
├── blog/
│ ├── error.tsx # Catches errors in /blog/*
│ └── [slug]/
│ ├── error.tsx # Catches errors in /blog/[slug]
│ └── page.tsx
└── layout.tsx # Errors here go to global-error.tsx
```

View File

@ -0,0 +1,140 @@
# File Conventions
Next.js App Router uses file-based routing with special file conventions.
## Project Structure
Reference: https://nextjs.org/docs/app/getting-started/project-structure
```
app/
├── layout.tsx # Root layout (required)
├── page.tsx # Home page (/)
├── loading.tsx # Loading UI
├── error.tsx # Error UI
├── not-found.tsx # 404 UI
├── global-error.tsx # Global error UI
├── route.ts # API endpoint
├── template.tsx # Re-rendered layout
├── default.tsx # Parallel route fallback
├── blog/
│ ├── page.tsx # /blog
│ └── [slug]/
│ └── page.tsx # /blog/:slug
└── (group)/ # Route group (no URL impact)
└── page.tsx
```
## Special Files
| File | Purpose |
|------|---------|
| `page.tsx` | UI for a route segment |
| `layout.tsx` | Shared UI for segment and children |
| `loading.tsx` | Loading UI (Suspense boundary) |
| `error.tsx` | Error UI (Error boundary) |
| `not-found.tsx` | 404 UI |
| `route.ts` | API endpoint |
| `template.tsx` | Like layout but re-renders on navigation |
| `default.tsx` | Fallback for parallel routes |
## Route Segments
```
app/
├── blog/ # Static segment: /blog
├── [slug]/ # Dynamic segment: /:slug
├── [...slug]/ # Catch-all: /a/b/c
├── [[...slug]]/ # Optional catch-all: / or /a/b/c
└── (marketing)/ # Route group (ignored in URL)
```
## Parallel Routes
```
app/
├── @analytics/
│ └── page.tsx
├── @sidebar/
│ └── page.tsx
└── layout.tsx # Receives { analytics, sidebar } as props
```
## Intercepting Routes
```
app/
├── feed/
│ └── page.tsx
├── @modal/
│ └── (.)photo/[id]/ # Intercepts /photo/[id] from /feed
│ └── page.tsx
└── photo/[id]/
└── page.tsx
```
Conventions:
- `(.)` - same level
- `(..)` - one level up
- `(..)(..)` - two levels up
- `(...)` - from root
## Private Folders
```
app/
├── _components/ # Private folder (not a route)
│ └── Button.tsx
└── page.tsx
```
Prefix with `_` to exclude from routing.
## Middleware / Proxy
### Next.js 14-15: `middleware.ts`
```ts
// middleware.ts (root of project)
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
// Auth, redirects, rewrites, etc.
return NextResponse.next();
}
export const config = {
matcher: ['/dashboard/:path*', '/api/:path*'],
};
```
### Next.js 16+: `proxy.ts`
Renamed for clarity - same capabilities, different names:
```ts
// proxy.ts (root of project)
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function proxy(request: NextRequest) {
// Same logic as middleware
return NextResponse.next();
}
export const config = {
matcher: ['/dashboard/:path*', '/api/:path*'],
};
```
| Version | File | Export | Config |
|---------|------|--------|--------|
| v14-15 | `middleware.ts` | `middleware()` | `config` |
| v16+ | `proxy.ts` | `proxy()` | `config` |
**Migration**: Run `npx @next/codemod@latest upgrade` to auto-rename.
## File Conventions Reference
Reference: https://nextjs.org/docs/app/api-reference/file-conventions

View File

@ -0,0 +1,245 @@
# Font Optimization
Use `next/font` for automatic font optimization with zero layout shift.
## Google Fonts
```tsx
// app/layout.tsx
import { Inter } from 'next/font/google'
const inter = Inter({ subsets: ['latin'] })
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en" className={inter.className}>
<body>{children}</body>
</html>
)
}
```
## Multiple Fonts
```tsx
import { Inter, Roboto_Mono } from 'next/font/google'
const inter = Inter({
subsets: ['latin'],
variable: '--font-inter',
})
const robotoMono = Roboto_Mono({
subsets: ['latin'],
variable: '--font-roboto-mono',
})
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en" className={`${inter.variable} ${robotoMono.variable}`}>
<body>{children}</body>
</html>
)
}
```
Use in CSS:
```css
body {
font-family: var(--font-inter);
}
code {
font-family: var(--font-roboto-mono);
}
```
## Font Weights and Styles
```tsx
// Single weight
const inter = Inter({
subsets: ['latin'],
weight: '400',
})
// Multiple weights
const inter = Inter({
subsets: ['latin'],
weight: ['400', '500', '700'],
})
// Variable font (recommended) - includes all weights
const inter = Inter({
subsets: ['latin'],
// No weight needed - variable fonts support all weights
})
// With italic
const inter = Inter({
subsets: ['latin'],
style: ['normal', 'italic'],
})
```
## Local Fonts
```tsx
import localFont from 'next/font/local'
const myFont = localFont({
src: './fonts/MyFont.woff2',
})
// Multiple files for different weights
const myFont = localFont({
src: [
{
path: './fonts/MyFont-Regular.woff2',
weight: '400',
style: 'normal',
},
{
path: './fonts/MyFont-Bold.woff2',
weight: '700',
style: 'normal',
},
],
})
// Variable font
const myFont = localFont({
src: './fonts/MyFont-Variable.woff2',
variable: '--font-my-font',
})
```
## Tailwind CSS Integration
```tsx
// app/layout.tsx
import { Inter } from 'next/font/google'
const inter = Inter({
subsets: ['latin'],
variable: '--font-inter',
})
export default function RootLayout({ children }) {
return (
<html lang="en" className={inter.variable}>
<body>{children}</body>
</html>
)
}
```
```js
// tailwind.config.js
module.exports = {
theme: {
extend: {
fontFamily: {
sans: ['var(--font-inter)'],
},
},
},
}
```
## Preloading Subsets
Only load needed character subsets:
```tsx
// Latin only (most common)
const inter = Inter({ subsets: ['latin'] })
// Multiple subsets
const inter = Inter({ subsets: ['latin', 'latin-ext', 'cyrillic'] })
```
## Display Strategy
Control font loading behavior:
```tsx
const inter = Inter({
subsets: ['latin'],
display: 'swap', // Default - shows fallback, swaps when loaded
})
// Options:
// 'auto' - browser decides
// 'block' - short block period, then swap
// 'swap' - immediate fallback, swap when ready (recommended)
// 'fallback' - short block, short swap, then fallback
// 'optional' - short block, no swap (use if font is optional)
```
## Don't Use Manual Font Links
Always use `next/font` instead of `<link>` tags for Google Fonts.
```tsx
// Bad: Manual link tag (blocks rendering, no optimization)
<link href="https://fonts.googleapis.com/css2?family=Inter" rel="stylesheet" />
// Bad: Missing display and preconnect
<link href="https://fonts.googleapis.com/css2?family=Inter" rel="stylesheet" />
// Good: Use next/font (self-hosted, zero layout shift)
import { Inter } from 'next/font/google'
const inter = Inter({ subsets: ['latin'] })
```
## Common Mistakes
```tsx
// Bad: Importing font in every component
// components/Button.tsx
import { Inter } from 'next/font/google'
const inter = Inter({ subsets: ['latin'] }) // Creates new instance each time!
// Good: Import once in layout, use CSS variable
// app/layout.tsx
const inter = Inter({ subsets: ['latin'], variable: '--font-inter' })
// Bad: Using @import in CSS (blocks rendering)
/* globals.css */
@import url('https://fonts.googleapis.com/css2?family=Inter');
// Good: Use next/font (self-hosted, no network request)
import { Inter } from 'next/font/google'
// Bad: Loading all weights when only using a few
const inter = Inter({ subsets: ['latin'] }) // Loads all weights
// Good: Specify only needed weights (for non-variable fonts)
const inter = Inter({ subsets: ['latin'], weight: ['400', '700'] })
// Bad: Missing subset - loads all characters
const inter = Inter({})
// Good: Always specify subset
const inter = Inter({ subsets: ['latin'] })
```
## Font in Specific Components
```tsx
// For component-specific fonts, export from a shared file
// lib/fonts.ts
import { Inter, Playfair_Display } from 'next/font/google'
export const inter = Inter({ subsets: ['latin'], variable: '--font-inter' })
export const playfair = Playfair_Display({ subsets: ['latin'], variable: '--font-playfair' })
// components/Heading.tsx
import { playfair } from '@/lib/fonts'
export function Heading({ children }) {
return <h1 className={playfair.className}>{children}</h1>
}
```

View File

@ -0,0 +1,108 @@
# Functions
Next.js function APIs.
Reference: https://nextjs.org/docs/app/api-reference/functions
## Navigation Hooks (Client)
| Hook | Purpose | Reference |
|------|---------|-----------|
| `useRouter` | Programmatic navigation (`push`, `replace`, `back`, `refresh`) | [Docs](https://nextjs.org/docs/app/api-reference/functions/use-router) |
| `usePathname` | Get current pathname | [Docs](https://nextjs.org/docs/app/api-reference/functions/use-pathname) |
| `useSearchParams` | Read URL search parameters | [Docs](https://nextjs.org/docs/app/api-reference/functions/use-search-params) |
| `useParams` | Access dynamic route parameters | [Docs](https://nextjs.org/docs/app/api-reference/functions/use-params) |
| `useSelectedLayoutSegment` | Active child segment (one level) | [Docs](https://nextjs.org/docs/app/api-reference/functions/use-selected-layout-segment) |
| `useSelectedLayoutSegments` | All active segments below layout | [Docs](https://nextjs.org/docs/app/api-reference/functions/use-selected-layout-segments) |
| `useLinkStatus` | Check link prefetch status | [Docs](https://nextjs.org/docs/app/api-reference/functions/use-link-status) |
| `useReportWebVitals` | Report Core Web Vitals metrics | [Docs](https://nextjs.org/docs/app/api-reference/functions/use-report-web-vitals) |
## Server Functions
| Function | Purpose | Reference |
|----------|---------|-----------|
| `cookies` | Read/write cookies | [Docs](https://nextjs.org/docs/app/api-reference/functions/cookies) |
| `headers` | Read request headers | [Docs](https://nextjs.org/docs/app/api-reference/functions/headers) |
| `draftMode` | Enable preview of unpublished CMS content | [Docs](https://nextjs.org/docs/app/api-reference/functions/draft-mode) |
| `after` | Run code after response finishes streaming | [Docs](https://nextjs.org/docs/app/api-reference/functions/after) |
| `connection` | Wait for connection before dynamic rendering | [Docs](https://nextjs.org/docs/app/api-reference/functions/connection) |
| `userAgent` | Parse User-Agent header | [Docs](https://nextjs.org/docs/app/api-reference/functions/userAgent) |
## Generate Functions
| Function | Purpose | Reference |
|----------|---------|-----------|
| `generateStaticParams` | Pre-render dynamic routes at build time | [Docs](https://nextjs.org/docs/app/api-reference/functions/generate-static-params) |
| `generateMetadata` | Dynamic metadata | [Docs](https://nextjs.org/docs/app/api-reference/functions/generate-metadata) |
| `generateViewport` | Dynamic viewport config | [Docs](https://nextjs.org/docs/app/api-reference/functions/generate-viewport) |
| `generateSitemaps` | Multiple sitemaps for large sites | [Docs](https://nextjs.org/docs/app/api-reference/functions/generate-sitemaps) |
| `generateImageMetadata` | Multiple OG images per route | [Docs](https://nextjs.org/docs/app/api-reference/functions/generate-image-metadata) |
## Request/Response
| Function | Purpose | Reference |
|----------|---------|-----------|
| `NextRequest` | Extended Request with helpers | [Docs](https://nextjs.org/docs/app/api-reference/functions/next-request) |
| `NextResponse` | Extended Response with helpers | [Docs](https://nextjs.org/docs/app/api-reference/functions/next-response) |
| `ImageResponse` | Generate OG images | [Docs](https://nextjs.org/docs/app/api-reference/functions/image-response) |
## Common Examples
### Navigation
Use `next/link` for internal navigation instead of `<a>` tags.
```tsx
// Bad: Plain anchor tag
<a href="/about">About</a>
// Good: Next.js Link
import Link from 'next/link'
<Link href="/about">About</Link>
```
Active link styling:
```tsx
'use client'
import Link from 'next/link'
import { usePathname } from 'next/navigation'
export function NavLink({ href, children }) {
const pathname = usePathname()
return (
<Link href={href} className={pathname === href ? 'active' : ''}>
{children}
</Link>
)
}
```
### Static Generation
```tsx
// app/blog/[slug]/page.tsx
export async function generateStaticParams() {
const posts = await getPosts()
return posts.map((post) => ({ slug: post.slug }))
}
```
### After Response
```tsx
import { after } from 'next/server'
export async function POST(request: Request) {
const data = await processRequest(request)
after(async () => {
await logAnalytics(data)
})
return Response.json({ success: true })
}
```

View File

@ -0,0 +1,91 @@
# Hydration Errors
Diagnose and fix React hydration mismatch errors.
## Error Signs
- "Hydration failed because the initial UI does not match"
- "Text content does not match server-rendered HTML"
## Debugging
In development, click the hydration error to see the server/client diff.
## Common Causes and Fixes
### Browser-only APIs
```tsx
// Bad: Causes mismatch - window doesn't exist on server
<div>{window.innerWidth}</div>
// Good: Use client component with mounted check
'use client'
import { useState, useEffect } from 'react'
export function ClientOnly({ children }: { children: React.ReactNode }) {
const [mounted, setMounted] = useState(false)
useEffect(() => setMounted(true), [])
return mounted ? children : null
}
```
### Date/Time Rendering
Server and client may be in different timezones:
```tsx
// Bad: Causes mismatch
<span>{new Date().toLocaleString()}</span>
// Good: Render on client only
'use client'
const [time, setTime] = useState<string>()
useEffect(() => setTime(new Date().toLocaleString()), [])
```
### Random Values or IDs
```tsx
// Bad: Random values differ between server and client
<div id={Math.random().toString()}>
// Good: Use useId hook
import { useId } from 'react'
function Input() {
const id = useId()
return <input id={id} />
}
```
### Invalid HTML Nesting
```tsx
// Bad: Invalid - div inside p
<p><div>Content</div></p>
// Bad: Invalid - p inside p
<p><p>Nested</p></p>
// Good: Valid nesting
<div><p>Content</p></div>
```
### Third-party Scripts
Scripts that modify DOM during hydration.
```tsx
// Good: Use next/script with afterInteractive
import Script from 'next/script'
export default function Page() {
return (
<Script
src="https://example.com/script.js"
strategy="afterInteractive"
/>
)
}
```

View File

@ -0,0 +1,173 @@
# Image Optimization
Use `next/image` for automatic image optimization.
## Always Use next/image
```tsx
// Bad: Avoid native img
<img src="/hero.png" alt="Hero" />
// Good: Use next/image
import Image from 'next/image'
<Image src="/hero.png" alt="Hero" width={800} height={400} />
```
## Required Props
Images need explicit dimensions to prevent layout shift:
```tsx
// Local images - dimensions inferred automatically
import heroImage from './hero.png'
<Image src={heroImage} alt="Hero" />
// Remote images - must specify width/height
<Image src="https://example.com/image.jpg" alt="Hero" width={800} height={400} />
// Or use fill for parent-relative sizing
<div style={{ position: 'relative', width: '100%', height: 400 }}>
<Image src="/hero.png" alt="Hero" fill style={{ objectFit: 'cover' }} />
</div>
```
## Remote Images Configuration
Remote domains must be configured in `next.config.js`:
```js
// next.config.js
module.exports = {
images: {
remotePatterns: [
{
protocol: 'https',
hostname: 'example.com',
pathname: '/images/**',
},
{
protocol: 'https',
hostname: '*.cdn.com', // Wildcard subdomain
},
],
},
}
```
## Responsive Images
Use `sizes` to tell the browser which size to download:
```tsx
// Full-width hero
<Image
src="/hero.png"
alt="Hero"
fill
sizes="100vw"
/>
// Responsive grid (3 columns on desktop, 1 on mobile)
<Image
src="/card.png"
alt="Card"
fill
sizes="(max-width: 768px) 100vw, 33vw"
/>
// Fixed sidebar image
<Image
src="/avatar.png"
alt="Avatar"
width={200}
height={200}
sizes="200px"
/>
```
## Blur Placeholder
Prevent layout shift with placeholders:
```tsx
// Local images - automatic blur hash
import heroImage from './hero.png'
<Image src={heroImage} alt="Hero" placeholder="blur" />
// Remote images - provide blurDataURL
<Image
src="https://example.com/image.jpg"
alt="Hero"
width={800}
height={400}
placeholder="blur"
blurDataURL="data:image/jpeg;base64,/9j/4AAQSkZJRg..."
/>
// Or use color placeholder
<Image
src="https://example.com/image.jpg"
alt="Hero"
width={800}
height={400}
placeholder="empty"
style={{ backgroundColor: '#e0e0e0' }}
/>
```
## Priority Loading
Use `priority` for above-the-fold images (LCP):
```tsx
// Hero image - loads immediately
<Image src="/hero.png" alt="Hero" fill priority />
// Below-fold images - lazy loaded by default (no priority needed)
<Image src="/card.png" alt="Card" width={400} height={300} />
```
## Common Mistakes
```tsx
// Bad: Missing sizes with fill - downloads largest image
<Image src="/hero.png" alt="Hero" fill />
// Good: Add sizes for proper responsive behavior
<Image src="/hero.png" alt="Hero" fill sizes="100vw" />
// Bad: Using width/height for aspect ratio only
<Image src="/hero.png" alt="Hero" width={16} height={9} />
// Good: Use actual display dimensions or fill with sizes
<Image src="/hero.png" alt="Hero" fill sizes="100vw" style={{ objectFit: 'cover' }} />
// Bad: Remote image without config
<Image src="https://untrusted.com/image.jpg" alt="Image" width={400} height={300} />
// Error: Invalid src prop, hostname not configured
// Good: Add hostname to next.config.js remotePatterns
```
## Static Export
When using `output: 'export'`, use `unoptimized` or custom loader:
```tsx
// Option 1: Disable optimization
<Image src="/hero.png" alt="Hero" width={800} height={400} unoptimized />
// Option 2: Global config
// next.config.js
module.exports = {
output: 'export',
images: { unoptimized: true },
}
// Option 3: Custom loader (Cloudinary, Imgix, etc.)
const cloudinaryLoader = ({ src, width, quality }) => {
return `https://res.cloudinary.com/demo/image/upload/w_${width},q_${quality || 75}/${src}`
}
<Image loader={cloudinaryLoader} src="sample.jpg" alt="Sample" width={800} height={400} />
```

View File

@ -0,0 +1,301 @@
# Metadata
Add SEO metadata to Next.js pages using the Metadata API.
## Important: Server Components Only
The `metadata` object and `generateMetadata` function are **only supported in Server Components**. They cannot be used in Client Components.
If the target page has `'use client'`:
1. Remove `'use client'` if possible, move client logic to child components
2. Or extract metadata to a parent Server Component layout
3. Or split the file: Server Component with metadata imports Client Components
## Static Metadata
```tsx
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: 'Page Title',
description: 'Page description for search engines',
}
```
## Dynamic Metadata
```tsx
import type { Metadata } from 'next'
type Props = { params: Promise<{ slug: string }> }
export async function generateMetadata({ params }: Props): Promise<Metadata> {
const { slug } = await params
const post = await getPost(slug)
return { title: post.title, description: post.description }
}
```
## Avoid Duplicate Fetches
Use React `cache()` when the same data is needed for both metadata and page:
```tsx
import { cache } from 'react'
export const getPost = cache(async (slug: string) => {
return await db.posts.findFirst({ where: { slug } })
})
```
## Viewport
Separate from metadata for streaming support:
```tsx
import type { Viewport } from 'next'
export const viewport: Viewport = {
width: 'device-width',
initialScale: 1,
themeColor: '#000000',
}
// Or dynamic
export function generateViewport({ params }): Viewport {
return { themeColor: getThemeColor(params) }
}
```
## Title Templates
In root layout for consistent naming:
```tsx
export const metadata: Metadata = {
title: { default: 'Site Name', template: '%s | Site Name' },
}
```
## Metadata File Conventions
Reference: https://nextjs.org/docs/app/getting-started/project-structure#metadata-file-conventions
Place these files in `app/` directory (or route segments):
| File | Purpose |
|------|---------|
| `favicon.ico` | Favicon |
| `icon.png` / `icon.svg` | App icon |
| `apple-icon.png` | Apple app icon |
| `opengraph-image.png` | OG image |
| `twitter-image.png` | Twitter card image |
| `sitemap.ts` / `sitemap.xml` | Sitemap (use `generateSitemaps` for multiple) |
| `robots.ts` / `robots.txt` | Robots directives |
| `manifest.ts` / `manifest.json` | Web app manifest |
## SEO Best Practice: Static Files Are Often Enough
For most sites, **static metadata files provide excellent SEO coverage**:
```
app/
├── favicon.ico
├── opengraph-image.png # Works for both OG and Twitter
├── sitemap.ts
├── robots.ts
└── layout.tsx # With title/description metadata
```
**Tips:**
- A single `opengraph-image.png` covers both Open Graph and Twitter (Twitter falls back to OG)
- Static `title` and `description` in layout metadata is sufficient for most pages
- Only use dynamic `generateMetadata` when content varies per page
---
# OG Image Generation
Generate dynamic Open Graph images using `next/og`.
## Important Rules
1. **Use `next/og`** - not `@vercel/og` (it's built into Next.js)
2. **No searchParams** - OG images can't access search params, use route params instead
3. **Avoid Edge runtime** - Use default Node.js runtime
```tsx
// Good
import { ImageResponse } from 'next/og'
// Bad
// import { ImageResponse } from '@vercel/og'
// export const runtime = 'edge'
```
## Basic OG Image
```tsx
// app/opengraph-image.tsx
import { ImageResponse } from 'next/og'
export const alt = 'Site Name'
export const size = { width: 1200, height: 630 }
export const contentType = 'image/png'
export default function Image() {
return new ImageResponse(
(
<div
style={{
fontSize: 128,
background: 'white',
width: '100%',
height: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
Hello World
</div>
),
{ ...size }
)
}
```
## Dynamic OG Image
```tsx
// app/blog/[slug]/opengraph-image.tsx
import { ImageResponse } from 'next/og'
export const alt = 'Blog Post'
export const size = { width: 1200, height: 630 }
export const contentType = 'image/png'
type Props = { params: Promise<{ slug: string }> }
export default async function Image({ params }: Props) {
const { slug } = await params
const post = await getPost(slug)
return new ImageResponse(
(
<div
style={{
fontSize: 48,
background: 'linear-gradient(to bottom, #1a1a1a, #333)',
color: 'white',
width: '100%',
height: '100%',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
padding: 48,
}}
>
<div style={{ fontSize: 64, fontWeight: 'bold' }}>{post.title}</div>
<div style={{ marginTop: 24, opacity: 0.8 }}>{post.description}</div>
</div>
),
{ ...size }
)
}
```
## Custom Fonts
```tsx
import { ImageResponse } from 'next/og'
import { join } from 'path'
import { readFile } from 'fs/promises'
export default async function Image() {
const fontPath = join(process.cwd(), 'assets/fonts/Inter-Bold.ttf')
const fontData = await readFile(fontPath)
return new ImageResponse(
(
<div style={{ fontFamily: 'Inter', fontSize: 64 }}>
Custom Font Text
</div>
),
{
width: 1200,
height: 630,
fonts: [{ name: 'Inter', data: fontData, style: 'normal' }],
}
)
}
```
## File Naming
- `opengraph-image.tsx` - Open Graph (Facebook, LinkedIn)
- `twitter-image.tsx` - Twitter/X cards (optional, falls back to OG)
## Styling Notes
ImageResponse uses Flexbox layout:
- Use `display: 'flex'`
- No CSS Grid support
- Styles must be inline objects
## Multiple OG Images
Use `generateImageMetadata` for multiple images per route:
```tsx
// app/blog/[slug]/opengraph-image.tsx
import { ImageResponse } from 'next/og'
export async function generateImageMetadata({ params }) {
const images = await getPostImages(params.slug)
return images.map((img, idx) => ({
id: idx,
alt: img.alt,
size: { width: 1200, height: 630 },
contentType: 'image/png',
}))
}
export default async function Image({ params, id }) {
const images = await getPostImages(params.slug)
const image = images[id]
return new ImageResponse(/* ... */)
}
```
## Multiple Sitemaps
Use `generateSitemaps` for large sites:
```tsx
// app/sitemap.ts
import type { MetadataRoute } from 'next'
export async function generateSitemaps() {
// Return array of sitemap IDs
return [{ id: 0 }, { id: 1 }, { id: 2 }]
}
export default async function sitemap({
id,
}: {
id: number
}): Promise<MetadataRoute.Sitemap> {
const start = id * 50000
const end = start + 50000
const products = await getProducts(start, end)
return products.map((product) => ({
url: `https://example.com/product/${product.id}`,
lastModified: product.updatedAt,
}))
}
```
Generates `/sitemap/0.xml`, `/sitemap/1.xml`, etc.

View File

@ -0,0 +1,287 @@
# Parallel & Intercepting Routes
Parallel routes render multiple pages in the same layout. Intercepting routes show a different UI when navigating from within your app vs direct URL access. Together they enable modal patterns.
## File Structure
```
app/
├── @modal/ # Parallel route slot
│ ├── default.tsx # Required! Returns null
│ ├── (.)photos/ # Intercepts /photos/*
│ │ └── [id]/
│ │ └── page.tsx # Modal content
│ └── [...]catchall/ # Optional: catch unmatched
│ └── page.tsx
├── photos/
│ └── [id]/
│ └── page.tsx # Full page (direct access)
├── layout.tsx # Renders both children and @modal
└── page.tsx
```
## Step 1: Root Layout with Slot
```tsx
// app/layout.tsx
export default function RootLayout({
children,
modal,
}: {
children: React.ReactNode;
modal: React.ReactNode;
}) {
return (
<html>
<body>
{children}
{modal}
</body>
</html>
);
}
```
## Step 2: Default File (Critical!)
**Every parallel route slot MUST have a `default.tsx`** to prevent 404s on hard navigation.
```tsx
// app/@modal/default.tsx
export default function Default() {
return null;
}
```
Without this file, refreshing any page will 404 because Next.js can't determine what to render in the `@modal` slot.
## Step 3: Intercepting Route (Modal)
The `(.)` prefix intercepts routes at the same level.
```tsx
// app/@modal/(.)photos/[id]/page.tsx
import { Modal } from '@/components/modal';
export default async function PhotoModal({
params
}: {
params: Promise<{ id: string }>
}) {
const { id } = await params;
const photo = await getPhoto(id);
return (
<Modal>
<img src={photo.url} alt={photo.title} />
</Modal>
);
}
```
## Step 4: Full Page (Direct Access)
```tsx
// app/photos/[id]/page.tsx
export default async function PhotoPage({
params
}: {
params: Promise<{ id: string }>
}) {
const { id } = await params;
const photo = await getPhoto(id);
return (
<div className="full-page">
<img src={photo.url} alt={photo.title} />
<h1>{photo.title}</h1>
</div>
);
}
```
## Step 5: Modal Component with Correct Closing
**Critical: Use `router.back()` to close modals, NOT `router.push()` or `<Link>`.**
```tsx
// components/modal.tsx
'use client';
import { useRouter } from 'next/navigation';
import { useCallback, useEffect, useRef } from 'react';
export function Modal({ children }: { children: React.ReactNode }) {
const router = useRouter();
const overlayRef = useRef<HTMLDivElement>(null);
// Close on escape key
useEffect(() => {
function onKeyDown(e: KeyboardEvent) {
if (e.key === 'Escape') {
router.back(); // Correct
}
}
document.addEventListener('keydown', onKeyDown);
return () => document.removeEventListener('keydown', onKeyDown);
}, [router]);
// Close on overlay click
const handleOverlayClick = useCallback((e: React.MouseEvent) => {
if (e.target === overlayRef.current) {
router.back(); // Correct
}
}, [router]);
return (
<div
ref={overlayRef}
onClick={handleOverlayClick}
className="fixed inset-0 bg-black/50 flex items-center justify-center z-50"
>
<div className="bg-white rounded-lg p-6 max-w-2xl w-full mx-4">
<button
onClick={() => router.back()} // Correct!
className="absolute top-4 right-4"
>
Close
</button>
{children}
</div>
</div>
);
}
```
### Why NOT `router.push('/')` or `<Link href="/">`?
Using `push` or `Link` to "close" a modal:
1. Adds a new history entry (back button shows modal again)
2. Doesn't properly clear the intercepted route
3. Can cause the modal to flash or persist unexpectedly
`router.back()` correctly:
1. Removes the intercepted route from history
2. Returns to the previous page
3. Properly unmounts the modal
## Route Matcher Reference
Matchers match **route segments**, not filesystem paths:
| Matcher | Matches | Example |
|---------|---------|---------|
| `(.)` | Same level | `@modal/(.)photos` intercepts `/photos` |
| `(..)` | One level up | `@modal/(..)settings` from `/dashboard/@modal` intercepts `/settings` |
| `(..)(..)` | Two levels up | Rarely used |
| `(...)` | From root | `@modal/(...)photos` intercepts `/photos` from anywhere |
**Common mistake**: Thinking `(..)` means "parent folder" - it means "parent route segment".
## Handling Hard Navigation
When users directly visit `/photos/123` (bookmark, refresh, shared link):
- The intercepting route is bypassed
- The full `photos/[id]/page.tsx` renders
- Modal doesn't appear (expected behavior)
If you want the modal to appear on direct access too, you need additional logic:
```tsx
// app/photos/[id]/page.tsx
import { Modal } from '@/components/modal';
export default async function PhotoPage({ params }) {
const { id } = await params;
const photo = await getPhoto(id);
// Option: Render as modal on direct access too
return (
<Modal>
<img src={photo.url} alt={photo.title} />
</Modal>
);
}
```
## Common Gotchas
### 1. Missing `default.tsx` → 404 on Refresh
Every `@slot` folder needs a `default.tsx` that returns `null` (or appropriate content).
### 2. Modal Persists After Navigation
You're using `router.push()` instead of `router.back()`.
### 3. Nested Parallel Routes Need Defaults Too
If you have `@modal` inside a route group, each level needs its own `default.tsx`:
```
app/
├── (marketing)/
│ ├── @modal/
│ │ └── default.tsx # Needed!
│ └── layout.tsx
└── layout.tsx
```
### 4. Intercepted Route Shows Wrong Content
Check your matcher:
- `(.)photos` intercepts `/photos` from the same route level
- If your `@modal` is in `app/dashboard/@modal`, use `(.)photos` to intercept `/dashboard/photos`, not `/photos`
### 5. TypeScript Errors with `params`
In Next.js 15+, `params` is a Promise:
```tsx
// Correct
export default async function Page({ params }: { params: Promise<{ id: string }> }) {
const { id } = await params;
}
```
## Complete Example: Photo Gallery Modal
```
app/
├── @modal/
│ ├── default.tsx
│ └── (.)photos/
│ └── [id]/
│ └── page.tsx
├── photos/
│ ├── page.tsx # Gallery grid
│ └── [id]/
│ └── page.tsx # Full photo page
├── layout.tsx
└── page.tsx
```
Links in the gallery:
```tsx
// app/photos/page.tsx
import Link from 'next/link';
export default async function Gallery() {
const photos = await getPhotos();
return (
<div className="grid grid-cols-3 gap-4">
{photos.map(photo => (
<Link key={photo.id} href={`/photos/${photo.id}`}>
<img src={photo.thumbnail} alt={photo.title} />
</Link>
))}
</div>
);
}
```
Clicking a photo → Modal opens (intercepted)
Direct URL → Full page renders
Refresh while modal open → Full page renders

View File

@ -0,0 +1,146 @@
# Route Handlers
Create API endpoints with `route.ts` files.
## Basic Usage
```tsx
// app/api/users/route.ts
export async function GET() {
const users = await getUsers()
return Response.json(users)
}
export async function POST(request: Request) {
const body = await request.json()
const user = await createUser(body)
return Response.json(user, { status: 201 })
}
```
## Supported Methods
`GET`, `POST`, `PUT`, `PATCH`, `DELETE`, `HEAD`, `OPTIONS`
## GET Handler Conflicts with page.tsx
**A `route.ts` and `page.tsx` cannot coexist in the same folder.**
```
app/
├── api/
│ └── users/
│ └── route.ts # /api/users
└── users/
├── page.tsx # /users (page)
└── route.ts # Warning: Conflicts with page.tsx!
```
If you need both a page and an API at the same path, use different paths:
```
app/
├── users/
│ └── page.tsx # /users (page)
└── api/
└── users/
└── route.ts # /api/users (API)
```
## Environment Behavior
Route handlers run in a **Server Component-like environment**:
- Yes: Can use `async/await`
- Yes: Can access `cookies()`, `headers()`
- Yes: Can use Node.js APIs
- No: Cannot use React hooks
- No: Cannot use React DOM APIs
- No: Cannot use browser APIs
```tsx
// Bad: This won't work - no React DOM in route handlers
import { renderToString } from 'react-dom/server'
export async function GET() {
const html = renderToString(<Component />) // Error!
return new Response(html)
}
```
## Dynamic Route Handlers
```tsx
// app/api/users/[id]/route.ts
export async function GET(
request: Request,
{ params }: { params: Promise<{ id: string }> }
) {
const { id } = await params
const user = await getUser(id)
if (!user) {
return Response.json({ error: 'Not found' }, { status: 404 })
}
return Response.json(user)
}
```
## Request Helpers
```tsx
export async function GET(request: Request) {
// URL and search params
const { searchParams } = new URL(request.url)
const query = searchParams.get('q')
// Headers
const authHeader = request.headers.get('authorization')
// Cookies (Next.js helper)
const cookieStore = await cookies()
const token = cookieStore.get('token')
return Response.json({ query, token })
}
```
## Response Helpers
```tsx
// JSON response
return Response.json({ data })
// With status
return Response.json({ error: 'Not found' }, { status: 404 })
// With headers
return Response.json(data, {
headers: {
'Cache-Control': 'max-age=3600',
},
})
// Redirect
return Response.redirect(new URL('/login', request.url))
// Stream
return new Response(stream, {
headers: { 'Content-Type': 'text/event-stream' },
})
```
## When to Use Route Handlers vs Server Actions
| Use Case | Route Handlers | Server Actions |
|----------|----------------|----------------|
| Form submissions | No | Yes |
| Data mutations from UI | No | Yes |
| Third-party webhooks | Yes | No |
| External API consumption | Yes | No |
| Public REST API | Yes | No |
| File uploads | Both work | Both work |
**Prefer Server Actions** for mutations triggered from your UI.
**Use Route Handlers** for external integrations and public APIs.

View File

@ -0,0 +1,159 @@
# RSC Boundaries
Detect and prevent invalid patterns when crossing Server/Client component boundaries.
## Detection Rules
### 1. Async Client Components Are Invalid
Client components **cannot** be async functions. Only Server Components can be async.
**Detect:** File has `'use client'` AND component is `async function` or returns `Promise`
```tsx
// Bad: async client component
'use client'
export default async function UserProfile() {
const user = await getUser() // Cannot await in client component
return <div>{user.name}</div>
}
// Good: Remove async, fetch data in parent server component
// page.tsx (server component - no 'use client')
export default async function Page() {
const user = await getUser()
return <UserProfile user={user} />
}
// UserProfile.tsx (client component)
'use client'
export function UserProfile({ user }: { user: User }) {
return <div>{user.name}</div>
}
```
```tsx
// Bad: async arrow function client component
'use client'
const Dashboard = async () => {
const data = await fetchDashboard()
return <div>{data}</div>
}
// Good: Fetch in server component, pass data down
```
### 2. Non-Serializable Props to Client Components
Props passed from Server → Client must be JSON-serializable.
**Detect:** Server component passes these to a client component:
- Functions (except Server Actions with `'use server'`)
- `Date` objects
- `Map`, `Set`, `WeakMap`, `WeakSet`
- Class instances
- `Symbol` (unless globally registered)
- Circular references
```tsx
// Bad: Function prop
// page.tsx (server)
export default function Page() {
const handleClick = () => console.log('clicked')
return <ClientButton onClick={handleClick} />
}
// Good: Define function inside client component
// ClientButton.tsx
'use client'
export function ClientButton() {
const handleClick = () => console.log('clicked')
return <button onClick={handleClick}>Click</button>
}
```
```tsx
// Bad: Date object (silently becomes string, then crashes)
// page.tsx (server)
export default async function Page() {
const post = await getPost()
return <PostCard createdAt={post.createdAt} /> // Date object
}
// PostCard.tsx (client) - will crash on .getFullYear()
'use client'
export function PostCard({ createdAt }: { createdAt: Date }) {
return <span>{createdAt.getFullYear()}</span> // Runtime error!
}
// Good: Serialize to string on server
// page.tsx (server)
export default async function Page() {
const post = await getPost()
return <PostCard createdAt={post.createdAt.toISOString()} />
}
// PostCard.tsx (client)
'use client'
export function PostCard({ createdAt }: { createdAt: string }) {
const date = new Date(createdAt)
return <span>{date.getFullYear()}</span>
}
```
```tsx
// Bad: Class instance
const user = new UserModel(data)
<ClientProfile user={user} /> // Methods will be stripped
// Good: Pass plain object
const user = await getUser()
<ClientProfile user={{ id: user.id, name: user.name }} />
```
```tsx
// Bad: Map/Set
<ClientComponent items={new Map([['a', 1]])} />
// Good: Convert to array/object
<ClientComponent items={Object.fromEntries(map)} />
<ClientComponent items={Array.from(set)} />
```
### 3. Server Actions Are the Exception
Functions marked with `'use server'` CAN be passed to client components.
```tsx
// Valid: Server Action can be passed
// actions.ts
'use server'
export async function submitForm(formData: FormData) {
// server-side logic
}
// page.tsx (server)
import { submitForm } from './actions'
export default function Page() {
return <ClientForm onSubmit={submitForm} /> // OK!
}
// ClientForm.tsx (client)
'use client'
export function ClientForm({ onSubmit }: { onSubmit: (data: FormData) => Promise<void> }) {
return <form action={onSubmit}>...</form>
}
```
## Quick Reference
| Pattern | Valid? | Fix |
|---------|--------|-----|
| `'use client'` + `async function` | No | Fetch in server parent, pass data |
| Pass `() => {}` to client | No | Define in client or use server action |
| Pass `new Date()` to client | No | Use `.toISOString()` |
| Pass `new Map()` to client | No | Convert to object/array |
| Pass class instance to client | No | Pass plain object |
| Pass server action to client | Yes | - |
| Pass `string/number/boolean` | Yes | - |
| Pass plain object/array | Yes | - |

View File

@ -0,0 +1,39 @@
# Runtime Selection
## Use Node.js Runtime by Default
Use the default Node.js runtime for new routes and pages. Only use Edge runtime if the project already uses it or there's a specific requirement.
```tsx
// Good: Default - no runtime config needed (uses Node.js)
export default function Page() { ... }
// Caution: Only if already used in project or specifically required
export const runtime = 'edge'
```
## When to Use Each
### Node.js Runtime (Default)
- Full Node.js API support
- File system access (`fs`)
- Full `crypto` support
- Database connections
- Most npm packages work
### Edge Runtime
- Only for specific edge-location latency requirements
- Limited API (no `fs`, limited `crypto`)
- Smaller cold start
- Geographic distribution needs
## Detection
**Before adding `runtime = 'edge'`**, check:
1. Does the project already use Edge runtime?
2. Is there a specific latency requirement?
3. Are all dependencies Edge-compatible?
If unsure, use Node.js runtime.

View File

@ -0,0 +1,141 @@
# Scripts
Loading third-party scripts in Next.js.
## Use next/script
Always use `next/script` instead of native `<script>` tags for better performance.
```tsx
// Bad: Native script tag
<script src="https://example.com/script.js"></script>
// Good: Next.js Script component
import Script from 'next/script'
<Script src="https://example.com/script.js" />
```
## Inline Scripts Need ID
Inline scripts require an `id` attribute for Next.js to track them.
```tsx
// Bad: Missing id
<Script dangerouslySetInnerHTML={{ __html: 'console.log("hi")' }} />
// Good: Has id
<Script id="my-script" dangerouslySetInnerHTML={{ __html: 'console.log("hi")' }} />
// Good: Inline with id
<Script id="show-banner">
{`document.getElementById('banner').classList.remove('hidden')`}
</Script>
```
## Don't Put Script in Head
`next/script` should not be placed inside `next/head`. It handles its own positioning.
```tsx
// Bad: Script inside Head
import Head from 'next/head'
import Script from 'next/script'
<Head>
<Script src="/analytics.js" />
</Head>
// Good: Script outside Head
<Head>
<title>Page</title>
</Head>
<Script src="/analytics.js" />
```
## Loading Strategies
```tsx
// afterInteractive (default) - Load after page is interactive
<Script src="/analytics.js" strategy="afterInteractive" />
// lazyOnload - Load during idle time
<Script src="/widget.js" strategy="lazyOnload" />
// beforeInteractive - Load before page is interactive (use sparingly)
// Only works in app/layout.tsx or pages/_document.js
<Script src="/critical.js" strategy="beforeInteractive" />
// worker - Load in web worker (experimental)
<Script src="/heavy.js" strategy="worker" />
```
## Google Analytics
Use `@next/third-parties` instead of inline GA scripts.
```tsx
// Bad: Inline GA script
<Script src="https://www.googletagmanager.com/gtag/js?id=G-XXXXX" />
<Script id="ga-init">
{`window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-XXXXX');`}
</Script>
// Good: Next.js component
import { GoogleAnalytics } from '@next/third-parties/google'
export default function Layout({ children }) {
return (
<html>
<body>{children}</body>
<GoogleAnalytics gaId="G-XXXXX" />
</html>
)
}
```
## Google Tag Manager
```tsx
import { GoogleTagManager } from '@next/third-parties/google'
export default function Layout({ children }) {
return (
<html>
<GoogleTagManager gtmId="GTM-XXXXX" />
<body>{children}</body>
</html>
)
}
```
## Other Third-Party Scripts
```tsx
// YouTube embed
import { YouTubeEmbed } from '@next/third-parties/google'
<YouTubeEmbed videoid="dQw4w9WgXcQ" />
// Google Maps
import { GoogleMapsEmbed } from '@next/third-parties/google'
<GoogleMapsEmbed
apiKey="YOUR_API_KEY"
mode="place"
q="Brooklyn+Bridge,New+York,NY"
/>
```
## Quick Reference
| Pattern | Issue | Fix |
|---------|-------|-----|
| `<script src="...">` | No optimization | Use `next/script` |
| `<Script>` without id | Can't track inline scripts | Add `id` attribute |
| `<Script>` inside `<Head>` | Wrong placement | Move outside Head |
| Inline GA/GTM scripts | No optimization | Use `@next/third-parties` |
| `strategy="beforeInteractive"` outside layout | Won't work | Only use in root layout |

View File

@ -0,0 +1,371 @@
# Self-Hosting Next.js
Deploy Next.js outside of Vercel with confidence.
## Quick Start: Standalone Output
For Docker or any containerized deployment, use standalone output:
```js
// next.config.js
module.exports = {
output: 'standalone',
};
```
This creates a minimal `standalone` folder with only production dependencies:
```
.next/
├── standalone/
│ ├── server.js # Entry point
│ ├── node_modules/ # Only production deps
│ └── .next/ # Build output
└── static/ # Must be copied separately
```
## Docker Deployment
### Dockerfile
```dockerfile
FROM node:20-alpine AS base
# Install dependencies
FROM base AS deps
WORKDIR /app
COPY package.json package-lock.json* ./
RUN npm ci
# Build
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build
# Production
FROM base AS runner
WORKDIR /app
ENV NODE_ENV=production
# Create non-root user
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
# Copy standalone output
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
COPY --from=builder /app/public ./public
USER nextjs
EXPOSE 3000
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"
CMD ["node", "server.js"]
```
### Docker Compose
```yaml
version: '3.8'
services:
web:
build: .
ports:
- "3000:3000"
environment:
- NODE_ENV=production
restart: unless-stopped
healthcheck:
test: ["CMD", "wget", "-q", "--spider", "http://localhost:3000/api/health"]
interval: 30s
timeout: 10s
retries: 3
```
## PM2 Deployment
For traditional server deployments:
```js
// ecosystem.config.js
module.exports = {
apps: [{
name: 'nextjs',
script: '.next/standalone/server.js',
instances: 'max',
exec_mode: 'cluster',
env: {
NODE_ENV: 'production',
PORT: 3000,
},
}],
};
```
```bash
npm run build
pm2 start ecosystem.config.js
```
## ISR and Cache Handlers
### The Problem
ISR (Incremental Static Regeneration) uses filesystem caching by default. This **breaks with multiple instances**:
- Instance A regenerates page → saves to its local disk
- Instance B serves stale page → doesn't see Instance A's cache
- Load balancer sends users to random instances → inconsistent content
### Solution: Custom Cache Handler
Next.js 14+ supports custom cache handlers for shared storage:
```js
// next.config.js
module.exports = {
cacheHandler: require.resolve('./cache-handler.js'),
cacheMaxMemorySize: 0, // Disable in-memory cache
};
```
#### Redis Cache Handler Example
```js
// cache-handler.js
const Redis = require('ioredis');
const redis = new Redis(process.env.REDIS_URL);
const CACHE_PREFIX = 'nextjs:';
module.exports = class CacheHandler {
constructor(options) {
this.options = options;
}
async get(key) {
const data = await redis.get(CACHE_PREFIX + key);
if (!data) return null;
const parsed = JSON.parse(data);
return {
value: parsed.value,
lastModified: parsed.lastModified,
};
}
async set(key, data, ctx) {
const cacheData = {
value: data,
lastModified: Date.now(),
};
// Set TTL based on revalidate option
if (ctx?.revalidate) {
await redis.setex(
CACHE_PREFIX + key,
ctx.revalidate,
JSON.stringify(cacheData)
);
} else {
await redis.set(CACHE_PREFIX + key, JSON.stringify(cacheData));
}
}
async revalidateTag(tags) {
// Implement tag-based invalidation
// This requires tracking which keys have which tags
}
};
```
#### S3 Cache Handler Example
```js
// cache-handler.js
const { S3Client, GetObjectCommand, PutObjectCommand } = require('@aws-sdk/client-s3');
const s3 = new S3Client({ region: process.env.AWS_REGION });
const BUCKET = process.env.CACHE_BUCKET;
module.exports = class CacheHandler {
async get(key) {
try {
const response = await s3.send(new GetObjectCommand({
Bucket: BUCKET,
Key: `cache/${key}`,
}));
const body = await response.Body.transformToString();
return JSON.parse(body);
} catch (err) {
if (err.name === 'NoSuchKey') return null;
throw err;
}
}
async set(key, data, ctx) {
await s3.send(new PutObjectCommand({
Bucket: BUCKET,
Key: `cache/${key}`,
Body: JSON.stringify({
value: data,
lastModified: Date.now(),
}),
ContentType: 'application/json',
}));
}
};
```
## What Works vs What Needs Setup
| Feature | Single Instance | Multi-Instance | Notes |
|---------|----------------|----------------|-------|
| SSR | Yes | Yes | No special setup |
| SSG | Yes | Yes | Built at deploy time |
| ISR | Yes | Needs cache handler | Filesystem cache breaks |
| Image Optimization | Yes | Yes | CPU-intensive, consider CDN |
| Middleware | Yes | Yes | Runs on Node.js |
| Edge Runtime | Limited | Limited | Some features Node-only |
| `revalidatePath/Tag` | Yes | Needs cache handler | Must share cache |
| `next/font` | Yes | Yes | Fonts bundled at build |
| Draft Mode | Yes | Yes | Cookie-based |
## Image Optimization
Next.js Image Optimization works out of the box but is CPU-intensive.
### Option 1: Built-in (Simple)
Works automatically, but consider:
- Set `deviceSizes` and `imageSizes` in config to limit variants
- Use `minimumCacheTTL` to reduce regeneration
```js
// next.config.js
module.exports = {
images: {
minimumCacheTTL: 60 * 60 * 24, // 24 hours
deviceSizes: [640, 750, 1080, 1920], // Limit sizes
},
};
```
### Option 2: External Loader (Recommended for Scale)
Offload to Cloudinary, Imgix, or similar:
```js
// next.config.js
module.exports = {
images: {
loader: 'custom',
loaderFile: './lib/image-loader.js',
},
};
```
```js
// lib/image-loader.js
export default function cloudinaryLoader({ src, width, quality }) {
const params = ['f_auto', 'c_limit', `w_${width}`, `q_${quality || 'auto'}`];
return `https://res.cloudinary.com/demo/image/upload/${params.join(',')}${src}`;
}
```
## Environment Variables
### Build-time vs Runtime
```js
// Available at build time only (baked into bundle)
NEXT_PUBLIC_API_URL=https://api.example.com
// Available at runtime (server-side only)
DATABASE_URL=postgresql://...
API_SECRET=...
```
### Runtime Configuration
For truly dynamic config, don't use `NEXT_PUBLIC_*`. Instead:
```tsx
// app/api/config/route.ts
export async function GET() {
return Response.json({
apiUrl: process.env.API_URL,
features: process.env.FEATURES?.split(','),
});
}
```
## OpenNext: Serverless Without Vercel
[OpenNext](https://open-next.js.org/) adapts Next.js for AWS Lambda, Cloudflare Workers, etc.
```bash
npx create-sst@latest
# or
npx @opennextjs/aws build
```
Supports:
- AWS Lambda + CloudFront
- Cloudflare Workers
- Netlify Functions
- Deno Deploy
## Health Check Endpoint
Always include a health check for load balancers:
```tsx
// app/api/health/route.ts
export async function GET() {
try {
// Optional: check database connection
// await db.$queryRaw`SELECT 1`;
return Response.json({ status: 'healthy' }, { status: 200 });
} catch (error) {
return Response.json({ status: 'unhealthy' }, { status: 503 });
}
}
```
## Pre-Deployment Checklist
1. **Build locally first**: `npm run build` - catch errors before deploy
2. **Test standalone output**: `node .next/standalone/server.js`
3. **Set `output: 'standalone'`** for Docker
4. **Configure cache handler** for multi-instance ISR
5. **Set `HOSTNAME="0.0.0.0"`** for containers
6. **Copy `public/` and `.next/static/`** - not included in standalone
7. **Add health check endpoint**
8. **Test ISR revalidation** after deployment
9. **Monitor memory usage** - Node.js defaults may need tuning
## Testing Cache Handler
**Critical**: Test your cache handler on every Next.js upgrade:
```bash
# Start multiple instances
PORT=3001 node .next/standalone/server.js &
PORT=3002 node .next/standalone/server.js &
# Trigger ISR revalidation
curl http://localhost:3001/api/revalidate?path=/posts
# Verify both instances see the update
curl http://localhost:3001/posts
curl http://localhost:3002/posts
# Should return identical content
```

View File

@ -0,0 +1,67 @@
# Suspense Boundaries
Client hooks that cause CSR bailout without Suspense boundaries.
## useSearchParams
Always requires Suspense boundary in static routes. Without it, the entire page becomes client-side rendered.
```tsx
// Bad: Entire page becomes CSR
'use client'
import { useSearchParams } from 'next/navigation'
export default function SearchBar() {
const searchParams = useSearchParams()
return <div>Query: {searchParams.get('q')}</div>
}
```
```tsx
// Good: Wrap in Suspense
import { Suspense } from 'react'
import SearchBar from './search-bar'
export default function Page() {
return (
<Suspense fallback={<div>Loading...</div>}>
<SearchBar />
</Suspense>
)
}
```
## usePathname
Requires Suspense boundary when route has dynamic parameters.
```tsx
// In dynamic route [slug]
// Bad: No Suspense
'use client'
import { usePathname } from 'next/navigation'
export function Breadcrumb() {
const pathname = usePathname()
return <nav>{pathname}</nav>
}
```
```tsx
// Good: Wrap in Suspense
<Suspense fallback={<BreadcrumbSkeleton />}>
<Breadcrumb />
</Suspense>
```
If you use `generateStaticParams`, Suspense is optional.
## Quick Reference
| Hook | Suspense Required |
|------|-------------------|
| `useSearchParams()` | Yes |
| `usePathname()` | Yes (dynamic routes) |
| `useParams()` | No |
| `useRouter()` | No |

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,123 @@
# React Best Practices
A structured repository for creating and maintaining React Best Practices optimized for agents and LLMs.
## Structure
- `rules/` - Individual rule files (one per rule)
- `_sections.md` - Section metadata (titles, impacts, descriptions)
- `_template.md` - Template for creating new rules
- `area-description.md` - Individual rule files
- `src/` - Build scripts and utilities
- `metadata.json` - Document metadata (version, organization, abstract)
- __`AGENTS.md`__ - Compiled output (generated)
- __`test-cases.json`__ - Test cases for LLM evaluation (generated)
## Getting Started
1. Install dependencies:
```bash
pnpm install
```
2. Build AGENTS.md from rules:
```bash
pnpm build
```
3. Validate rule files:
```bash
pnpm validate
```
4. Extract test cases:
```bash
pnpm extract-tests
```
## Creating a New Rule
1. Copy `rules/_template.md` to `rules/area-description.md`
2. Choose the appropriate area prefix:
- `async-` for Eliminating Waterfalls (Section 1)
- `bundle-` for Bundle Size Optimization (Section 2)
- `server-` for Server-Side Performance (Section 3)
- `client-` for Client-Side Data Fetching (Section 4)
- `rerender-` for Re-render Optimization (Section 5)
- `rendering-` for Rendering Performance (Section 6)
- `js-` for JavaScript Performance (Section 7)
- `advanced-` for Advanced Patterns (Section 8)
3. Fill in the frontmatter and content
4. Ensure you have clear examples with explanations
5. Run `pnpm build` to regenerate AGENTS.md and test-cases.json
## Rule File Structure
Each rule file should follow this structure:
```markdown
---
title: Rule Title Here
impact: MEDIUM
impactDescription: Optional description
tags: tag1, tag2, tag3
---
## Rule Title Here
Brief explanation of the rule and why it matters.
**Incorrect (description of what's wrong):**
```typescript
// Bad code example
```
**Correct (description of what's right):**
```typescript
// Good code example
```
Optional explanatory text after examples.
Reference: [Link](https://example.com)
## File Naming Convention
- Files starting with `_` are special (excluded from build)
- Rule files: `area-description.md` (e.g., `async-parallel.md`)
- Section is automatically inferred from filename prefix
- Rules are sorted alphabetically by title within each section
- IDs (e.g., 1.1, 1.2) are auto-generated during build
## Impact Levels
- `CRITICAL` - Highest priority, major performance gains
- `HIGH` - Significant performance improvements
- `MEDIUM-HIGH` - Moderate-high gains
- `MEDIUM` - Moderate performance improvements
- `LOW-MEDIUM` - Low-medium gains
- `LOW` - Incremental improvements
## Scripts
- `pnpm build` - Compile rules into AGENTS.md
- `pnpm validate` - Validate all rule files
- `pnpm extract-tests` - Extract test cases for LLM evaluation
- `pnpm dev` - Build and validate
## Contributing
When adding or modifying rules:
1. Use the correct filename prefix for your section
2. Follow the `_template.md` structure
3. Include clear bad/good examples with explanations
4. Add appropriate tags
5. Run `pnpm build` to regenerate AGENTS.md and test-cases.json
6. Rules are automatically sorted by title - no need to manage numbers!
## Acknowledgments
Originally created by [@shuding](https://x.com/shuding) at [Vercel](https://vercel.com).

View File

@ -0,0 +1,149 @@
---
name: vercel-react-best-practices
description: React and Next.js performance optimization guidelines from Vercel Engineering. This skill should be used when writing, reviewing, or refactoring React/Next.js code to ensure optimal performance patterns. Triggers on tasks involving React components, Next.js pages, data fetching, bundle optimization, or performance improvements.
license: MIT
metadata:
author: vercel
version: "1.0.0"
---
# Vercel React Best Practices
Comprehensive performance optimization guide for React and Next.js applications, maintained by Vercel. Contains 70 rules across 8 categories, prioritized by impact to guide automated refactoring and code generation.
## When to Apply
Reference these guidelines when:
- Writing new React components or Next.js pages
- Implementing data fetching (client or server-side)
- Reviewing code for performance issues
- Refactoring existing React/Next.js code
- Optimizing bundle size or load times
## Rule Categories by Priority
| Priority | Category | Impact | Prefix |
|----------|----------|--------|--------|
| 1 | Eliminating Waterfalls | CRITICAL | `async-` |
| 2 | Bundle Size Optimization | CRITICAL | `bundle-` |
| 3 | Server-Side Performance | HIGH | `server-` |
| 4 | Client-Side Data Fetching | MEDIUM-HIGH | `client-` |
| 5 | Re-render Optimization | MEDIUM | `rerender-` |
| 6 | Rendering Performance | MEDIUM | `rendering-` |
| 7 | JavaScript Performance | LOW-MEDIUM | `js-` |
| 8 | Advanced Patterns | LOW | `advanced-` |
## Quick Reference
### 1. Eliminating Waterfalls (CRITICAL)
- `async-cheap-condition-before-await` - Check cheap sync conditions before awaiting flags or remote values
- `async-defer-await` - Move await into branches where actually used
- `async-parallel` - Use Promise.all() for independent operations
- `async-dependencies` - Use better-all for partial dependencies
- `async-api-routes` - Start promises early, await late in API routes
- `async-suspense-boundaries` - Use Suspense to stream content
### 2. Bundle Size Optimization (CRITICAL)
- `bundle-barrel-imports` - Import directly, avoid barrel files
- `bundle-analyzable-paths` - Prefer statically analyzable import and file-system paths to avoid broad bundles and traces
- `bundle-dynamic-imports` - Use next/dynamic for heavy components
- `bundle-defer-third-party` - Load analytics/logging after hydration
- `bundle-conditional` - Load modules only when feature is activated
- `bundle-preload` - Preload on hover/focus for perceived speed
### 3. Server-Side Performance (HIGH)
- `server-auth-actions` - Authenticate server actions like API routes
- `server-cache-react` - Use React.cache() for per-request deduplication
- `server-cache-lru` - Use LRU cache for cross-request caching
- `server-dedup-props` - Avoid duplicate serialization in RSC props
- `server-hoist-static-io` - Hoist static I/O (fonts, logos) to module level
- `server-no-shared-module-state` - Avoid module-level mutable request state in RSC/SSR
- `server-serialization` - Minimize data passed to client components
- `server-parallel-fetching` - Restructure components to parallelize fetches
- `server-parallel-nested-fetching` - Chain nested fetches per item in Promise.all
- `server-after-nonblocking` - Use after() for non-blocking operations
### 4. Client-Side Data Fetching (MEDIUM-HIGH)
- `client-swr-dedup` - Use SWR for automatic request deduplication
- `client-event-listeners` - Deduplicate global event listeners
- `client-passive-event-listeners` - Use passive listeners for scroll
- `client-localstorage-schema` - Version and minimize localStorage data
### 5. Re-render Optimization (MEDIUM)
- `rerender-defer-reads` - Don't subscribe to state only used in callbacks
- `rerender-memo` - Extract expensive work into memoized components
- `rerender-memo-with-default-value` - Hoist default non-primitive props
- `rerender-dependencies` - Use primitive dependencies in effects
- `rerender-derived-state` - Subscribe to derived booleans, not raw values
- `rerender-derived-state-no-effect` - Derive state during render, not effects
- `rerender-functional-setstate` - Use functional setState for stable callbacks
- `rerender-lazy-state-init` - Pass function to useState for expensive values
- `rerender-simple-expression-in-memo` - Avoid memo for simple primitives
- `rerender-split-combined-hooks` - Split hooks with independent dependencies
- `rerender-move-effect-to-event` - Put interaction logic in event handlers
- `rerender-transitions` - Use startTransition for non-urgent updates
- `rerender-use-deferred-value` - Defer expensive renders to keep input responsive
- `rerender-use-ref-transient-values` - Use refs for transient frequent values
- `rerender-no-inline-components` - Don't define components inside components
### 6. Rendering Performance (MEDIUM)
- `rendering-animate-svg-wrapper` - Animate div wrapper, not SVG element
- `rendering-content-visibility` - Use content-visibility for long lists
- `rendering-hoist-jsx` - Extract static JSX outside components
- `rendering-svg-precision` - Reduce SVG coordinate precision
- `rendering-hydration-no-flicker` - Use inline script for client-only data
- `rendering-hydration-suppress-warning` - Suppress expected mismatches
- `rendering-activity` - Use Activity component for show/hide
- `rendering-conditional-render` - Use ternary, not && for conditionals
- `rendering-usetransition-loading` - Prefer useTransition for loading state
- `rendering-resource-hints` - Use React DOM resource hints for preloading
- `rendering-script-defer-async` - Use defer or async on script tags
### 7. JavaScript Performance (LOW-MEDIUM)
- `js-batch-dom-css` - Group CSS changes via classes or cssText
- `js-index-maps` - Build Map for repeated lookups
- `js-cache-property-access` - Cache object properties in loops
- `js-cache-function-results` - Cache function results in module-level Map
- `js-cache-storage` - Cache localStorage/sessionStorage reads
- `js-combine-iterations` - Combine multiple filter/map into one loop
- `js-length-check-first` - Check array length before expensive comparison
- `js-early-exit` - Return early from functions
- `js-hoist-regexp` - Hoist RegExp creation outside loops
- `js-min-max-loop` - Use loop for min/max instead of sort
- `js-set-map-lookups` - Use Set/Map for O(1) lookups
- `js-tosorted-immutable` - Use toSorted() for immutability
- `js-flatmap-filter` - Use flatMap to map and filter in one pass
- `js-request-idle-callback` - Defer non-critical work to browser idle time
### 8. Advanced Patterns (LOW)
- `advanced-effect-event-deps` - Don't put `useEffectEvent` results in effect deps
- `advanced-event-handler-refs` - Store event handlers in refs
- `advanced-init-once` - Initialize app once per app load
- `advanced-use-latest` - useLatest for stable callback refs
## How to Use
Read individual rule files for detailed explanations and code examples:
```
rules/async-parallel.md
rules/bundle-barrel-imports.md
```
Each rule file contains:
- Brief explanation of why it matters
- Incorrect code example with explanation
- Correct code example with explanation
- Additional context and references
## Full Compiled Document
For the complete guide with all rules expanded: `AGENTS.md`

View File

@ -0,0 +1,46 @@
# Sections
This file defines all sections, their ordering, impact levels, and descriptions.
The section ID (in parentheses) is the filename prefix used to group rules.
---
## 1. Eliminating Waterfalls (async)
**Impact:** CRITICAL
**Description:** Waterfalls are the #1 performance killer. Each sequential await adds full network latency. Eliminating them yields the largest gains.
## 2. Bundle Size Optimization (bundle)
**Impact:** CRITICAL
**Description:** Reducing initial bundle size improves Time to Interactive and Largest Contentful Paint.
## 3. Server-Side Performance (server)
**Impact:** HIGH
**Description:** Optimizing server-side rendering and data fetching eliminates server-side waterfalls and reduces response times.
## 4. Client-Side Data Fetching (client)
**Impact:** MEDIUM-HIGH
**Description:** Automatic deduplication and efficient data fetching patterns reduce redundant network requests.
## 5. Re-render Optimization (rerender)
**Impact:** MEDIUM
**Description:** Reducing unnecessary re-renders minimizes wasted computation and improves UI responsiveness.
## 6. Rendering Performance (rendering)
**Impact:** MEDIUM
**Description:** Optimizing the rendering process reduces the work the browser needs to do.
## 7. JavaScript Performance (js)
**Impact:** LOW-MEDIUM
**Description:** Micro-optimizations for hot paths can add up to meaningful improvements.
## 8. Advanced Patterns (advanced)
**Impact:** LOW
**Description:** Advanced patterns for specific cases that require careful implementation.

View File

@ -0,0 +1,28 @@
---
title: Rule Title Here
impact: MEDIUM
impactDescription: Optional description of impact (e.g., "20-50% improvement")
tags: tag1, tag2
---
## Rule Title Here
**Impact: MEDIUM (optional impact description)**
Brief explanation of the rule and why it matters. This should be clear and concise, explaining the performance implications.
**Incorrect (description of what's wrong):**
```typescript
// Bad code example here
const bad = example()
```
**Correct (description of what's right):**
```typescript
// Good code example here
const good = example()
```
Reference: [Link to documentation or resource](https://example.com)

View File

@ -0,0 +1,56 @@
---
title: Do Not Put Effect Events in Dependency Arrays
impact: LOW
impactDescription: avoids unnecessary effect re-runs and lint errors
tags: advanced, hooks, useEffectEvent, dependencies, effects
---
## Do Not Put Effect Events in Dependency Arrays
Effect Event functions do not have a stable identity. Their identity intentionally changes on every render. Do not include the function returned by `useEffectEvent` in a `useEffect` dependency array. Keep the actual reactive values as dependencies and call the Effect Event from inside the effect body or subscriptions created by that effect.
**Incorrect (Effect Event added as a dependency):**
```tsx
import { useEffect, useEffectEvent } from 'react'
function ChatRoom({ roomId, onConnected }: {
roomId: string
onConnected: () => void
}) {
const handleConnected = useEffectEvent(onConnected)
useEffect(() => {
const connection = createConnection(roomId)
connection.on('connected', handleConnected)
connection.connect()
return () => connection.disconnect()
}, [roomId, handleConnected])
}
```
Including the Effect Event in dependencies makes the effect re-run every render and triggers the React Hooks lint rule.
**Correct (depend on reactive values, not the Effect Event):**
```tsx
import { useEffect, useEffectEvent } from 'react'
function ChatRoom({ roomId, onConnected }: {
roomId: string
onConnected: () => void
}) {
const handleConnected = useEffectEvent(onConnected)
useEffect(() => {
const connection = createConnection(roomId)
connection.on('connected', handleConnected)
connection.connect()
return () => connection.disconnect()
}, [roomId])
}
```
Reference: [React useEffectEvent: Effect Event in deps](https://react.dev/reference/react/useEffectEvent#effect-event-in-deps)

View File

@ -0,0 +1,55 @@
---
title: Store Event Handlers in Refs
impact: LOW
impactDescription: stable subscriptions
tags: advanced, hooks, refs, event-handlers, optimization
---
## Store Event Handlers in Refs
Store callbacks in refs when used in effects that shouldn't re-subscribe on callback changes.
**Incorrect (re-subscribes on every render):**
```tsx
function useWindowEvent(event: string, handler: (e) => void) {
useEffect(() => {
window.addEventListener(event, handler)
return () => window.removeEventListener(event, handler)
}, [event, handler])
}
```
**Correct (stable subscription):**
```tsx
function useWindowEvent(event: string, handler: (e) => void) {
const handlerRef = useRef(handler)
useEffect(() => {
handlerRef.current = handler
}, [handler])
useEffect(() => {
const listener = (e) => handlerRef.current(e)
window.addEventListener(event, listener)
return () => window.removeEventListener(event, listener)
}, [event])
}
```
**Alternative: use `useEffectEvent` if you're on latest React:**
```tsx
import { useEffectEvent } from 'react'
function useWindowEvent(event: string, handler: (e) => void) {
const onEvent = useEffectEvent(handler)
useEffect(() => {
window.addEventListener(event, onEvent)
return () => window.removeEventListener(event, onEvent)
}, [event])
}
```
`useEffectEvent` provides a cleaner API for the same pattern: it creates a stable function reference that always calls the latest version of the handler.

View File

@ -0,0 +1,42 @@
---
title: Initialize App Once, Not Per Mount
impact: LOW-MEDIUM
impactDescription: avoids duplicate init in development
tags: initialization, useEffect, app-startup, side-effects
---
## Initialize App Once, Not Per Mount
Do not put app-wide initialization that must run once per app load inside `useEffect([])` of a component. Components can remount and effects will re-run. Use a module-level guard or top-level init in the entry module instead.
**Incorrect (runs twice in dev, re-runs on remount):**
```tsx
function Comp() {
useEffect(() => {
loadFromStorage()
checkAuthToken()
}, [])
// ...
}
```
**Correct (once per app load):**
```tsx
let didInit = false
function Comp() {
useEffect(() => {
if (didInit) return
didInit = true
loadFromStorage()
checkAuthToken()
}, [])
// ...
}
```
Reference: [Initializing the application](https://react.dev/learn/you-might-not-need-an-effect#initializing-the-application)

View File

@ -0,0 +1,39 @@
---
title: useEffectEvent for Stable Callback Refs
impact: LOW
impactDescription: prevents effect re-runs
tags: advanced, hooks, useEffectEvent, refs, optimization
---
## useEffectEvent for Stable Callback Refs
Access latest values in callbacks without adding them to dependency arrays. Prevents effect re-runs while avoiding stale closures.
**Incorrect (effect re-runs on every callback change):**
```tsx
function SearchInput({ onSearch }: { onSearch: (q: string) => void }) {
const [query, setQuery] = useState('')
useEffect(() => {
const timeout = setTimeout(() => onSearch(query), 300)
return () => clearTimeout(timeout)
}, [query, onSearch])
}
```
**Correct (using React's useEffectEvent):**
```tsx
import { useEffectEvent } from 'react';
function SearchInput({ onSearch }: { onSearch: (q: string) => void }) {
const [query, setQuery] = useState('')
const onSearchEvent = useEffectEvent(onSearch)
useEffect(() => {
const timeout = setTimeout(() => onSearchEvent(query), 300)
return () => clearTimeout(timeout)
}, [query])
}
```

View File

@ -0,0 +1,38 @@
---
title: Prevent Waterfall Chains in API Routes
impact: CRITICAL
impactDescription: 2-10× improvement
tags: api-routes, server-actions, waterfalls, parallelization
---
## Prevent Waterfall Chains in API Routes
In API routes and Server Actions, start independent operations immediately, even if you don't await them yet.
**Incorrect (config waits for auth, data waits for both):**
```typescript
export async function GET(request: Request) {
const session = await auth()
const config = await fetchConfig()
const data = await fetchData(session.user.id)
return Response.json({ data, config })
}
```
**Correct (auth and config start immediately):**
```typescript
export async function GET(request: Request) {
const sessionPromise = auth()
const configPromise = fetchConfig()
const session = await sessionPromise
const [config, data] = await Promise.all([
configPromise,
fetchData(session.user.id)
])
return Response.json({ data, config })
}
```
For operations with more complex dependency chains, use `better-all` to automatically maximize parallelism (see Dependency-Based Parallelization).

View File

@ -0,0 +1,37 @@
---
title: Check Cheap Conditions Before Async Flags
impact: HIGH
impactDescription: avoids unnecessary async work when a synchronous guard already fails
tags: async, await, feature-flags, short-circuit, conditional
---
## Check Cheap Conditions Before Async Flags
When a branch uses `await` for a flag or remote value and also requires a **cheap synchronous** condition (local props, request metadata, already-loaded state), evaluate the cheap condition **first**. Otherwise you pay for the async call even when the compound condition can never be true.
This is a specialization of [Defer Await Until Needed](./async-defer-await.md) for `flag && cheapCondition` style checks.
**Incorrect:**
```typescript
const someFlag = await getFlag()
if (someFlag && someCondition) {
// ...
}
```
**Correct:**
```typescript
if (someCondition) {
const someFlag = await getFlag()
if (someFlag) {
// ...
}
}
```
This matters when `getFlag` hits the network, a feature-flag service, or `React.cache` / DB work: skipping it when `someCondition` is false removes that cost on the cold path.
Keep the original order if `someCondition` is expensive, depends on the flag, or you must run side effects in a fixed order.

View File

@ -0,0 +1,82 @@
---
title: Defer Await Until Needed
impact: HIGH
impactDescription: avoids blocking unused code paths
tags: async, await, conditional, optimization
---
## Defer Await Until Needed
Move `await` operations into the branches where they're actually used to avoid blocking code paths that don't need them.
**Incorrect (blocks both branches):**
```typescript
async function handleRequest(userId: string, skipProcessing: boolean) {
const userData = await fetchUserData(userId)
if (skipProcessing) {
// Returns immediately but still waited for userData
return { skipped: true }
}
// Only this branch uses userData
return processUserData(userData)
}
```
**Correct (only blocks when needed):**
```typescript
async function handleRequest(userId: string, skipProcessing: boolean) {
if (skipProcessing) {
// Returns immediately without waiting
return { skipped: true }
}
// Fetch only when needed
const userData = await fetchUserData(userId)
return processUserData(userData)
}
```
**Another example (early return optimization):**
```typescript
// Incorrect: always fetches permissions
async function updateResource(resourceId: string, userId: string) {
const permissions = await fetchPermissions(userId)
const resource = await getResource(resourceId)
if (!resource) {
return { error: 'Not found' }
}
if (!permissions.canEdit) {
return { error: 'Forbidden' }
}
return await updateResourceData(resource, permissions)
}
// Correct: fetches only when needed
async function updateResource(resourceId: string, userId: string) {
const resource = await getResource(resourceId)
if (!resource) {
return { error: 'Not found' }
}
const permissions = await fetchPermissions(userId)
if (!permissions.canEdit) {
return { error: 'Forbidden' }
}
return await updateResourceData(resource, permissions)
}
```
This optimization is especially valuable when the skipped branch is frequently taken, or when the deferred operation is expensive.
For `await getFlag()` combined with a cheap synchronous guard (`flag && someCondition`), see [Check Cheap Conditions Before Async Flags](./async-cheap-condition-before-await.md).

View File

@ -0,0 +1,51 @@
---
title: Dependency-Based Parallelization
impact: CRITICAL
impactDescription: 2-10× improvement
tags: async, parallelization, dependencies, better-all
---
## Dependency-Based Parallelization
For operations with partial dependencies, use `better-all` to maximize parallelism. It automatically starts each task at the earliest possible moment.
**Incorrect (profile waits for config unnecessarily):**
```typescript
const [user, config] = await Promise.all([
fetchUser(),
fetchConfig()
])
const profile = await fetchProfile(user.id)
```
**Correct (config and profile run in parallel):**
```typescript
import { all } from 'better-all'
const { user, config, profile } = await all({
async user() { return fetchUser() },
async config() { return fetchConfig() },
async profile() {
return fetchProfile((await this.$.user).id)
}
})
```
**Alternative without extra dependencies:**
We can also create all the promises first, and do `Promise.all()` at the end.
```typescript
const userPromise = fetchUser()
const profilePromise = userPromise.then(user => fetchProfile(user.id))
const [user, config, profile] = await Promise.all([
userPromise,
fetchConfig(),
profilePromise
])
```
Reference: [https://github.com/shuding/better-all](https://github.com/shuding/better-all)

View File

@ -0,0 +1,28 @@
---
title: Promise.all() for Independent Operations
impact: CRITICAL
impactDescription: 2-10× improvement
tags: async, parallelization, promises, waterfalls
---
## Promise.all() for Independent Operations
When async operations have no interdependencies, execute them concurrently using `Promise.all()`.
**Incorrect (sequential execution, 3 round trips):**
```typescript
const user = await fetchUser()
const posts = await fetchPosts()
const comments = await fetchComments()
```
**Correct (parallel execution, 1 round trip):**
```typescript
const [user, posts, comments] = await Promise.all([
fetchUser(),
fetchPosts(),
fetchComments()
])
```

View File

@ -0,0 +1,99 @@
---
title: Strategic Suspense Boundaries
impact: HIGH
impactDescription: faster initial paint
tags: async, suspense, streaming, layout-shift
---
## Strategic Suspense Boundaries
Instead of awaiting data in async components before returning JSX, use Suspense boundaries to show the wrapper UI faster while data loads.
**Incorrect (wrapper blocked by data fetching):**
```tsx
async function Page() {
const data = await fetchData() // Blocks entire page
return (
<div>
<div>Sidebar</div>
<div>Header</div>
<div>
<DataDisplay data={data} />
</div>
<div>Footer</div>
</div>
)
}
```
The entire layout waits for data even though only the middle section needs it.
**Correct (wrapper shows immediately, data streams in):**
```tsx
function Page() {
return (
<div>
<div>Sidebar</div>
<div>Header</div>
<div>
<Suspense fallback={<Skeleton />}>
<DataDisplay />
</Suspense>
</div>
<div>Footer</div>
</div>
)
}
async function DataDisplay() {
const data = await fetchData() // Only blocks this component
return <div>{data.content}</div>
}
```
Sidebar, Header, and Footer render immediately. Only DataDisplay waits for data.
**Alternative (share promise across components):**
```tsx
function Page() {
// Start fetch immediately, but don't await
const dataPromise = fetchData()
return (
<div>
<div>Sidebar</div>
<div>Header</div>
<Suspense fallback={<Skeleton />}>
<DataDisplay dataPromise={dataPromise} />
<DataSummary dataPromise={dataPromise} />
</Suspense>
<div>Footer</div>
</div>
)
}
function DataDisplay({ dataPromise }: { dataPromise: Promise<Data> }) {
const data = use(dataPromise) // Unwraps the promise
return <div>{data.content}</div>
}
function DataSummary({ dataPromise }: { dataPromise: Promise<Data> }) {
const data = use(dataPromise) // Reuses the same promise
return <div>{data.summary}</div>
}
```
Both components share the same promise, so only one fetch occurs. Layout renders immediately while both components wait together.
**When NOT to use this pattern:**
- Critical data needed for layout decisions (affects positioning)
- SEO-critical content above the fold
- Small, fast queries where suspense overhead isn't worth it
- When you want to avoid layout shift (loading → content jump)
**Trade-off:** Faster initial paint vs potential layout shift. Choose based on your UX priorities.

View File

@ -0,0 +1,63 @@
---
title: Prefer Statically Analyzable Paths
impact: HIGH
impactDescription: avoids accidental broad bundles and file traces
tags: bundle, nextjs, vite, webpack, rollup, esbuild, path
---
## Prefer Statically Analyzable Paths
Build tools work best when import and file-system paths are obvious at build time. If you hide the real path inside a variable or compose it too dynamically, the tool either has to include a broad set of possible files, warn that it cannot analyze the import, or widen file tracing to stay safe.
Prefer explicit maps or literal paths so the set of reachable files stays narrow and predictable. This is the same rule whether you are choosing modules with `import()` or reading files in server/build code.
When analysis becomes too broad, the cost is real:
- Larger server bundles
- Slower builds
- Worse cold starts
- More memory use
### Import Paths
**Incorrect (the bundler cannot tell what may be imported):**
```ts
const PAGE_MODULES = {
home: './pages/home',
settings: './pages/settings',
} as const
const Page = await import(PAGE_MODULES[pageName])
```
**Correct (use an explicit map of allowed modules):**
```ts
const PAGE_MODULES = {
home: () => import('./pages/home'),
settings: () => import('./pages/settings'),
} as const
const Page = await PAGE_MODULES[pageName]()
```
### File-System Paths
**Incorrect (a 2-value enum still hides the final path from static analysis):**
```ts
const baseDir = path.join(process.cwd(), 'content/' + contentKind)
```
**Correct (make each final path literal at the callsite):**
```ts
const baseDir =
kind === ContentKind.Blog
? path.join(process.cwd(), 'content/blog')
: path.join(process.cwd(), 'content/docs')
```
In Next.js server code, this matters for output file tracing too. `path.join(process.cwd(), someVar)` can widen the traced file set because Next.js statically analyze `import`, `require`, and `fs` usage.
Reference: [Next.js output](https://nextjs.org/docs/app/api-reference/config/next-config-js/output), [Next.js dynamic imports](https://nextjs.org/learn/seo/dynamic-imports), [Vite features](https://vite.dev/guide/features.html), [esbuild API](https://esbuild.github.io/api/), [Rollup dynamic import vars](https://www.npmjs.com/package/@rollup/plugin-dynamic-import-vars), [Webpack dependency management](https://webpack.js.org/guides/dependency-management/)

View File

@ -0,0 +1,60 @@
---
title: Avoid Barrel File Imports
impact: CRITICAL
impactDescription: 200-800ms import cost, slow builds
tags: bundle, imports, tree-shaking, barrel-files, performance
---
## Avoid Barrel File Imports
Import directly from source files instead of barrel files to avoid loading thousands of unused modules. **Barrel files** are entry points that re-export multiple modules (e.g., `index.js` that does `export * from './module'`).
Popular icon and component libraries can have **up to 10,000 re-exports** in their entry file. For many React packages, **it takes 200-800ms just to import them**, affecting both development speed and production cold starts.
**Why tree-shaking doesn't help:** When a library is marked as external (not bundled), the bundler can't optimize it. If you bundle it to enable tree-shaking, builds become substantially slower analyzing the entire module graph.
**Incorrect (imports entire library):**
```tsx
import { Check, X, Menu } from 'lucide-react'
// Loads 1,583 modules, takes ~2.8s extra in dev
// Runtime cost: 200-800ms on every cold start
import { Button, TextField } from '@mui/material'
// Loads 2,225 modules, takes ~4.2s extra in dev
```
**Correct - Next.js 13.5+ (recommended):**
```js
// next.config.js - automatically optimizes barrel imports at build time
module.exports = {
experimental: {
optimizePackageImports: ['lucide-react', '@mui/material']
}
}
```
```tsx
// Keep the standard imports - Next.js transforms them to direct imports
import { Check, X, Menu } from 'lucide-react'
// Full TypeScript support, no manual path wrangling
```
This is the recommended approach because it preserves TypeScript type safety and editor autocompletion while still eliminating the barrel import cost.
**Correct - Direct imports (non-Next.js projects):**
```tsx
import Button from '@mui/material/Button'
import TextField from '@mui/material/TextField'
// Loads only what you use
```
> **TypeScript warning:** Some libraries (notably `lucide-react`) don't ship `.d.ts` files for their deep import paths. Importing from `lucide-react/dist/esm/icons/check` resolves to an implicit `any` type, causing errors under `strict` or `noImplicitAny`. Prefer `optimizePackageImports` when available, or verify the library exports types for its subpaths before using direct imports.
These optimizations provide 15-70% faster dev boot, 28% faster builds, 40% faster cold starts, and significantly faster HMR.
Libraries commonly affected: `lucide-react`, `@mui/material`, `@mui/icons-material`, `@tabler/icons-react`, `react-icons`, `@headlessui/react`, `@radix-ui/react-*`, `lodash`, `ramda`, `date-fns`, `rxjs`, `react-use`.
Reference: [How we optimized package imports in Next.js](https://vercel.com/blog/how-we-optimized-package-imports-in-next-js)

View File

@ -0,0 +1,31 @@
---
title: Conditional Module Loading
impact: HIGH
impactDescription: loads large data only when needed
tags: bundle, conditional-loading, lazy-loading
---
## Conditional Module Loading
Load large data or modules only when a feature is activated.
**Example (lazy-load animation frames):**
```tsx
function AnimationPlayer({ enabled, setEnabled }: { enabled: boolean; setEnabled: React.Dispatch<React.SetStateAction<boolean>> }) {
const [frames, setFrames] = useState<Frame[] | null>(null)
useEffect(() => {
if (enabled && !frames && typeof window !== 'undefined') {
import('./animation-frames.js')
.then(mod => setFrames(mod.frames))
.catch(() => setEnabled(false))
}
}, [enabled, frames, setEnabled])
if (!frames) return <Skeleton />
return <Canvas frames={frames} />
}
```
The `typeof window !== 'undefined'` check prevents bundling this module for SSR, optimizing server bundle size and build speed.

View File

@ -0,0 +1,49 @@
---
title: Defer Non-Critical Third-Party Libraries
impact: MEDIUM
impactDescription: loads after hydration
tags: bundle, third-party, analytics, defer
---
## Defer Non-Critical Third-Party Libraries
Analytics, logging, and error tracking don't block user interaction. Load them after hydration.
**Incorrect (blocks initial bundle):**
```tsx
import { Analytics } from '@vercel/analytics/react'
export default function RootLayout({ children }) {
return (
<html>
<body>
{children}
<Analytics />
</body>
</html>
)
}
```
**Correct (loads after hydration):**
```tsx
import dynamic from 'next/dynamic'
const Analytics = dynamic(
() => import('@vercel/analytics/react').then(m => m.Analytics),
{ ssr: false }
)
export default function RootLayout({ children }) {
return (
<html>
<body>
{children}
<Analytics />
</body>
</html>
)
}
```

View File

@ -0,0 +1,35 @@
---
title: Dynamic Imports for Heavy Components
impact: CRITICAL
impactDescription: directly affects TTI and LCP
tags: bundle, dynamic-import, code-splitting, next-dynamic
---
## Dynamic Imports for Heavy Components
Use `next/dynamic` to lazy-load large components not needed on initial render.
**Incorrect (Monaco bundles with main chunk ~300KB):**
```tsx
import { MonacoEditor } from './monaco-editor'
function CodePanel({ code }: { code: string }) {
return <MonacoEditor value={code} />
}
```
**Correct (Monaco loads on demand):**
```tsx
import dynamic from 'next/dynamic'
const MonacoEditor = dynamic(
() => import('./monaco-editor').then(m => m.MonacoEditor),
{ ssr: false }
)
function CodePanel({ code }: { code: string }) {
return <MonacoEditor value={code} />
}
```

View File

@ -0,0 +1,50 @@
---
title: Preload Based on User Intent
impact: MEDIUM
impactDescription: reduces perceived latency
tags: bundle, preload, user-intent, hover
---
## Preload Based on User Intent
Preload heavy bundles before they're needed to reduce perceived latency.
**Example (preload on hover/focus):**
```tsx
function EditorButton({ onClick }: { onClick: () => void }) {
const preload = () => {
if (typeof window !== 'undefined') {
void import('./monaco-editor')
}
}
return (
<button
onMouseEnter={preload}
onFocus={preload}
onClick={onClick}
>
Open Editor
</button>
)
}
```
**Example (preload when feature flag is enabled):**
```tsx
function FlagsProvider({ children, flags }: Props) {
useEffect(() => {
if (flags.editorEnabled && typeof window !== 'undefined') {
void import('./monaco-editor').then(mod => mod.init())
}
}, [flags.editorEnabled])
return <FlagsContext.Provider value={flags}>
{children}
</FlagsContext.Provider>
}
```
The `typeof window !== 'undefined'` check prevents bundling preloaded modules for SSR, optimizing server bundle size and build speed.

View File

@ -0,0 +1,74 @@
---
title: Deduplicate Global Event Listeners
impact: LOW
impactDescription: single listener for N components
tags: client, swr, event-listeners, subscription
---
## Deduplicate Global Event Listeners
Use `useSWRSubscription()` to share global event listeners across component instances.
**Incorrect (N instances = N listeners):**
```tsx
function useKeyboardShortcut(key: string, callback: () => void) {
useEffect(() => {
const handler = (e: KeyboardEvent) => {
if (e.metaKey && e.key === key) {
callback()
}
}
window.addEventListener('keydown', handler)
return () => window.removeEventListener('keydown', handler)
}, [key, callback])
}
```
When using the `useKeyboardShortcut` hook multiple times, each instance will register a new listener.
**Correct (N instances = 1 listener):**
```tsx
import useSWRSubscription from 'swr/subscription'
// Module-level Map to track callbacks per key
const keyCallbacks = new Map<string, Set<() => void>>()
function useKeyboardShortcut(key: string, callback: () => void) {
// Register this callback in the Map
useEffect(() => {
if (!keyCallbacks.has(key)) {
keyCallbacks.set(key, new Set())
}
keyCallbacks.get(key)!.add(callback)
return () => {
const set = keyCallbacks.get(key)
if (set) {
set.delete(callback)
if (set.size === 0) {
keyCallbacks.delete(key)
}
}
}
}, [key, callback])
useSWRSubscription('global-keydown', () => {
const handler = (e: KeyboardEvent) => {
if (e.metaKey && keyCallbacks.has(e.key)) {
keyCallbacks.get(e.key)!.forEach(cb => cb())
}
}
window.addEventListener('keydown', handler)
return () => window.removeEventListener('keydown', handler)
})
}
function Profile() {
// Multiple shortcuts will share the same listener
useKeyboardShortcut('p', () => { /* ... */ })
useKeyboardShortcut('k', () => { /* ... */ })
// ...
}
```

View File

@ -0,0 +1,71 @@
---
title: Version and Minimize localStorage Data
impact: MEDIUM
impactDescription: prevents schema conflicts, reduces storage size
tags: client, localStorage, storage, versioning, data-minimization
---
## Version and Minimize localStorage Data
Add version prefix to keys and store only needed fields. Prevents schema conflicts and accidental storage of sensitive data.
**Incorrect:**
```typescript
// No version, stores everything, no error handling
localStorage.setItem('userConfig', JSON.stringify(fullUserObject))
const data = localStorage.getItem('userConfig')
```
**Correct:**
```typescript
const VERSION = 'v2'
function saveConfig(config: { theme: string; language: string }) {
try {
localStorage.setItem(`userConfig:${VERSION}`, JSON.stringify(config))
} catch {
// Throws in incognito/private browsing, quota exceeded, or disabled
}
}
function loadConfig() {
try {
const data = localStorage.getItem(`userConfig:${VERSION}`)
return data ? JSON.parse(data) : null
} catch {
return null
}
}
// Migration from v1 to v2
function migrate() {
try {
const v1 = localStorage.getItem('userConfig:v1')
if (v1) {
const old = JSON.parse(v1)
saveConfig({ theme: old.darkMode ? 'dark' : 'light', language: old.lang })
localStorage.removeItem('userConfig:v1')
}
} catch {}
}
```
**Store minimal fields from server responses:**
```typescript
// User object has 20+ fields, only store what UI needs
function cachePrefs(user: FullUser) {
try {
localStorage.setItem('prefs:v1', JSON.stringify({
theme: user.preferences.theme,
notifications: user.preferences.notifications
}))
} catch {}
}
```
**Always wrap in try-catch:** `getItem()` and `setItem()` throw in incognito/private browsing (Safari, Firefox), when quota exceeded, or when disabled.
**Benefits:** Schema evolution via versioning, reduced storage size, prevents storing tokens/PII/internal flags.

View File

@ -0,0 +1,48 @@
---
title: Use Passive Event Listeners for Scrolling Performance
impact: MEDIUM
impactDescription: eliminates scroll delay caused by event listeners
tags: client, event-listeners, scrolling, performance, touch, wheel
---
## Use Passive Event Listeners for Scrolling Performance
Add `{ passive: true }` to touch and wheel event listeners to enable immediate scrolling. Browsers normally wait for listeners to finish to check if `preventDefault()` is called, causing scroll delay.
**Incorrect:**
```typescript
useEffect(() => {
const handleTouch = (e: TouchEvent) => console.log(e.touches[0].clientX)
const handleWheel = (e: WheelEvent) => console.log(e.deltaY)
document.addEventListener('touchstart', handleTouch)
document.addEventListener('wheel', handleWheel)
return () => {
document.removeEventListener('touchstart', handleTouch)
document.removeEventListener('wheel', handleWheel)
}
}, [])
```
**Correct:**
```typescript
useEffect(() => {
const handleTouch = (e: TouchEvent) => console.log(e.touches[0].clientX)
const handleWheel = (e: WheelEvent) => console.log(e.deltaY)
document.addEventListener('touchstart', handleTouch, { passive: true })
document.addEventListener('wheel', handleWheel, { passive: true })
return () => {
document.removeEventListener('touchstart', handleTouch)
document.removeEventListener('wheel', handleWheel)
}
}, [])
```
**Use passive when:** tracking/analytics, logging, any listener that doesn't call `preventDefault()`.
**Don't use passive when:** implementing custom swipe gestures, custom zoom controls, or any listener that needs `preventDefault()`.

View File

@ -0,0 +1,56 @@
---
title: Use SWR for Automatic Deduplication
impact: MEDIUM-HIGH
impactDescription: automatic deduplication
tags: client, swr, deduplication, data-fetching
---
## Use SWR for Automatic Deduplication
SWR enables request deduplication, caching, and revalidation across component instances.
**Incorrect (no deduplication, each instance fetches):**
```tsx
function UserList() {
const [users, setUsers] = useState([])
useEffect(() => {
fetch('/api/users')
.then(r => r.json())
.then(setUsers)
}, [])
}
```
**Correct (multiple instances share one request):**
```tsx
import useSWR from 'swr'
function UserList() {
const { data: users } = useSWR('/api/users', fetcher)
}
```
**For immutable data:**
```tsx
import { useImmutableSWR } from '@/lib/swr'
function StaticContent() {
const { data } = useImmutableSWR('/api/config', fetcher)
}
```
**For mutations:**
```tsx
import { useSWRMutation } from 'swr/mutation'
function UpdateButton() {
const { trigger } = useSWRMutation('/api/user', updateUser)
return <button onClick={() => trigger()}>Update</button>
}
```
Reference: [https://swr.vercel.app](https://swr.vercel.app)

View File

@ -0,0 +1,107 @@
---
title: Avoid Layout Thrashing
impact: MEDIUM
impactDescription: prevents forced synchronous layouts and reduces performance bottlenecks
tags: javascript, dom, css, performance, reflow, layout-thrashing
---
## Avoid Layout Thrashing
Avoid interleaving style writes with layout reads. When you read a layout property (like `offsetWidth`, `getBoundingClientRect()`, or `getComputedStyle()`) between style changes, the browser is forced to trigger a synchronous reflow.
**This is OK (browser batches style changes):**
```typescript
function updateElementStyles(element: HTMLElement) {
// Each line invalidates style, but browser batches the recalculation
element.style.width = '100px'
element.style.height = '200px'
element.style.backgroundColor = 'blue'
element.style.border = '1px solid black'
}
```
**Incorrect (interleaved reads and writes force reflows):**
```typescript
function layoutThrashing(element: HTMLElement) {
element.style.width = '100px'
const width = element.offsetWidth // Forces reflow
element.style.height = '200px'
const height = element.offsetHeight // Forces another reflow
}
```
**Correct (batch writes, then read once):**
```typescript
function updateElementStyles(element: HTMLElement) {
// Batch all writes together
element.style.width = '100px'
element.style.height = '200px'
element.style.backgroundColor = 'blue'
element.style.border = '1px solid black'
// Read after all writes are done (single reflow)
const { width, height } = element.getBoundingClientRect()
}
```
**Correct (batch reads, then writes):**
```typescript
function avoidThrashing(element: HTMLElement) {
// Read phase - all layout queries first
const rect1 = element.getBoundingClientRect()
const offsetWidth = element.offsetWidth
const offsetHeight = element.offsetHeight
// Write phase - all style changes after
element.style.width = '100px'
element.style.height = '200px'
}
```
**Better: use CSS classes**
```css
.highlighted-box {
width: 100px;
height: 200px;
background-color: blue;
border: 1px solid black;
}
```
```typescript
function updateElementStyles(element: HTMLElement) {
element.classList.add('highlighted-box')
const { width, height } = element.getBoundingClientRect()
}
```
**React example:**
```tsx
// Incorrect: interleaving style changes with layout queries
function Box({ isHighlighted }: { isHighlighted: boolean }) {
const ref = useRef<HTMLDivElement>(null)
useEffect(() => {
if (ref.current && isHighlighted) {
ref.current.style.width = '100px'
const width = ref.current.offsetWidth // Forces layout
ref.current.style.height = '200px'
}
}, [isHighlighted])
return <div ref={ref}>Content</div>
}
// Correct: toggle class
function Box({ isHighlighted }: { isHighlighted: boolean }) {
return (
<div className={isHighlighted ? 'highlighted-box' : ''}>
Content
</div>
)
}
```
Prefer CSS classes over inline styles when possible. CSS files are cached by the browser, and classes provide better separation of concerns and are easier to maintain.
See [this gist](https://gist.github.com/paulirish/5d52fb081b3570c81e3a) and [CSS Triggers](https://csstriggers.com/) for more information on layout-forcing operations.

View File

@ -0,0 +1,80 @@
---
title: Cache Repeated Function Calls
impact: MEDIUM
impactDescription: avoid redundant computation
tags: javascript, cache, memoization, performance
---
## Cache Repeated Function Calls
Use a module-level Map to cache function results when the same function is called repeatedly with the same inputs during render.
**Incorrect (redundant computation):**
```typescript
function ProjectList({ projects }: { projects: Project[] }) {
return (
<div>
{projects.map(project => {
// slugify() called 100+ times for same project names
const slug = slugify(project.name)
return <ProjectCard key={project.id} slug={slug} />
})}
</div>
)
}
```
**Correct (cached results):**
```typescript
// Module-level cache
const slugifyCache = new Map<string, string>()
function cachedSlugify(text: string): string {
if (slugifyCache.has(text)) {
return slugifyCache.get(text)!
}
const result = slugify(text)
slugifyCache.set(text, result)
return result
}
function ProjectList({ projects }: { projects: Project[] }) {
return (
<div>
{projects.map(project => {
// Computed only once per unique project name
const slug = cachedSlugify(project.name)
return <ProjectCard key={project.id} slug={slug} />
})}
</div>
)
}
```
**Simpler pattern for single-value functions:**
```typescript
let isLoggedInCache: boolean | null = null
function isLoggedIn(): boolean {
if (isLoggedInCache !== null) {
return isLoggedInCache
}
isLoggedInCache = document.cookie.includes('auth=')
return isLoggedInCache
}
// Clear cache when auth changes
function onAuthChange() {
isLoggedInCache = null
}
```
Use a Map (not a hook) so it works everywhere: utilities, event handlers, not just React components.
Reference: [How we made the Vercel Dashboard twice as fast](https://vercel.com/blog/how-we-made-the-vercel-dashboard-twice-as-fast)

View File

@ -0,0 +1,28 @@
---
title: Cache Property Access in Loops
impact: LOW-MEDIUM
impactDescription: reduces lookups
tags: javascript, loops, optimization, caching
---
## Cache Property Access in Loops
Cache object property lookups in hot paths.
**Incorrect (3 lookups × N iterations):**
```typescript
for (let i = 0; i < arr.length; i++) {
process(obj.config.settings.value)
}
```
**Correct (1 lookup total):**
```typescript
const value = obj.config.settings.value
const len = arr.length
for (let i = 0; i < len; i++) {
process(value)
}
```

View File

@ -0,0 +1,70 @@
---
title: Cache Storage API Calls
impact: LOW-MEDIUM
impactDescription: reduces expensive I/O
tags: javascript, localStorage, storage, caching, performance
---
## Cache Storage API Calls
`localStorage`, `sessionStorage`, and `document.cookie` are synchronous and expensive. Cache reads in memory.
**Incorrect (reads storage on every call):**
```typescript
function getTheme() {
return localStorage.getItem('theme') ?? 'light'
}
// Called 10 times = 10 storage reads
```
**Correct (Map cache):**
```typescript
const storageCache = new Map<string, string | null>()
function getLocalStorage(key: string) {
if (!storageCache.has(key)) {
storageCache.set(key, localStorage.getItem(key))
}
return storageCache.get(key)
}
function setLocalStorage(key: string, value: string) {
localStorage.setItem(key, value)
storageCache.set(key, value) // keep cache in sync
}
```
Use a Map (not a hook) so it works everywhere: utilities, event handlers, not just React components.
**Cookie caching:**
```typescript
let cookieCache: Record<string, string> | null = null
function getCookie(name: string) {
if (!cookieCache) {
cookieCache = Object.fromEntries(
document.cookie.split('; ').map(c => c.split('='))
)
}
return cookieCache[name]
}
```
**Important (invalidate on external changes):**
If storage can change externally (another tab, server-set cookies), invalidate cache:
```typescript
window.addEventListener('storage', (e) => {
if (e.key) storageCache.delete(e.key)
})
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'visible') {
storageCache.clear()
}
})
```

View File

@ -0,0 +1,32 @@
---
title: Combine Multiple Array Iterations
impact: LOW-MEDIUM
impactDescription: reduces iterations
tags: javascript, arrays, loops, performance
---
## Combine Multiple Array Iterations
Multiple `.filter()` or `.map()` calls iterate the array multiple times. Combine into one loop.
**Incorrect (3 iterations):**
```typescript
const admins = users.filter(u => u.isAdmin)
const testers = users.filter(u => u.isTester)
const inactive = users.filter(u => !u.isActive)
```
**Correct (1 iteration):**
```typescript
const admins: User[] = []
const testers: User[] = []
const inactive: User[] = []
for (const user of users) {
if (user.isAdmin) admins.push(user)
if (user.isTester) testers.push(user)
if (!user.isActive) inactive.push(user)
}
```

View File

@ -0,0 +1,50 @@
---
title: Early Return from Functions
impact: LOW-MEDIUM
impactDescription: avoids unnecessary computation
tags: javascript, functions, optimization, early-return
---
## Early Return from Functions
Return early when result is determined to skip unnecessary processing.
**Incorrect (processes all items even after finding answer):**
```typescript
function validateUsers(users: User[]) {
let hasError = false
let errorMessage = ''
for (const user of users) {
if (!user.email) {
hasError = true
errorMessage = 'Email required'
}
if (!user.name) {
hasError = true
errorMessage = 'Name required'
}
// Continues checking all users even after error found
}
return hasError ? { valid: false, error: errorMessage } : { valid: true }
}
```
**Correct (returns immediately on first error):**
```typescript
function validateUsers(users: User[]) {
for (const user of users) {
if (!user.email) {
return { valid: false, error: 'Email required' }
}
if (!user.name) {
return { valid: false, error: 'Name required' }
}
}
return { valid: true }
}
```

View File

@ -0,0 +1,60 @@
---
title: Use flatMap to Map and Filter in One Pass
impact: LOW-MEDIUM
impactDescription: eliminates intermediate array
tags: javascript, arrays, flatMap, filter, performance
---
## Use flatMap to Map and Filter in One Pass
**Impact: LOW-MEDIUM (eliminates intermediate array)**
Chaining `.map().filter(Boolean)` creates an intermediate array and iterates twice. Use `.flatMap()` to transform and filter in a single pass.
**Incorrect (2 iterations, intermediate array):**
```typescript
const userNames = users
.map(user => user.isActive ? user.name : null)
.filter(Boolean)
```
**Correct (1 iteration, no intermediate array):**
```typescript
const userNames = users.flatMap(user =>
user.isActive ? [user.name] : []
)
```
**More examples:**
```typescript
// Extract valid emails from responses
// Before
const emails = responses
.map(r => r.success ? r.data.email : null)
.filter(Boolean)
// After
const emails = responses.flatMap(r =>
r.success ? [r.data.email] : []
)
// Parse and filter valid numbers
// Before
const numbers = strings
.map(s => parseInt(s, 10))
.filter(n => !isNaN(n))
// After
const numbers = strings.flatMap(s => {
const n = parseInt(s, 10)
return isNaN(n) ? [] : [n]
})
```
**When to use:**
- Transforming items while filtering some out
- Conditional mapping where some inputs produce no output
- Parsing/validating where invalid inputs should be skipped

View File

@ -0,0 +1,45 @@
---
title: Hoist RegExp Creation
impact: LOW-MEDIUM
impactDescription: avoids recreation
tags: javascript, regexp, optimization, memoization
---
## Hoist RegExp Creation
Don't create RegExp inside render. Hoist to module scope or memoize with `useMemo()`.
**Incorrect (new RegExp every render):**
```tsx
function Highlighter({ text, query }: Props) {
const regex = new RegExp(`(${query})`, 'gi')
const parts = text.split(regex)
return <>{parts.map((part, i) => ...)}</>
}
```
**Correct (memoize or hoist):**
```tsx
const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
function Highlighter({ text, query }: Props) {
const regex = useMemo(
() => new RegExp(`(${escapeRegex(query)})`, 'gi'),
[query]
)
const parts = text.split(regex)
return <>{parts.map((part, i) => ...)}</>
}
```
**Warning (global regex has mutable state):**
Global regex (`/g`) has mutable `lastIndex` state:
```typescript
const regex = /foo/g
regex.test('foo') // true, lastIndex = 3
regex.test('foo') // false, lastIndex = 0
```

View File

@ -0,0 +1,37 @@
---
title: Build Index Maps for Repeated Lookups
impact: LOW-MEDIUM
impactDescription: 1M ops to 2K ops
tags: javascript, map, indexing, optimization, performance
---
## Build Index Maps for Repeated Lookups
Multiple `.find()` calls by the same key should use a Map.
**Incorrect (O(n) per lookup):**
```typescript
function processOrders(orders: Order[], users: User[]) {
return orders.map(order => ({
...order,
user: users.find(u => u.id === order.userId)
}))
}
```
**Correct (O(1) per lookup):**
```typescript
function processOrders(orders: Order[], users: User[]) {
const userById = new Map(users.map(u => [u.id, u]))
return orders.map(order => ({
...order,
user: userById.get(order.userId)
}))
}
```
Build map once (O(n)), then all lookups are O(1).
For 1000 orders × 1000 users: 1M ops → 2K ops.

View File

@ -0,0 +1,49 @@
---
title: Early Length Check for Array Comparisons
impact: MEDIUM-HIGH
impactDescription: avoids expensive operations when lengths differ
tags: javascript, arrays, performance, optimization, comparison
---
## Early Length Check for Array Comparisons
When comparing arrays with expensive operations (sorting, deep equality, serialization), check lengths first. If lengths differ, the arrays cannot be equal.
In real-world applications, this optimization is especially valuable when the comparison runs in hot paths (event handlers, render loops).
**Incorrect (always runs expensive comparison):**
```typescript
function hasChanges(current: string[], original: string[]) {
// Always sorts and joins, even when lengths differ
return current.sort().join() !== original.sort().join()
}
```
Two O(n log n) sorts run even when `current.length` is 5 and `original.length` is 100. There is also overhead of joining the arrays and comparing the strings.
**Correct (O(1) length check first):**
```typescript
function hasChanges(current: string[], original: string[]) {
// Early return if lengths differ
if (current.length !== original.length) {
return true
}
// Only sort when lengths match
const currentSorted = current.toSorted()
const originalSorted = original.toSorted()
for (let i = 0; i < currentSorted.length; i++) {
if (currentSorted[i] !== originalSorted[i]) {
return true
}
}
return false
}
```
This new approach is more efficient because:
- It avoids the overhead of sorting and joining the arrays when lengths differ
- It avoids consuming memory for the joined strings (especially important for large arrays)
- It avoids mutating the original arrays
- It returns early when a difference is found

View File

@ -0,0 +1,82 @@
---
title: Use Loop for Min/Max Instead of Sort
impact: LOW
impactDescription: O(n) instead of O(n log n)
tags: javascript, arrays, performance, sorting, algorithms
---
## Use Loop for Min/Max Instead of Sort
Finding the smallest or largest element only requires a single pass through the array. Sorting is wasteful and slower.
**Incorrect (O(n log n) - sort to find latest):**
```typescript
interface Project {
id: string
name: string
updatedAt: number
}
function getLatestProject(projects: Project[]) {
const sorted = [...projects].sort((a, b) => b.updatedAt - a.updatedAt)
return sorted[0]
}
```
Sorts the entire array just to find the maximum value.
**Incorrect (O(n log n) - sort for oldest and newest):**
```typescript
function getOldestAndNewest(projects: Project[]) {
const sorted = [...projects].sort((a, b) => a.updatedAt - b.updatedAt)
return { oldest: sorted[0], newest: sorted[sorted.length - 1] }
}
```
Still sorts unnecessarily when only min/max are needed.
**Correct (O(n) - single loop):**
```typescript
function getLatestProject(projects: Project[]) {
if (projects.length === 0) return null
let latest = projects[0]
for (let i = 1; i < projects.length; i++) {
if (projects[i].updatedAt > latest.updatedAt) {
latest = projects[i]
}
}
return latest
}
function getOldestAndNewest(projects: Project[]) {
if (projects.length === 0) return { oldest: null, newest: null }
let oldest = projects[0]
let newest = projects[0]
for (let i = 1; i < projects.length; i++) {
if (projects[i].updatedAt < oldest.updatedAt) oldest = projects[i]
if (projects[i].updatedAt > newest.updatedAt) newest = projects[i]
}
return { oldest, newest }
}
```
Single pass through the array, no copying, no sorting.
**Alternative (Math.min/Math.max for small arrays):**
```typescript
const numbers = [5, 2, 8, 1, 9]
const min = Math.min(...numbers)
const max = Math.max(...numbers)
```
This works for small arrays, but can be slower or just throw an error for very large arrays due to spread operator limitations. Maximal array length is approximately 124000 in Chrome 143 and 638000 in Safari 18; exact numbers may vary - see [the fiddle](https://jsfiddle.net/qw1jabsx/4/). Use the loop approach for reliability.

View File

@ -0,0 +1,105 @@
---
title: Defer Non-Critical Work with requestIdleCallback
impact: MEDIUM
impactDescription: keeps UI responsive during background tasks
tags: javascript, performance, idle, scheduling, analytics
---
## Defer Non-Critical Work with requestIdleCallback
**Impact: MEDIUM (keeps UI responsive during background tasks)**
Use `requestIdleCallback()` to schedule non-critical work during browser idle periods. This keeps the main thread free for user interactions and animations, reducing jank and improving perceived performance.
**Incorrect (blocks main thread during user interaction):**
```typescript
function handleSearch(query: string) {
const results = searchItems(query)
setResults(results)
// These block the main thread immediately
analytics.track('search', { query })
saveToRecentSearches(query)
prefetchTopResults(results.slice(0, 3))
}
```
**Correct (defers non-critical work to idle time):**
```typescript
function handleSearch(query: string) {
const results = searchItems(query)
setResults(results)
// Defer non-critical work to idle periods
requestIdleCallback(() => {
analytics.track('search', { query })
})
requestIdleCallback(() => {
saveToRecentSearches(query)
})
requestIdleCallback(() => {
prefetchTopResults(results.slice(0, 3))
})
}
```
**With timeout for required work:**
```typescript
// Ensure analytics fires within 2 seconds even if browser stays busy
requestIdleCallback(
() => analytics.track('page_view', { path: location.pathname }),
{ timeout: 2000 }
)
```
**Chunking large tasks:**
```typescript
function processLargeDataset(items: Item[]) {
let index = 0
function processChunk(deadline: IdleDeadline) {
// Process items while we have idle time (aim for <50ms chunks)
while (index < items.length && deadline.timeRemaining() > 0) {
processItem(items[index])
index++
}
// Schedule next chunk if more items remain
if (index < items.length) {
requestIdleCallback(processChunk)
}
}
requestIdleCallback(processChunk)
}
```
**With fallback for unsupported browsers:**
```typescript
const scheduleIdleWork = window.requestIdleCallback ?? ((cb: () => void) => setTimeout(cb, 1))
scheduleIdleWork(() => {
// Non-critical work
})
```
**When to use:**
- Analytics and telemetry
- Saving state to localStorage/IndexedDB
- Prefetching resources for likely next actions
- Processing non-urgent data transformations
- Lazy initialization of non-critical features
**When NOT to use:**
- User-initiated actions that need immediate feedback
- Rendering updates the user is waiting for
- Time-sensitive operations

View File

@ -0,0 +1,24 @@
---
title: Use Set/Map for O(1) Lookups
impact: LOW-MEDIUM
impactDescription: O(n) to O(1)
tags: javascript, set, map, data-structures, performance
---
## Use Set/Map for O(1) Lookups
Convert arrays to Set/Map for repeated membership checks.
**Incorrect (O(n) per check):**
```typescript
const allowedIds = ['a', 'b', 'c', ...]
items.filter(item => allowedIds.includes(item.id))
```
**Correct (O(1) per check):**
```typescript
const allowedIds = new Set(['a', 'b', 'c', ...])
items.filter(item => allowedIds.has(item.id))
```

View File

@ -0,0 +1,57 @@
---
title: Use toSorted() Instead of sort() for Immutability
impact: MEDIUM-HIGH
impactDescription: prevents mutation bugs in React state
tags: javascript, arrays, immutability, react, state, mutation
---
## Use toSorted() Instead of sort() for Immutability
`.sort()` mutates the array in place, which can cause bugs with React state and props. Use `.toSorted()` to create a new sorted array without mutation.
**Incorrect (mutates original array):**
```typescript
function UserList({ users }: { users: User[] }) {
// Mutates the users prop array!
const sorted = useMemo(
() => users.sort((a, b) => a.name.localeCompare(b.name)),
[users]
)
return <div>{sorted.map(renderUser)}</div>
}
```
**Correct (creates new array):**
```typescript
function UserList({ users }: { users: User[] }) {
// Creates new sorted array, original unchanged
const sorted = useMemo(
() => users.toSorted((a, b) => a.name.localeCompare(b.name)),
[users]
)
return <div>{sorted.map(renderUser)}</div>
}
```
**Why this matters in React:**
1. Props/state mutations break React's immutability model - React expects props and state to be treated as read-only
2. Causes stale closure bugs - Mutating arrays inside closures (callbacks, effects) can lead to unexpected behavior
**Browser support (fallback for older browsers):**
`.toSorted()` is available in all modern browsers (Chrome 110+, Safari 16+, Firefox 115+, Node.js 20+). For older environments, use spread operator:
```typescript
// Fallback for older browsers
const sorted = [...items].sort((a, b) => a.value - b.value)
```
**Other immutable array methods:**
- `.toSorted()` - immutable sort
- `.toReversed()` - immutable reverse
- `.toSpliced()` - immutable splice
- `.with()` - immutable element replacement

View File

@ -0,0 +1,26 @@
---
title: Use Activity Component for Show/Hide
impact: MEDIUM
impactDescription: preserves state/DOM
tags: rendering, activity, visibility, state-preservation
---
## Use Activity Component for Show/Hide
Use React's `<Activity>` to preserve state/DOM for expensive components that frequently toggle visibility.
**Usage:**
```tsx
import { Activity } from 'react'
function Dropdown({ isOpen }: Props) {
return (
<Activity mode={isOpen ? 'visible' : 'hidden'}>
<ExpensiveMenu />
</Activity>
)
}
```
Avoids expensive re-renders and state loss.

View File

@ -0,0 +1,47 @@
---
title: Animate SVG Wrapper Instead of SVG Element
impact: LOW
impactDescription: enables hardware acceleration
tags: rendering, svg, css, animation, performance
---
## Animate SVG Wrapper Instead of SVG Element
Many browsers don't have hardware acceleration for CSS3 animations on SVG elements. Wrap SVG in a `<div>` and animate the wrapper instead.
**Incorrect (animating SVG directly - no hardware acceleration):**
```tsx
function LoadingSpinner() {
return (
<svg
className="animate-spin"
width="24"
height="24"
viewBox="0 0 24 24"
>
<circle cx="12" cy="12" r="10" stroke="currentColor" />
</svg>
)
}
```
**Correct (animating wrapper div - hardware accelerated):**
```tsx
function LoadingSpinner() {
return (
<div className="animate-spin">
<svg
width="24"
height="24"
viewBox="0 0 24 24"
>
<circle cx="12" cy="12" r="10" stroke="currentColor" />
</svg>
</div>
)
}
```
This applies to all CSS transforms and transitions (`transform`, `opacity`, `translate`, `scale`, `rotate`). The wrapper div allows browsers to use GPU acceleration for smoother animations.

View File

@ -0,0 +1,40 @@
---
title: Use Explicit Conditional Rendering
impact: LOW
impactDescription: prevents rendering 0 or NaN
tags: rendering, conditional, jsx, falsy-values
---
## Use Explicit Conditional Rendering
Use explicit ternary operators (`? :`) instead of `&&` for conditional rendering when the condition can be `0`, `NaN`, or other falsy values that render.
**Incorrect (renders "0" when count is 0):**
```tsx
function Badge({ count }: { count: number }) {
return (
<div>
{count && <span className="badge">{count}</span>}
</div>
)
}
// When count = 0, renders: <div>0</div>
// When count = 5, renders: <div><span class="badge">5</span></div>
```
**Correct (renders nothing when count is 0):**
```tsx
function Badge({ count }: { count: number }) {
return (
<div>
{count > 0 ? <span className="badge">{count}</span> : null}
</div>
)
}
// When count = 0, renders: <div></div>
// When count = 5, renders: <div><span class="badge">5</span></div>
```

View File

@ -0,0 +1,38 @@
---
title: CSS content-visibility for Long Lists
impact: HIGH
impactDescription: faster initial render
tags: rendering, css, content-visibility, long-lists
---
## CSS content-visibility for Long Lists
Apply `content-visibility: auto` to defer off-screen rendering.
**CSS:**
```css
.message-item {
content-visibility: auto;
contain-intrinsic-size: 0 80px;
}
```
**Example:**
```tsx
function MessageList({ messages }: { messages: Message[] }) {
return (
<div className="overflow-y-auto h-screen">
{messages.map(msg => (
<div key={msg.id} className="message-item">
<Avatar user={msg.author} />
<div>{msg.content}</div>
</div>
))}
</div>
)
}
```
For 1000 messages, browser skips layout/paint for ~990 off-screen items (10× faster initial render).

View File

@ -0,0 +1,46 @@
---
title: Hoist Static JSX Elements
impact: LOW
impactDescription: avoids re-creation
tags: rendering, jsx, static, optimization
---
## Hoist Static JSX Elements
Extract static JSX outside components to avoid re-creation.
**Incorrect (recreates element every render):**
```tsx
function LoadingSkeleton() {
return <div className="animate-pulse h-20 bg-gray-200" />
}
function Container() {
return (
<div>
{loading && <LoadingSkeleton />}
</div>
)
}
```
**Correct (reuses same element):**
```tsx
const loadingSkeleton = (
<div className="animate-pulse h-20 bg-gray-200" />
)
function Container() {
return (
<div>
{loading && loadingSkeleton}
</div>
)
}
```
This is especially helpful for large and static SVG nodes, which can be expensive to recreate on every render.
**Note:** If your project has [React Compiler](https://react.dev/learn/react-compiler) enabled, the compiler automatically hoists static JSX elements and optimizes component re-renders, making manual hoisting unnecessary.

View File

@ -0,0 +1,82 @@
---
title: Prevent Hydration Mismatch Without Flickering
impact: MEDIUM
impactDescription: avoids visual flicker and hydration errors
tags: rendering, ssr, hydration, localStorage, flicker
---
## Prevent Hydration Mismatch Without Flickering
When rendering content that depends on client-side storage (localStorage, cookies), avoid both SSR breakage and post-hydration flickering by injecting a synchronous script that updates the DOM before React hydrates.
**Incorrect (breaks SSR):**
```tsx
function ThemeWrapper({ children }: { children: ReactNode }) {
// localStorage is not available on server - throws error
const theme = localStorage.getItem('theme') || 'light'
return (
<div className={theme}>
{children}
</div>
)
}
```
Server-side rendering will fail because `localStorage` is undefined.
**Incorrect (visual flickering):**
```tsx
function ThemeWrapper({ children }: { children: ReactNode }) {
const [theme, setTheme] = useState('light')
useEffect(() => {
// Runs after hydration - causes visible flash
const stored = localStorage.getItem('theme')
if (stored) {
setTheme(stored)
}
}, [])
return (
<div className={theme}>
{children}
</div>
)
}
```
Component first renders with default value (`light`), then updates after hydration, causing a visible flash of incorrect content.
**Correct (no flicker, no hydration mismatch):**
```tsx
function ThemeWrapper({ children }: { children: ReactNode }) {
return (
<>
<div id="theme-wrapper">
{children}
</div>
<script
dangerouslySetInnerHTML={{
__html: `
(function() {
try {
var theme = localStorage.getItem('theme') || 'light';
var el = document.getElementById('theme-wrapper');
if (el) el.className = theme;
} catch (e) {}
})();
`,
}}
/>
</>
)
}
```
The inline script executes synchronously before showing the element, ensuring the DOM already has the correct value. No flickering, no hydration mismatch.
This pattern is especially useful for theme toggles, user preferences, authentication states, and any client-only data that should render immediately without flashing default values.

View File

@ -0,0 +1,30 @@
---
title: Suppress Expected Hydration Mismatches
impact: LOW-MEDIUM
impactDescription: avoids noisy hydration warnings for known differences
tags: rendering, hydration, ssr, nextjs
---
## Suppress Expected Hydration Mismatches
In SSR frameworks (e.g., Next.js), some values are intentionally different on server vs client (random IDs, dates, locale/timezone formatting). For these *expected* mismatches, wrap the dynamic text in an element with `suppressHydrationWarning` to prevent noisy warnings. Do not use this to hide real bugs. Dont overuse it.
**Incorrect (known mismatch warnings):**
```tsx
function Timestamp() {
return <span>{new Date().toLocaleString()}</span>
}
```
**Correct (suppress expected mismatch only):**
```tsx
function Timestamp() {
return (
<span suppressHydrationWarning>
{new Date().toLocaleString()}
</span>
)
}
```

View File

@ -0,0 +1,85 @@
---
title: Use React DOM Resource Hints
impact: HIGH
impactDescription: reduces load time for critical resources
tags: rendering, preload, preconnect, prefetch, resource-hints
---
## Use React DOM Resource Hints
**Impact: HIGH (reduces load time for critical resources)**
React DOM provides APIs to hint the browser about resources it will need. These are especially useful in server components to start loading resources before the client even receives the HTML.
- **`prefetchDNS(href)`**: Resolve DNS for a domain you expect to connect to
- **`preconnect(href)`**: Establish connection (DNS + TCP + TLS) to a server
- **`preload(href, options)`**: Fetch a resource (stylesheet, font, script, image) you'll use soon
- **`preloadModule(href)`**: Fetch an ES module you'll use soon
- **`preinit(href, options)`**: Fetch and evaluate a stylesheet or script
- **`preinitModule(href)`**: Fetch and evaluate an ES module
**Example (preconnect to third-party APIs):**
```tsx
import { preconnect, prefetchDNS } from 'react-dom'
export default function App() {
prefetchDNS('https://analytics.example.com')
preconnect('https://api.example.com')
return <main>{/* content */}</main>
}
```
**Example (preload critical fonts and styles):**
```tsx
import { preload, preinit } from 'react-dom'
export default function RootLayout({ children }) {
// Preload font file
preload('/fonts/inter.woff2', { as: 'font', type: 'font/woff2', crossOrigin: 'anonymous' })
// Fetch and apply critical stylesheet immediately
preinit('/styles/critical.css', { as: 'style' })
return (
<html>
<body>{children}</body>
</html>
)
}
```
**Example (preload modules for code-split routes):**
```tsx
import { preloadModule, preinitModule } from 'react-dom'
function Navigation() {
const preloadDashboard = () => {
preloadModule('/dashboard.js', { as: 'script' })
}
return (
<nav>
<a href="/dashboard" onMouseEnter={preloadDashboard}>
Dashboard
</a>
</nav>
)
}
```
**When to use each:**
| API | Use case |
|-----|----------|
| `prefetchDNS` | Third-party domains you'll connect to later |
| `preconnect` | APIs or CDNs you'll fetch from immediately |
| `preload` | Critical resources needed for current page |
| `preloadModule` | JS modules for likely next navigation |
| `preinit` | Stylesheets/scripts that must execute early |
| `preinitModule` | ES modules that must execute early |
Reference: [React DOM Resource Preloading APIs](https://react.dev/reference/react-dom#resource-preloading-apis)

View File

@ -0,0 +1,68 @@
---
title: Use defer or async on Script Tags
impact: HIGH
impactDescription: eliminates render-blocking
tags: rendering, script, defer, async, performance
---
## Use defer or async on Script Tags
**Impact: HIGH (eliminates render-blocking)**
Script tags without `defer` or `async` block HTML parsing while the script downloads and executes. This delays First Contentful Paint and Time to Interactive.
- **`defer`**: Downloads in parallel, executes after HTML parsing completes, maintains execution order
- **`async`**: Downloads in parallel, executes immediately when ready, no guaranteed order
Use `defer` for scripts that depend on DOM or other scripts. Use `async` for independent scripts like analytics.
**Incorrect (blocks rendering):**
```tsx
export default function Document() {
return (
<html>
<head>
<script src="https://example.com/analytics.js" />
<script src="/scripts/utils.js" />
</head>
<body>{/* content */}</body>
</html>
)
}
```
**Correct (non-blocking):**
```tsx
export default function Document() {
return (
<html>
<head>
{/* Independent script - use async */}
<script src="https://example.com/analytics.js" async />
{/* DOM-dependent script - use defer */}
<script src="/scripts/utils.js" defer />
</head>
<body>{/* content */}</body>
</html>
)
}
```
**Note:** In Next.js, prefer the `next/script` component with `strategy` prop instead of raw script tags:
```tsx
import Script from 'next/script'
export default function Page() {
return (
<>
<Script src="https://example.com/analytics.js" strategy="afterInteractive" />
<Script src="/scripts/utils.js" strategy="beforeInteractive" />
</>
)
}
```
Reference: [MDN - Script element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#defer)

View File

@ -0,0 +1,28 @@
---
title: Optimize SVG Precision
impact: LOW
impactDescription: reduces file size
tags: rendering, svg, optimization, svgo
---
## Optimize SVG Precision
Reduce SVG coordinate precision to decrease file size. The optimal precision depends on the viewBox size, but in general reducing precision should be considered.
**Incorrect (excessive precision):**
```svg
<path d="M 10.293847 20.847362 L 30.938472 40.192837" />
```
**Correct (1 decimal place):**
```svg
<path d="M 10.3 20.8 L 30.9 40.2" />
```
**Automate with SVGO:**
```bash
npx svgo --precision=1 --multipass icon.svg
```

View File

@ -0,0 +1,75 @@
---
title: Use useTransition Over Manual Loading States
impact: LOW
impactDescription: reduces re-renders and improves code clarity
tags: rendering, transitions, useTransition, loading, state
---
## Use useTransition Over Manual Loading States
Use `useTransition` instead of manual `useState` for loading states. This provides built-in `isPending` state and automatically manages transitions.
**Incorrect (manual loading state):**
```tsx
function SearchResults() {
const [query, setQuery] = useState('')
const [results, setResults] = useState([])
const [isLoading, setIsLoading] = useState(false)
const handleSearch = async (value: string) => {
setIsLoading(true)
setQuery(value)
const data = await fetchResults(value)
setResults(data)
setIsLoading(false)
}
return (
<>
<input onChange={(e) => handleSearch(e.target.value)} />
{isLoading && <Spinner />}
<ResultsList results={results} />
</>
)
}
```
**Correct (useTransition with built-in pending state):**
```tsx
import { useTransition, useState } from 'react'
function SearchResults() {
const [query, setQuery] = useState('')
const [results, setResults] = useState([])
const [isPending, startTransition] = useTransition()
const handleSearch = (value: string) => {
setQuery(value) // Update input immediately
startTransition(async () => {
// Fetch and update results
const data = await fetchResults(value)
setResults(data)
})
}
return (
<>
<input onChange={(e) => handleSearch(e.target.value)} />
{isPending && <Spinner />}
<ResultsList results={results} />
</>
)
}
```
**Benefits:**
- **Automatic pending state**: No need to manually manage `setIsLoading(true/false)`
- **Error resilience**: Pending state correctly resets even if the transition throws
- **Better responsiveness**: Keeps the UI responsive during updates
- **Interrupt handling**: New transitions automatically cancel pending ones
Reference: [useTransition](https://react.dev/reference/react/useTransition)

View File

@ -0,0 +1,39 @@
---
title: Defer State Reads to Usage Point
impact: MEDIUM
impactDescription: avoids unnecessary subscriptions
tags: rerender, searchParams, localStorage, optimization
---
## Defer State Reads to Usage Point
Don't subscribe to dynamic state (searchParams, localStorage) if you only read it inside callbacks.
**Incorrect (subscribes to all searchParams changes):**
```tsx
function ShareButton({ chatId }: { chatId: string }) {
const searchParams = useSearchParams()
const handleShare = () => {
const ref = searchParams.get('ref')
shareChat(chatId, { ref })
}
return <button onClick={handleShare}>Share</button>
}
```
**Correct (reads on demand, no subscription):**
```tsx
function ShareButton({ chatId }: { chatId: string }) {
const handleShare = () => {
const params = new URLSearchParams(window.location.search)
const ref = params.get('ref')
shareChat(chatId, { ref })
}
return <button onClick={handleShare}>Share</button>
}
```

View File

@ -0,0 +1,45 @@
---
title: Narrow Effect Dependencies
impact: LOW
impactDescription: minimizes effect re-runs
tags: rerender, useEffect, dependencies, optimization
---
## Narrow Effect Dependencies
Specify primitive dependencies instead of objects to minimize effect re-runs.
**Incorrect (re-runs on any user field change):**
```tsx
useEffect(() => {
console.log(user.id)
}, [user])
```
**Correct (re-runs only when id changes):**
```tsx
useEffect(() => {
console.log(user.id)
}, [user.id])
```
**For derived state, compute outside effect:**
```tsx
// Incorrect: runs on width=767, 766, 765...
useEffect(() => {
if (width < 768) {
enableMobileMode()
}
}, [width])
// Correct: runs only on boolean transition
const isMobile = width < 768
useEffect(() => {
if (isMobile) {
enableMobileMode()
}
}, [isMobile])
```

View File

@ -0,0 +1,40 @@
---
title: Calculate Derived State During Rendering
impact: MEDIUM
impactDescription: avoids redundant renders and state drift
tags: rerender, derived-state, useEffect, state
---
## Calculate Derived State During Rendering
If a value can be computed from current props/state, do not store it in state or update it in an effect. Derive it during render to avoid extra renders and state drift. Do not set state in effects solely in response to prop changes; prefer derived values or keyed resets instead.
**Incorrect (redundant state and effect):**
```tsx
function Form() {
const [firstName, setFirstName] = useState('First')
const [lastName, setLastName] = useState('Last')
const [fullName, setFullName] = useState('')
useEffect(() => {
setFullName(firstName + ' ' + lastName)
}, [firstName, lastName])
return <p>{fullName}</p>
}
```
**Correct (derive during render):**
```tsx
function Form() {
const [firstName, setFirstName] = useState('First')
const [lastName, setLastName] = useState('Last')
const fullName = firstName + ' ' + lastName
return <p>{fullName}</p>
}
```
References: [You Might Not Need an Effect](https://react.dev/learn/you-might-not-need-an-effect)

View File

@ -0,0 +1,29 @@
---
title: Subscribe to Derived State
impact: MEDIUM
impactDescription: reduces re-render frequency
tags: rerender, derived-state, media-query, optimization
---
## Subscribe to Derived State
Subscribe to derived boolean state instead of continuous values to reduce re-render frequency.
**Incorrect (re-renders on every pixel change):**
```tsx
function Sidebar() {
const width = useWindowWidth() // updates continuously
const isMobile = width < 768
return <nav className={isMobile ? 'mobile' : 'desktop'} />
}
```
**Correct (re-renders only when boolean changes):**
```tsx
function Sidebar() {
const isMobile = useMediaQuery('(max-width: 767px)')
return <nav className={isMobile ? 'mobile' : 'desktop'} />
}
```

View File

@ -0,0 +1,74 @@
---
title: Use Functional setState Updates
impact: MEDIUM
impactDescription: prevents stale closures and unnecessary callback recreations
tags: react, hooks, useState, useCallback, callbacks, closures
---
## Use Functional setState Updates
When updating state based on the current state value, use the functional update form of setState instead of directly referencing the state variable. This prevents stale closures, eliminates unnecessary dependencies, and creates stable callback references.
**Incorrect (requires state as dependency):**
```tsx
function TodoList() {
const [items, setItems] = useState(initialItems)
// Callback must depend on items, recreated on every items change
const addItems = useCallback((newItems: Item[]) => {
setItems([...items, ...newItems])
}, [items]) // ❌ items dependency causes recreations
// Risk of stale closure if dependency is forgotten
const removeItem = useCallback((id: string) => {
setItems(items.filter(item => item.id !== id))
}, []) // ❌ Missing items dependency - will use stale items!
return <ItemsEditor items={items} onAdd={addItems} onRemove={removeItem} />
}
```
The first callback is recreated every time `items` changes, which can cause child components to re-render unnecessarily. The second callback has a stale closure bug—it will always reference the initial `items` value.
**Correct (stable callbacks, no stale closures):**
```tsx
function TodoList() {
const [items, setItems] = useState(initialItems)
// Stable callback, never recreated
const addItems = useCallback((newItems: Item[]) => {
setItems(curr => [...curr, ...newItems])
}, []) // ✅ No dependencies needed
// Always uses latest state, no stale closure risk
const removeItem = useCallback((id: string) => {
setItems(curr => curr.filter(item => item.id !== id))
}, []) // ✅ Safe and stable
return <ItemsEditor items={items} onAdd={addItems} onRemove={removeItem} />
}
```
**Benefits:**
1. **Stable callback references** - Callbacks don't need to be recreated when state changes
2. **No stale closures** - Always operates on the latest state value
3. **Fewer dependencies** - Simplifies dependency arrays and reduces memory leaks
4. **Prevents bugs** - Eliminates the most common source of React closure bugs
**When to use functional updates:**
- Any setState that depends on the current state value
- Inside useCallback/useMemo when state is needed
- Event handlers that reference state
- Async operations that update state
**When direct updates are fine:**
- Setting state to a static value: `setCount(0)`
- Setting state from props/arguments only: `setName(newName)`
- State doesn't depend on previous value
**Note:** If your project has [React Compiler](https://react.dev/learn/react-compiler) enabled, the compiler can automatically optimize some cases, but functional updates are still recommended for correctness and to prevent stale closure bugs.

View File

@ -0,0 +1,58 @@
---
title: Use Lazy State Initialization
impact: MEDIUM
impactDescription: wasted computation on every render
tags: react, hooks, useState, performance, initialization
---
## Use Lazy State Initialization
Pass a function to `useState` for expensive initial values. Without the function form, the initializer runs on every render even though the value is only used once.
**Incorrect (runs on every render):**
```tsx
function FilteredList({ items }: { items: Item[] }) {
// buildSearchIndex() runs on EVERY render, even after initialization
const [searchIndex, setSearchIndex] = useState(buildSearchIndex(items))
const [query, setQuery] = useState('')
// When query changes, buildSearchIndex runs again unnecessarily
return <SearchResults index={searchIndex} query={query} />
}
function UserProfile() {
// JSON.parse runs on every render
const [settings, setSettings] = useState(
JSON.parse(localStorage.getItem('settings') || '{}')
)
return <SettingsForm settings={settings} onChange={setSettings} />
}
```
**Correct (runs only once):**
```tsx
function FilteredList({ items }: { items: Item[] }) {
// buildSearchIndex() runs ONLY on initial render
const [searchIndex, setSearchIndex] = useState(() => buildSearchIndex(items))
const [query, setQuery] = useState('')
return <SearchResults index={searchIndex} query={query} />
}
function UserProfile() {
// JSON.parse runs only on initial render
const [settings, setSettings] = useState(() => {
const stored = localStorage.getItem('settings')
return stored ? JSON.parse(stored) : {}
})
return <SettingsForm settings={settings} onChange={setSettings} />
}
```
Use lazy initialization when computing initial values from localStorage/sessionStorage, building data structures (indexes, maps), reading from the DOM, or performing heavy transformations.
For simple primitives (`useState(0)`), direct references (`useState(props.value)`), or cheap literals (`useState({})`), the function form is unnecessary.

View File

@ -0,0 +1,38 @@
---
title: Extract Default Non-primitive Parameter Value from Memoized Component to Constant
impact: MEDIUM
impactDescription: restores memoization by using a constant for default value
tags: rerender, memo, optimization
---
## Extract Default Non-primitive Parameter Value from Memoized Component to Constant
When memoized component has a default value for some non-primitive optional parameter, such as an array, function, or object, calling the component without that parameter results in broken memoization. This is because new value instances are created on every rerender, and they do not pass strict equality comparison in `memo()`.
To address this issue, extract the default value into a constant.
**Incorrect (`onClick` has different values on every rerender):**
```tsx
const UserAvatar = memo(function UserAvatar({ onClick = () => {} }: { onClick?: () => void }) {
// ...
})
// Used without optional onClick
<UserAvatar />
```
**Correct (stable default value):**
```tsx
const NOOP = () => {};
const UserAvatar = memo(function UserAvatar({ onClick = NOOP }: { onClick?: () => void }) {
// ...
})
// Used without optional onClick
<UserAvatar />
```

View File

@ -0,0 +1,44 @@
---
title: Extract to Memoized Components
impact: MEDIUM
impactDescription: enables early returns
tags: rerender, memo, useMemo, optimization
---
## Extract to Memoized Components
Extract expensive work into memoized components to enable early returns before computation.
**Incorrect (computes avatar even when loading):**
```tsx
function Profile({ user, loading }: Props) {
const avatar = useMemo(() => {
const id = computeAvatarId(user)
return <Avatar id={id} />
}, [user])
if (loading) return <Skeleton />
return <div>{avatar}</div>
}
```
**Correct (skips computation when loading):**
```tsx
const UserAvatar = memo(function UserAvatar({ user }: { user: User }) {
const id = useMemo(() => computeAvatarId(user), [user])
return <Avatar id={id} />
})
function Profile({ user, loading }: Props) {
if (loading) return <Skeleton />
return (
<div>
<UserAvatar user={user} />
</div>
)
}
```
**Note:** If your project has [React Compiler](https://react.dev/learn/react-compiler) enabled, manual memoization with `memo()` and `useMemo()` is not necessary. The compiler automatically optimizes re-renders.

View File

@ -0,0 +1,45 @@
---
title: Put Interaction Logic in Event Handlers
impact: MEDIUM
impactDescription: avoids effect re-runs and duplicate side effects
tags: rerender, useEffect, events, side-effects, dependencies
---
## Put Interaction Logic in Event Handlers
If a side effect is triggered by a specific user action (submit, click, drag), run it in that event handler. Do not model the action as state + effect; it makes effects re-run on unrelated changes and can duplicate the action.
**Incorrect (event modeled as state + effect):**
```tsx
function Form() {
const [submitted, setSubmitted] = useState(false)
const theme = useContext(ThemeContext)
useEffect(() => {
if (submitted) {
post('/api/register')
showToast('Registered', theme)
}
}, [submitted, theme])
return <button onClick={() => setSubmitted(true)}>Submit</button>
}
```
**Correct (do it in the handler):**
```tsx
function Form() {
const theme = useContext(ThemeContext)
function handleSubmit() {
post('/api/register')
showToast('Registered', theme)
}
return <button onClick={handleSubmit}>Submit</button>
}
```
Reference: [Should this code move to an event handler?](https://react.dev/learn/removing-effect-dependencies#should-this-code-move-to-an-event-handler)

View File

@ -0,0 +1,82 @@
---
title: Don't Define Components Inside Components
impact: HIGH
impactDescription: prevents remount on every render
tags: rerender, components, remount, performance
---
## Don't Define Components Inside Components
**Impact: HIGH (prevents remount on every render)**
Defining a component inside another component creates a new component type on every render. React sees a different component each time and fully remounts it, destroying all state and DOM.
A common reason developers do this is to access parent variables without passing props. Always pass props instead.
**Incorrect (remounts on every render):**
```tsx
function UserProfile({ user, theme }) {
// Defined inside to access `theme` - BAD
const Avatar = () => (
<img
src={user.avatarUrl}
className={theme === 'dark' ? 'avatar-dark' : 'avatar-light'}
/>
)
// Defined inside to access `user` - BAD
const Stats = () => (
<div>
<span>{user.followers} followers</span>
<span>{user.posts} posts</span>
</div>
)
return (
<div>
<Avatar />
<Stats />
</div>
)
}
```
Every time `UserProfile` renders, `Avatar` and `Stats` are new component types. React unmounts the old instances and mounts new ones, losing any internal state, running effects again, and recreating DOM nodes.
**Correct (pass props instead):**
```tsx
function Avatar({ src, theme }: { src: string; theme: string }) {
return (
<img
src={src}
className={theme === 'dark' ? 'avatar-dark' : 'avatar-light'}
/>
)
}
function Stats({ followers, posts }: { followers: number; posts: number }) {
return (
<div>
<span>{followers} followers</span>
<span>{posts} posts</span>
</div>
)
}
function UserProfile({ user, theme }) {
return (
<div>
<Avatar src={user.avatarUrl} theme={theme} />
<Stats followers={user.followers} posts={user.posts} />
</div>
)
}
```
**Symptoms of this bug:**
- Input fields lose focus on every keystroke
- Animations restart unexpectedly
- `useEffect` cleanup/setup runs on every parent render
- Scroll position resets inside the component

View File

@ -0,0 +1,35 @@
---
title: Do not wrap a simple expression with a primitive result type in useMemo
impact: LOW-MEDIUM
impactDescription: wasted computation on every render
tags: rerender, useMemo, optimization
---
## Do not wrap a simple expression with a primitive result type in useMemo
When an expression is simple (few logical or arithmetical operators) and has a primitive result type (boolean, number, string), do not wrap it in `useMemo`.
Calling `useMemo` and comparing hook dependencies may consume more resources than the expression itself.
**Incorrect:**
```tsx
function Header({ user, notifications }: Props) {
const isLoading = useMemo(() => {
return user.isLoading || notifications.isLoading
}, [user.isLoading, notifications.isLoading])
if (isLoading) return <Skeleton />
// return some markup
}
```
**Correct:**
```tsx
function Header({ user, notifications }: Props) {
const isLoading = user.isLoading || notifications.isLoading
if (isLoading) return <Skeleton />
// return some markup
}
```

View File

@ -0,0 +1,64 @@
---
title: Split Combined Hook Computations
impact: MEDIUM
impactDescription: avoids recomputing independent steps
tags: rerender, useMemo, useEffect, dependencies, optimization
---
## Split Combined Hook Computations
When a hook contains multiple independent tasks with different dependencies, split them into separate hooks. A combined hook reruns all tasks when any dependency changes, even if some tasks don't use the changed value.
**Incorrect (changing `sortOrder` recomputes filtering):**
```tsx
const sortedProducts = useMemo(() => {
const filtered = products.filter((p) => p.category === category)
const sorted = filtered.toSorted((a, b) =>
sortOrder === "asc" ? a.price - b.price : b.price - a.price
)
return sorted
}, [products, category, sortOrder])
```
**Correct (filtering only recomputes when products or category change):**
```tsx
const filteredProducts = useMemo(
() => products.filter((p) => p.category === category),
[products, category]
)
const sortedProducts = useMemo(
() =>
filteredProducts.toSorted((a, b) =>
sortOrder === "asc" ? a.price - b.price : b.price - a.price
),
[filteredProducts, sortOrder]
)
```
This pattern also applies to `useEffect` when combining unrelated side effects:
**Incorrect (both effects run when either dependency changes):**
```tsx
useEffect(() => {
analytics.trackPageView(pathname)
document.title = `${pageTitle} | My App`
}, [pathname, pageTitle])
```
**Correct (effects run independently):**
```tsx
useEffect(() => {
analytics.trackPageView(pathname)
}, [pathname])
useEffect(() => {
document.title = `${pageTitle} | My App`
}, [pageTitle])
```
**Note:** If your project has [React Compiler](https://react.dev/learn/react-compiler) enabled, it automatically optimizes dependency tracking and may handle some of these cases for you.

View File

@ -0,0 +1,40 @@
---
title: Use Transitions for Non-Urgent Updates
impact: MEDIUM
impactDescription: maintains UI responsiveness
tags: rerender, transitions, startTransition, performance
---
## Use Transitions for Non-Urgent Updates
Mark frequent, non-urgent state updates as transitions to maintain UI responsiveness.
**Incorrect (blocks UI on every scroll):**
```tsx
function ScrollTracker() {
const [scrollY, setScrollY] = useState(0)
useEffect(() => {
const handler = () => setScrollY(window.scrollY)
window.addEventListener('scroll', handler, { passive: true })
return () => window.removeEventListener('scroll', handler)
}, [])
}
```
**Correct (non-blocking updates):**
```tsx
import { startTransition } from 'react'
function ScrollTracker() {
const [scrollY, setScrollY] = useState(0)
useEffect(() => {
const handler = () => {
startTransition(() => setScrollY(window.scrollY))
}
window.addEventListener('scroll', handler, { passive: true })
return () => window.removeEventListener('scroll', handler)
}, [])
}
```

View File

@ -0,0 +1,59 @@
---
title: Use useDeferredValue for Expensive Derived Renders
impact: MEDIUM
impactDescription: keeps input responsive during heavy computation
tags: rerender, useDeferredValue, optimization, concurrent
---
## Use useDeferredValue for Expensive Derived Renders
When user input triggers expensive computations or renders, use `useDeferredValue` to keep the input responsive. The deferred value lags behind, allowing React to prioritize the input update and render the expensive result when idle.
**Incorrect (input feels laggy while filtering):**
```tsx
function Search({ items }: { items: Item[] }) {
const [query, setQuery] = useState('')
const filtered = items.filter(item => fuzzyMatch(item, query))
return (
<>
<input value={query} onChange={e => setQuery(e.target.value)} />
<ResultsList results={filtered} />
</>
)
}
```
**Correct (input stays snappy, results render when ready):**
```tsx
function Search({ items }: { items: Item[] }) {
const [query, setQuery] = useState('')
const deferredQuery = useDeferredValue(query)
const filtered = useMemo(
() => items.filter(item => fuzzyMatch(item, deferredQuery)),
[items, deferredQuery]
)
const isStale = query !== deferredQuery
return (
<>
<input value={query} onChange={e => setQuery(e.target.value)} />
<div style={{ opacity: isStale ? 0.7 : 1 }}>
<ResultsList results={filtered} />
</div>
</>
)
}
```
**When to use:**
- Filtering/searching large lists
- Expensive visualizations (charts, graphs) reacting to input
- Any derived state that causes noticeable render delays
**Note:** Wrap the expensive computation in `useMemo` with the deferred value as a dependency, otherwise it still runs on every render.
Reference: [React useDeferredValue](https://react.dev/reference/react/useDeferredValue)

View File

@ -0,0 +1,73 @@
---
title: Use useRef for Transient Values
impact: MEDIUM
impactDescription: avoids unnecessary re-renders on frequent updates
tags: rerender, useref, state, performance
---
## Use useRef for Transient Values
When a value changes frequently and you don't want a re-render on every update (e.g., mouse trackers, intervals, transient flags), store it in `useRef` instead of `useState`. Keep component state for UI; use refs for temporary DOM-adjacent values. Updating a ref does not trigger a re-render.
**Incorrect (renders every update):**
```tsx
function Tracker() {
const [lastX, setLastX] = useState(0)
useEffect(() => {
const onMove = (e: MouseEvent) => setLastX(e.clientX)
window.addEventListener('mousemove', onMove)
return () => window.removeEventListener('mousemove', onMove)
}, [])
return (
<div
style={{
position: 'fixed',
top: 0,
left: lastX,
width: 8,
height: 8,
background: 'black',
}}
/>
)
}
```
**Correct (no re-render for tracking):**
```tsx
function Tracker() {
const lastXRef = useRef(0)
const dotRef = useRef<HTMLDivElement>(null)
useEffect(() => {
const onMove = (e: MouseEvent) => {
lastXRef.current = e.clientX
const node = dotRef.current
if (node) {
node.style.transform = `translateX(${e.clientX}px)`
}
}
window.addEventListener('mousemove', onMove)
return () => window.removeEventListener('mousemove', onMove)
}, [])
return (
<div
ref={dotRef}
style={{
position: 'fixed',
top: 0,
left: 0,
width: 8,
height: 8,
background: 'black',
transform: 'translateX(0px)',
}}
/>
)
}
```

View File

@ -0,0 +1,73 @@
---
title: Use after() for Non-Blocking Operations
impact: MEDIUM
impactDescription: faster response times
tags: server, async, logging, analytics, side-effects
---
## Use after() for Non-Blocking Operations
Use Next.js's `after()` to schedule work that should execute after a response is sent. This prevents logging, analytics, and other side effects from blocking the response.
**Incorrect (blocks response):**
```tsx
import { logUserAction } from '@/app/utils'
export async function POST(request: Request) {
// Perform mutation
await updateDatabase(request)
// Logging blocks the response
const userAgent = request.headers.get('user-agent') || 'unknown'
await logUserAction({ userAgent })
return new Response(JSON.stringify({ status: 'success' }), {
status: 200,
headers: { 'Content-Type': 'application/json' }
})
}
```
**Correct (non-blocking):**
```tsx
import { after } from 'next/server'
import { headers, cookies } from 'next/headers'
import { logUserAction } from '@/app/utils'
export async function POST(request: Request) {
// Perform mutation
await updateDatabase(request)
// Log after response is sent
after(async () => {
const userAgent = (await headers()).get('user-agent') || 'unknown'
const sessionCookie = (await cookies()).get('session-id')?.value || 'anonymous'
logUserAction({ sessionCookie, userAgent })
})
return new Response(JSON.stringify({ status: 'success' }), {
status: 200,
headers: { 'Content-Type': 'application/json' }
})
}
```
The response is sent immediately while logging happens in the background.
**Common use cases:**
- Analytics tracking
- Audit logging
- Sending notifications
- Cache invalidation
- Cleanup tasks
**Important notes:**
- `after()` runs even if the response fails or redirects
- Works in Server Actions, Route Handlers, and Server Components
Reference: [https://nextjs.org/docs/app/api-reference/functions/after](https://nextjs.org/docs/app/api-reference/functions/after)

View File

@ -0,0 +1,96 @@
---
title: Authenticate Server Actions Like API Routes
impact: CRITICAL
impactDescription: prevents unauthorized access to server mutations
tags: server, server-actions, authentication, security, authorization
---
## Authenticate Server Actions Like API Routes
**Impact: CRITICAL (prevents unauthorized access to server mutations)**
Server Actions (functions with `"use server"`) are exposed as public endpoints, just like API routes. Always verify authentication and authorization **inside** each Server Action—do not rely solely on middleware, layout guards, or page-level checks, as Server Actions can be invoked directly.
Next.js documentation explicitly states: "Treat Server Actions with the same security considerations as public-facing API endpoints, and verify if the user is allowed to perform a mutation."
**Incorrect (no authentication check):**
```typescript
'use server'
export async function deleteUser(userId: string) {
// Anyone can call this! No auth check
await db.user.delete({ where: { id: userId } })
return { success: true }
}
```
**Correct (authentication inside the action):**
```typescript
'use server'
import { verifySession } from '@/lib/auth'
import { unauthorized } from '@/lib/errors'
export async function deleteUser(userId: string) {
// Always check auth inside the action
const session = await verifySession()
if (!session) {
throw unauthorized('Must be logged in')
}
// Check authorization too
if (session.user.role !== 'admin' && session.user.id !== userId) {
throw unauthorized('Cannot delete other users')
}
await db.user.delete({ where: { id: userId } })
return { success: true }
}
```
**With input validation:**
```typescript
'use server'
import { verifySession } from '@/lib/auth'
import { z } from 'zod'
const updateProfileSchema = z.object({
userId: z.string().uuid(),
name: z.string().min(1).max(100),
email: z.string().email()
})
export async function updateProfile(data: unknown) {
// Validate input first
const validated = updateProfileSchema.parse(data)
// Then authenticate
const session = await verifySession()
if (!session) {
throw new Error('Unauthorized')
}
// Then authorize
if (session.user.id !== validated.userId) {
throw new Error('Can only update own profile')
}
// Finally perform the mutation
await db.user.update({
where: { id: validated.userId },
data: {
name: validated.name,
email: validated.email
}
})
return { success: true }
}
```
Reference: [https://nextjs.org/docs/app/guides/authentication](https://nextjs.org/docs/app/guides/authentication)

View File

@ -0,0 +1,41 @@
---
title: Cross-Request LRU Caching
impact: HIGH
impactDescription: caches across requests
tags: server, cache, lru, cross-request
---
## Cross-Request LRU Caching
`React.cache()` only works within one request. For data shared across sequential requests (user clicks button A then button B), use an LRU cache.
**Implementation:**
```typescript
import { LRUCache } from 'lru-cache'
const cache = new LRUCache<string, any>({
max: 1000,
ttl: 5 * 60 * 1000 // 5 minutes
})
export async function getUser(id: string) {
const cached = cache.get(id)
if (cached) return cached
const user = await db.user.findUnique({ where: { id } })
cache.set(id, user)
return user
}
// Request 1: DB query, result cached
// Request 2: cache hit, no DB query
```
Use when sequential user actions hit multiple endpoints needing the same data within seconds.
**With Vercel's [Fluid Compute](https://vercel.com/docs/fluid-compute):** LRU caching is especially effective because multiple concurrent requests can share the same function instance and cache. This means the cache persists across requests without needing external storage like Redis.
**In traditional serverless:** Each invocation runs in isolation, so consider Redis for cross-process caching.
Reference: [https://github.com/isaacs/node-lru-cache](https://github.com/isaacs/node-lru-cache)

View File

@ -0,0 +1,76 @@
---
title: Per-Request Deduplication with React.cache()
impact: MEDIUM
impactDescription: deduplicates within request
tags: server, cache, react-cache, deduplication
---
## Per-Request Deduplication with React.cache()
Use `React.cache()` for server-side request deduplication. Authentication and database queries benefit most.
**Usage:**
```typescript
import { cache } from 'react'
export const getCurrentUser = cache(async () => {
const session = await auth()
if (!session?.user?.id) return null
return await db.user.findUnique({
where: { id: session.user.id }
})
})
```
Within a single request, multiple calls to `getCurrentUser()` execute the query only once.
**Avoid inline objects as arguments:**
`React.cache()` uses shallow equality (`Object.is`) to determine cache hits. Inline objects create new references each call, preventing cache hits.
**Incorrect (always cache miss):**
```typescript
const getUser = cache(async (params: { uid: number }) => {
return await db.user.findUnique({ where: { id: params.uid } })
})
// Each call creates new object, never hits cache
getUser({ uid: 1 })
getUser({ uid: 1 }) // Cache miss, runs query again
```
**Correct (cache hit):**
```typescript
const getUser = cache(async (uid: number) => {
return await db.user.findUnique({ where: { id: uid } })
})
// Primitive args use value equality
getUser(1)
getUser(1) // Cache hit, returns cached result
```
If you must pass objects, pass the same reference:
```typescript
const params = { uid: 1 }
getUser(params) // Query runs
getUser(params) // Cache hit (same reference)
```
**Next.js-Specific Note:**
In Next.js, the `fetch` API is automatically extended with request memoization. Requests with the same URL and options are automatically deduplicated within a single request, so you don't need `React.cache()` for `fetch` calls. However, `React.cache()` is still essential for other async tasks:
- Database queries (Prisma, Drizzle, etc.)
- Heavy computations
- Authentication checks
- File system operations
- Any non-fetch async work
Use `React.cache()` to deduplicate these operations across your component tree.
Reference: [React.cache documentation](https://react.dev/reference/react/cache)

View File

@ -0,0 +1,65 @@
---
title: Avoid Duplicate Serialization in RSC Props
impact: LOW
impactDescription: reduces network payload by avoiding duplicate serialization
tags: server, rsc, serialization, props, client-components
---
## Avoid Duplicate Serialization in RSC Props
**Impact: LOW (reduces network payload by avoiding duplicate serialization)**
RSC→client serialization deduplicates by object reference, not value. Same reference = serialized once; new reference = serialized again. Do transformations (`.toSorted()`, `.filter()`, `.map()`) in client, not server.
**Incorrect (duplicates array):**
```tsx
// RSC: sends 6 strings (2 arrays × 3 items)
<ClientList usernames={usernames} usernamesOrdered={usernames.toSorted()} />
```
**Correct (sends 3 strings):**
```tsx
// RSC: send once
<ClientList usernames={usernames} />
// Client: transform there
'use client'
const sorted = useMemo(() => [...usernames].sort(), [usernames])
```
**Nested deduplication behavior:**
Deduplication works recursively. Impact varies by data type:
- `string[]`, `number[]`, `boolean[]`: **HIGH impact** - array + all primitives fully duplicated
- `object[]`: **LOW impact** - array duplicated, but nested objects deduplicated by reference
```tsx
// string[] - duplicates everything
usernames={['a','b']} sorted={usernames.toSorted()} // sends 4 strings
// object[] - duplicates array structure only
users={[{id:1},{id:2}]} sorted={users.toSorted()} // sends 2 arrays + 2 unique objects (not 4)
```
**Operations breaking deduplication (create new references):**
- Arrays: `.toSorted()`, `.filter()`, `.map()`, `.slice()`, `[...arr]`
- Objects: `{...obj}`, `Object.assign()`, `structuredClone()`, `JSON.parse(JSON.stringify())`
**More examples:**
```tsx
// ❌ Bad
<C users={users} active={users.filter(u => u.active)} />
<C product={product} productName={product.name} />
// ✅ Good
<C users={users} />
<C product={product} />
// Do filtering/destructuring in client
```
**Exception:** Pass derived data when transformation is expensive or client doesn't need original.

View File

@ -0,0 +1,149 @@
---
title: Hoist Static I/O to Module Level
impact: HIGH
impactDescription: avoids repeated file/network I/O per request
tags: server, io, performance, next.js, route-handlers, og-image
---
## Hoist Static I/O to Module Level
**Impact: HIGH (avoids repeated file/network I/O per request)**
When loading static assets (fonts, logos, images, config files) in route handlers or server functions, hoist the I/O operation to module level. Module-level code runs once when the module is first imported, not on every request. This eliminates redundant file system reads or network fetches that would otherwise run on every invocation.
**Incorrect (reads font file on every request):**
```typescript
// app/api/og/route.tsx
import { ImageResponse } from 'next/og'
export async function GET(request: Request) {
// Runs on EVERY request - expensive!
const fontData = await fetch(
new URL('./fonts/Inter.ttf', import.meta.url)
).then(res => res.arrayBuffer())
const logoData = await fetch(
new URL('./images/logo.png', import.meta.url)
).then(res => res.arrayBuffer())
return new ImageResponse(
<div style={{ fontFamily: 'Inter' }}>
<img src={logoData} />
Hello World
</div>,
{ fonts: [{ name: 'Inter', data: fontData }] }
)
}
```
**Correct (loads once at module initialization):**
```typescript
// app/api/og/route.tsx
import { ImageResponse } from 'next/og'
// Module-level: runs ONCE when module is first imported
const fontData = fetch(
new URL('./fonts/Inter.ttf', import.meta.url)
).then(res => res.arrayBuffer())
const logoData = fetch(
new URL('./images/logo.png', import.meta.url)
).then(res => res.arrayBuffer())
export async function GET(request: Request) {
// Await the already-started promises
const [font, logo] = await Promise.all([fontData, logoData])
return new ImageResponse(
<div style={{ fontFamily: 'Inter' }}>
<img src={logo} />
Hello World
</div>,
{ fonts: [{ name: 'Inter', data: font }] }
)
}
```
**Correct (synchronous fs at module level):**
```typescript
// app/api/og/route.tsx
import { ImageResponse } from 'next/og'
import { readFileSync } from 'fs'
import { join } from 'path'
// Synchronous read at module level - blocks only during module init
const fontData = readFileSync(
join(process.cwd(), 'public/fonts/Inter.ttf')
)
const logoData = readFileSync(
join(process.cwd(), 'public/images/logo.png')
)
export async function GET(request: Request) {
return new ImageResponse(
<div style={{ fontFamily: 'Inter' }}>
<img src={logoData} />
Hello World
</div>,
{ fonts: [{ name: 'Inter', data: fontData }] }
)
}
```
**Incorrect (reads config on every call):**
```typescript
import fs from 'node:fs/promises'
export async function processRequest(data: Data) {
const config = JSON.parse(
await fs.readFile('./config.json', 'utf-8')
)
const template = await fs.readFile('./template.html', 'utf-8')
return render(template, data, config)
}
```
**Correct (hoists config and template to module level):**
```typescript
import fs from 'node:fs/promises'
const configPromise = fs
.readFile('./config.json', 'utf-8')
.then(JSON.parse)
const templatePromise = fs.readFile('./template.html', 'utf-8')
export async function processRequest(data: Data) {
const [config, template] = await Promise.all([
configPromise,
templatePromise,
])
return render(template, data, config)
}
```
When to use this pattern:
- Loading fonts for OG image generation
- Loading static logos, icons, or watermarks
- Reading configuration files that don't change at runtime
- Loading email templates or other static templates
- Any static asset that's the same across all requests
When not to use this pattern:
- Assets that vary per request or user
- Files that may change during runtime (use caching with TTL instead)
- Large files that would consume too much memory if kept loaded
- Sensitive data that shouldn't persist in memory
With Vercel's [Fluid Compute](https://vercel.com/docs/fluid-compute), module-level caching is especially effective because multiple concurrent requests share the same function instance. The static assets stay loaded in memory across requests without cold start penalties.
In traditional serverless, each cold start re-executes module-level code, but subsequent warm invocations reuse the loaded assets until the instance is recycled.

View File

@ -0,0 +1,50 @@
---
title: Avoid Shared Module State for Request Data
impact: HIGH
impactDescription: prevents concurrency bugs and request data leaks
tags: server, rsc, ssr, concurrency, security, state
---
## Avoid Shared Module State for Request Data
For React Server Components and client components rendered during SSR, avoid using mutable module-level variables to share request-scoped data. Server renders can run concurrently in the same process. If one render writes to shared module state and another render reads it, you can get race conditions, cross-request contamination, and security bugs where one user's data appears in another user's response.
Treat module scope on the server as process-wide shared memory, not request-local state.
**Incorrect (request data leaks across concurrent renders):**
```tsx
let currentUser: User | null = null
export default async function Page() {
currentUser = await auth()
return <Dashboard />
}
async function Dashboard() {
return <div>{currentUser?.name}</div>
}
```
If two requests overlap, request A can set `currentUser`, then request B overwrites it before request A finishes rendering `Dashboard`.
**Correct (keep request data local to the render tree):**
```tsx
export default async function Page() {
const user = await auth()
return <Dashboard user={user} />
}
function Dashboard({ user }: { user: User | null }) {
return <div>{user?.name}</div>
}
```
Safe exceptions:
- Immutable static assets or config loaded once at module scope
- Shared caches intentionally designed for cross-request reuse and keyed correctly
- Process-wide singletons that do not store request- or user-specific mutable data
For static assets and config, see [Hoist Static I/O to Module Level](./server-hoist-static-io.md).

View File

@ -0,0 +1,83 @@
---
title: Parallel Data Fetching with Component Composition
impact: CRITICAL
impactDescription: eliminates server-side waterfalls
tags: server, rsc, parallel-fetching, composition
---
## Parallel Data Fetching with Component Composition
React Server Components execute sequentially within a tree. Restructure with composition to parallelize data fetching.
**Incorrect (Sidebar waits for Page's fetch to complete):**
```tsx
export default async function Page() {
const header = await fetchHeader()
return (
<div>
<div>{header}</div>
<Sidebar />
</div>
)
}
async function Sidebar() {
const items = await fetchSidebarItems()
return <nav>{items.map(renderItem)}</nav>
}
```
**Correct (both fetch simultaneously):**
```tsx
async function Header() {
const data = await fetchHeader()
return <div>{data}</div>
}
async function Sidebar() {
const items = await fetchSidebarItems()
return <nav>{items.map(renderItem)}</nav>
}
export default function Page() {
return (
<div>
<Header />
<Sidebar />
</div>
)
}
```
**Alternative with children prop:**
```tsx
async function Header() {
const data = await fetchHeader()
return <div>{data}</div>
}
async function Sidebar() {
const items = await fetchSidebarItems()
return <nav>{items.map(renderItem)}</nav>
}
function Layout({ children }: { children: ReactNode }) {
return (
<div>
<Header />
{children}
</div>
)
}
export default function Page() {
return (
<Layout>
<Sidebar />
</Layout>
)
}
```

View File

@ -0,0 +1,34 @@
---
title: Parallel Nested Data Fetching
impact: CRITICAL
impactDescription: eliminates server-side waterfalls
tags: server, rsc, parallel-fetching, promise-chaining
---
## Parallel Nested Data Fetching
When fetching nested data in parallel, chain dependent fetches within each item's promise so a slow item doesn't block the rest.
**Incorrect (a single slow item blocks all nested fetches):**
```tsx
const chats = await Promise.all(
chatIds.map(id => getChat(id))
)
const chatAuthors = await Promise.all(
chats.map(chat => getUser(chat.author))
)
```
If one `getChat(id)` out of 100 is extremely slow, the authors of the other 99 chats can't start loading even though their data is ready.
**Correct (each item chains its own nested fetch):**
```tsx
const chatAuthors = await Promise.all(
chatIds.map(id => getChat(id).then(chat => getUser(chat.author)))
)
```
Each item independently chains `getChat``getUser`, so a slow chat doesn't block author fetches for the others.

View File

@ -0,0 +1,38 @@
---
title: Minimize Serialization at RSC Boundaries
impact: HIGH
impactDescription: reduces data transfer size
tags: server, rsc, serialization, props
---
## Minimize Serialization at RSC Boundaries
The React Server/Client boundary serializes all object properties into strings and embeds them in the HTML response and subsequent RSC requests. This serialized data directly impacts page weight and load time, so **size matters a lot**. Only pass fields that the client actually uses.
**Incorrect (serializes all 50 fields):**
```tsx
async function Page() {
const user = await fetchUser() // 50 fields
return <Profile user={user} />
}
'use client'
function Profile({ user }: { user: User }) {
return <div>{user.name}</div> // uses 1 field
}
```
**Correct (serializes only 1 field):**
```tsx
async function Page() {
const user = await fetchUser()
return <Profile name={user.name} />
}
'use client'
function Profile({ name }: { name: string }) {
return <div>{name}</div>
}
```

View File

@ -0,0 +1,396 @@
---
name: drawRoofByAttribute 내부 구조 (후반부 상세)
description: qpolygon-utils.js drawRoofByAttribute (1680-5455, 3777줄) 의 단계별 정밀 분석. 라인 분류→가선분(linesAnalysis) 누적→MAX_ITERATIONS 교점 처리→하단지붕 파생→최종 split. 수정 시 어느 stage 인지 식별 필수.
type: project
---
# drawRoofByAttribute 내부 구조 (후반부 상세)
`src/util/qpolygon-utils.js:1680-5455` 의 정밀 분해. catch-all 함수라 모든 비표준 케이스(요세무네/맨사드/형이동/벽취합/팔작/반절처/케라바 혼합 등)가 여기 모인다.
## 0. 데이터 흐름 한 장 요약
```
wall.baseLines → [zero merge] → baseLines (mutated copy)
type 분류: sheds / hipAndGables / eaves / jerkinHeads / gables
[stage1 처리] 각 type 별로:
- innerLines.push(drawHip/Ridge/RoofLine ...) ← 즉시 그림
- linesAnalysis.push({start, end, left, right, type, degree}) ← 가선분 누적
[stage2 MAX_ITERATIONS 루프] linesAnalysis 끼리 교점 → drawHip/Ridge + newAnalysis 분기
[stage3 가선분 잔여] 평행 라인 예외처리
[stage4 벽취합 잔여] 벽-벽 라인 처리
[stage5 하단지붕 파생] downRoofGable (A/B) + downRoofEaves
[stage6 최종 split] roof.lines × innerLinesPoints 분할 → drawHip/RoofLine
roof.innerLines = innerLines (덮어쓰기)
```
`innerLines``linesAnalysis`**다른 컨테이너**:
- `innerLines`: 실제 캔버스에 그려진 QLine 객체들 (drawHip/Ridge/RoofLine 반환값)
- `linesAnalysis`: 가상 선분 메타데이터 (`{start, end, left, right, type, degree, gableId?, connectCnt?}`) — 아직 캔버스에 안 그려짐, 교점 계산용
`TYPES = { HIP, RIDGE, GABLE_LINE, NEW }` 는 linesAnalysis 내부 enum (canvas 의 `LINE_TYPE.SUBLINE` 과 별개).
## 1. 전반부 (1680-1995) — 이미 파악된 영역
- **1680-1759 형이동 zero-merge**: `wall.baseLines``planeSize<EPSILON` 인덱스를 prev/next 페어로 모아 같은 vector 라인 끼리 합쳐 `baseLines` 재구성. zero-merge 후엔 `wall.baseLines` 의 중간 라인이 사라진 짧은 형태가 됨.
- **1761 `checkWallPolygon`**: `baseLines` 의 시작점만 모아 만든 임시 QPolygon. inPolygon 판정 전용.
- **1766-1995 벽취합 corner extension**: WALL+offset>0 라인의 prev/next 처리, `addRoofLine1/2` 추가. `name='addRoofLine'`, `attributes.type=WALLLINE.ETC`.
## 2. analyzeLine 헬퍼 (2000-2133)
drawGableRoof 의 `analyzeEavesLine` 과 거의 동일하지만 모든 type 에 적용. **return**:
```
{ startPoint, endPoint, length, angleDegree, normalizedAngle,
isHorizontal, isVertical, isDiagonal,
directionVector: {x,y},
roofLine: 매칭된 roof.lines 의 한 라인,
roofVector: {x,y} (지붕 안쪽 방향, 단위벡터) }
```
핵심:
- tolerance 1° 로 H/V/Diagonal 분류
- `roof.lines` 중 directionVector 가 같은 것을 `checkRoofLines` 로 거름
- offset=0: 좌표 직접 매칭. 실패 시 `currentRoof = { roofLine: line }` 자기 자신 fallback (NPE 방지)
- offset>0: midPoint 에서 roofVector 방향으로 offset 만큼 가선 그어서 `edgesIntersection` 검색, intersect 길이 - offset < 0.1 으로 best match 선택
⚠️ `roofLine` 매칭 실패 시 `currentRoof.roofLine` 직접 접근 (2130) — fallback 로직이 있어서 안전하지만 추가 변경 시 NPE 주의.
## 3. getRidgeDrivePoint 헬퍼 (2142-2270)
마루(ridge)/이등분선 등이 **반대편 케라바(GABLE) 또는 이중처마(EAVES-EAVES) 코너**까지만 뻗도록 클램프하는 포인트를 계산.
- prevLine/nextLine 이 모두 EAVES 가 아니면 `null` (호출처에서 fallback 처리)
- prevLine 이 EAVES 이고 그 prev 가 GABLE 이면 → GABLE 의 roofLine 과 testPoint 의 교점 (=GABLE 코너)
- prevLine 이 EAVES 이고 그 prev 도 EAVES 이면 → 두 EAVES 의 사잇각 vector 로 hip edge 그어서 testPoint 와의 교점
- nextLine 쪽도 대칭
- 둘 다 있으면 가까운 쪽
- prevHasGable / nextHasGable 우선순위: `prevLength <= nextLength` 면 prev, 아니면 next 의 GABLE 사용
호출처: hipAndGables, ridgeEaves, jerkinHeads, MAX_ITERATIONS 루프(NEW 가선분 분기 시)
## 4. 라인 분류 (2275-2300)
```
baseLines.forEach(switch on attributes.type):
SHED → sheds[]
HIPANDGABLE → hipAndGables[] (팔작지붕/이리모야)
EAVES → eaves[] (처마)
JERKINHEAD → jerkinHeads[] (반절처)
GABLE → gables[] (케라바/박공)
default → 무시 (WALL, ETC, DEFAULT 등)
```
`linesAnalysis = []` 초기화 (2303).
## 5. 단계별 처리 (1~5)
### 5-1. sheds.forEach (2306-2467) — 한쪽흐름
가드: 양옆이 둘 다 GABLE 이어야 함. 아니면 return.
1. 맞은편 EAVES 라인 찾기 → shedDegree 산출 (없으면 4寸 default)
2. roofLine 양 끝점에서 지붕 안쪽으로의 vector 두 개 (`checkRoofVector1/2`) 그어서 반대편 roof.lines 와의 교점 (`intersectPoints1/2`)
3. **HIP 가선분 2개 push** (양 끝점 → 교점), `left=beforePrevIndex/nextIndex`, `right=prevIndex/afterNextIndex`, `type=TYPES.HIP`, `degree=shedDegree`
⚠️ `intersectPoints1[0]` 직접 인덱스 접근 (2441) — sort 후 비어있으면 NPE.
### 5-2. hipAndGables.forEach (2470-2699) — 팔작지붕
가드: 양옆이 둘 다 EAVES. 그리고 prevLine/nextLine 의 vector 가 같으면(같은 방향, U자) silent return.
1. `lineWidth = min(currentLine.attributes.width, roofLength/2)` — 팔작지붕 두께 클램프
2. prevRoofLine/nextRoofLine 와 currentRoofLine 의 사잇각 vector 로 prevHipPoint/nextHipPoint 산출 (지붕 안쪽 방향 정규화)
3. **HIP 2개 즉시 그림** (`innerLines.push(drawHipLine prev, next)`)
4. 양옆이 모두 EAVES (gable 이 짧을 때): midPoint 에서 양쪽 gable hip 두개 추가 + roofVector 반대 방향으로 10000 길이 가선 그어서 반대편 roof 교점 → `getRidgeDrivePoint` 로 클램프 → `oppLine.attributes.type` 별 offset 보정 (HIPANDGABLE=width, JERKINHEAD=width/2, WALL=0) → **RIDGE 가선분 push**
5. else (gable 길이 충분): `drawRoofLine(gablePoint)` 즉시 그림. 길이 ~0 이면 RIDGE 가선분만 push
### 5-3. eaves 처리 (2701-3143)
eaves 를 `planeSize` 오름차순 sort 후 두 그룹으로 분리:
- **`ridgeEaves`** (2704-2723): 양옆 모두 EAVES + prev/next vector 가 다른 방향 + checkWallPolygon.inPolygon(midPoint+nextVector) — 즉 처마-처마-처마 코너에서 안쪽 들여진 모양
- **`hipedEaves`** (2725): 나머지 (= 옆이 GABLE 등)
#### ridgeEaves.forEach (2731-3036) — 가장 복잡한 블록
상태 누적기:
- `proceedEaves[]`: `{prev, current, point: {x1,y1,x2,y2}, length}` — 처리된 처마 hip
- `proceedRidges[]`: `{prev, next, point}` — 처리된 ridge
알고리즘:
1. pHipVector/nHipVector (양 옆과의 사잇각) 산출, inPolygon 으로 정규화
2. `findRoofPoints(checkPoint)` 헬퍼: hipEdge 와 roof.lines 교점들을 forward/backward 로 분리 후 가까운 것 선택. forward+backward / backward 2개 / forward 2개 fallback
3. prevHipPoint/nextHipPoint 두 hip 의 `edgesIntersection` 으로 정점 보정
4. **proceedRidges 루프**: 같은 base index 영향권의 ridge 가 prev/next hip 와 교차하면 `isRidgePrev/isRidgeNext` 플래그 → hip 좌표를 forward 로 되돌림(roof 정점까지)
5. **proceedEaves 중복 처리**: `alreadyPrev/Next` 가 있으면 길이가 짧은 쪽으로 교체 (또는 새 hip 좌표를 기존 것으로 덮음). isRidge 플래그 시 무조건 교체
6. prevHipPoint.x2 와 nextHipPoint.x2 가 같으면(=정점 만남) **ridge 생성**:
- midPoint 에서 ridgeVector(nextLine 방향, inPolygon 정규화) 로 가선
- roof.lines 와의 가까운 교점 → ridge 끝점
- `getRidgeDrivePoint` 로 클램프
- `isOverlapBefore/After` (beforePrev/afterNext 가 currentLine 과 동일 좌표 범위) 시 oppLine.type 별 offset 보정 분기:
- 양옆에 GABLE 있음(otherGable): drivePoint 우선
- 없음 + oppLine=EAVES + 양옆에 EAVES: 길이 같으면 drivePoint, 다르면 offset
- 없음 + 그 외: offset 만 적용
7. ridge 중복 체크 후 `proceedRidges.push`
#### hipedEaves.forEach (3038-3143)
가드: prevLine 이 EAVES 여야 함. 아니면 return.
1. currentLine 와 prevLine 의 사잇각 vector → hipVector (inPolygon 정규화)
2. `currentLine.x1` 에서 hipVector × `analyze.length` 길이로 가선 → roof.lines 의 forward/backward 교점
3. **HIP 가선분 push** (linesAnalysis), `degree=currentDegree` (currentLine.pitch)
#### proceedEaves → linesAnalysis 변환 (3146-3219)
```
proceedEaves.forEach((eaves, index)):
- jointEaves = 같은 끝점(point.x2,y2) 공유하는 다른 eaves 검색
- jointEaves.length === 1 + ridgeEaves.length > 1:
drawHipLine 즉시 그림 (innerLines.push)
- jointEaves.length === 2 (3-way 코너):
jointIndex 3개 모두 drawHipLine 즉시
proceedRidges 에서 해당 코너 ridge 제거
"denyVector" (반대쌍 없는 vector) 찾아서 roof 와의 교점 → HIP 가선분 push (uniqueLine 좌우)
- else:
HIP 가선분 push (linesAnalysis)
```
#### proceedRidges → linesAnalysis (3220-3229): 모두 RIDGE 가선분으로 push
### 5-4. jerkinHeads.forEach (3232-3445) — 반절처
가드: 양옆 EAVES. 양옆 vector 같으면 silent return.
1. `gableWidth = currentLine.attributes.width`, `lineLength = (roofLength - gableWidth) / 2`
2. roofLine 따라 4개 점 (`jerkinPoints`): 시작 → +lineLength → +gableWidth → +lineLength=끝
3. `gableWidth >= roofLength`: **drawRoofLine** 즉시 (roofPoints 그대로)
4. 정상: **drawHipLine(gable1, prevDegree) + drawRoofLine(gable2) + drawHipLine(gable3, nextDegree)** 즉시
5. 추가로 prevRoofLine/nextRoofLine 과의 사잇각 hipVector × gableWidth → prevHipEdge/nextHipEdge 교점 hipIntersection
6. hipIntersection 있으면 **drawHipLine prev/next 2개 즉시** + roofVector 반대 방향으로 10000 가선 → roof 교점 → drivePoint 클램프 → offset 보정 → **RIDGE 가선분 push**
### 5-5. gables.forEach (3447-4239) — 케라바/박공 (가장 큰 블록)
가드: 양옆 EAVES. 그리고 vector 다른 방향 + checkWallPolygon.inPolygon 통과해야 본 처리.
#### isOverlap 분기 (3502-3562)
`isOverlapBefore/After` (beforePrev 또는 afterNext 가 currentLine 좌표 범위 동일) + otherGable 없음 → **isOverlap=true**:
- mid-to-mid ridge: cMid + ridgeVector*cOffset → oMid - ridgeVector*oOffset, oppLine.type 별 추가 offset
- 중복 체크 후 **RIDGE 가선분 push**
#### 일반 분기 (3563-4182)
stdLine 결정: prevLine/nextLine 의 거리 (`prevDistance/nextDistance`) 비교, 둘 다 GABLE 이면 긴 쪽, 아니면 짧은 쪽. `stdFindOppVector` 도 같이 결정.
`stdPoints` 좌표 보정:
- **stdPrevVector === stdNextVector** (U자 코너): isHorizontal/isVertical × stdVector × prev/next y/x 비교로 8가지 분기, GABLE 일 때만 offset 적용
- 다른 방향: 양 끝점에 prev/nextLine offset 적용
`oppositeLine` 검색: stdLine 의 반대편 (stdFindOppVector 방향) 평행 라인들 (overlap 케이스 포함)
`oppositeLine.forEach(opposite)` (3787-4083):
1. ridgePoint 산출:
- oppPrev/oppNextVector 같으면 (U자): 평행 offset 두번 적용
- 다르면 (Y/L자): inPolygon 분기로 offset 부호 결정
2. stdAnalyze 축에 맞춰 ridgePoint 의 한 축을 mid 좌표로 통일
3. **oppLine 양옆 EAVES/GABLE 혼합 케이스 분기** (3835-3976):
- oppPrev xor oppNext 가 EAVES: stdLine 길이 >= oppLine 길이일 때 hipEdge 와 ridgeEdge 교점 → ridgePoint 시작점 보정
- 짧은 stdLine + EAVES 짧은 stdPrev/Next: drivePoint 보정
4. stdPrev/Next 가 EAVES: driveEdge 와 ridgeEdge 교점 → stdPoints 의 해당 끝점 좌표 갱신
5. overlap 영역(stdMin/Max × ridgeMin/Max) 으로 ridgePoint 클램프, vector 정렬 보정
6. `ridgePoints.push({point, left:stdLine, right:oppLine})`
`ridgePoints.forEach(r)` (4085-4180):
- inPolygon1/2 체크, 밖이면 vector 내 가장 가까운 roof.line 으로 보정
- roof boundary 위에 있는 점이 1개만이면 그 점이 startPoint
- **RIDGE 가선분 push** (중복 체크)
#### 4185-4231 — 양옆 vector 같음 (반절마루 불가) 케이스
`prevLineVector === nextLineVector` (U자): roofLine 직각 방향으로 가선 → 반대편 roof.line 교점 (가장 먼 것) → 시작점은 roofLine 양 끝 중 교점에서 먼 쪽 → **GABLE_LINE 가선분 push** (`degree = prevLine.pitch`, `gableId=baseLines.findIndex(currentLine)`, `connectCnt=0`)
## 6. MAX_ITERATIONS 루프 (4242-4608) — 가선분 교점 처리
`linesAnalysis` 가 비거나 1000회 도달까지 반복:
### 6-1. intersections 수집 (4248-4297)
각 currLine 마다:
- length<EPSILON 무시
- GABLE_LINE + connectCnt>1 무시
- 다른 nextLine 과 `lineIntersection(seg-seg)` 시도
- 가장 짧은 distance1 (+ 같은 GABLE_LINE pair gableId 같으면 skip) 의 partner 선택
- distance1==minDistance 일 때 `isSameLine` (left/right 공유) 우선
### 6-2. 단일 교점 보정 (4302-4305)
`intersectPoints` unique 가 1개고 intersections 가 ≥2 → 첫 두개를 서로 partner 로 강제.
### 6-3. for-of 처리 (4310-4597)
intersection 페어마다:
- mutual partner 아니거나 left/right 공통 안 되면 skip
- 각각 drawHip/Ridge 그림 (innerLines.push, alreadyPoints 중복 차단):
- `cLine.type === HIP` → drawHipLine (cLine.degree)
- `RIDGE` → drawRidgeLine
- `NEW` → diagonal 이면 hip, 아니면 ridge
- `GABLE_LINE` → degree>0 hip, 아니면 ridge
#### 동일 교점에 다른 라인이 더 있으면 (otherIs)
- 관련 baseline 공유하는 라인은 추가 그리고 processed
- (GABLE_LINE 페어인 경우는 otherIs 처리 skip)
#### GABLE_LINE 분기 (4425-4467)
- `gableLine.connectCnt++`
- uniqueLines 2개면:
- connectCnt<2: GABLE_LINE 가선분 새로 push (intersect end)
- connectCnt>=2: HIP 더미 (start==end) push — "가선분 발생만 시키는" trigger 용
#### 일반 분기 (4468-4588) — NEW 가선분 생성
1. uniqueLines 2개 → bisector 계산 (`isParallel``getBisectLines`(평행 시 길이 보정 100), 아니면 `getBisectBaseLines`)
2. bisector 방향으로 roof.lines 와 교점 → 가까운 것
3. **이미 같은 교점 가진 미처리 라인 있으면 그것을 newAnalysis 로 교체** (재활용)
4. 아니면 NEW 가선분 push (`type=NEW`, `degree= isDiagonal ? cLine.degree : 0`)
5. drivePoint 로 ridge 길이 클램프 (NEW + horizontal/vertical 일 때만)
`newAnalysis.length > 0` 이면 break (한 페어 처리 후 즉시 재계산)
### 6-4. 루프 종료 후 (4599-4608)
`linesAnalysis = newAnalysis ⊕ (linesAnalysis - processed)`. newAnalysis 비면 while 종료.
## 7. 가선분 잔여 (4610-4696)
남은 linesAnalysis 의 평행 페어 처리:
- isOpposite (반대 방향): start-start 사이 line, type 별 drawHip/Ridge
- 동일 방향: 4개 점 중 unique 2개로 line, isDiagonal 여부에 따라 drawHip/Ridge
- 처리된 라인은 `proceedAnalysis` 로 마킹 후 linesAnalysis 에서 제외
## 8. 벽취합 잔여 (4698-4732)
linesAnalysis 에 남은 line 중:
- start/end 가 모두 roof.lines 위에 있으면:
- line.type=RIDGE + baseLines.length===4 + (start 또는 end 가 onLine): drawRidgeLine
- 그 외 (양쪽 다 onLine): degree===0 → drawRoofLine, 아니면 drawHipLine
- innerLines 에 같은 끝점이 3개 미만, 다른 끝점은 없을 때만 그림 (중복 차단)
## 9. 하단지붕 분류 (4734-4778)
baseLines 순회하며 3종류로 분류:
- **downRoofGable type 'A'** (4750-4757): prevVector === nextVector (U자) + currentLine=EAVES + 양옆 중 하나라도 GABLE
- **downRoofGable type 'B'** (4759-4766): prevVector ≠ nextVector + checkWallPolygon.inPolygon(mid+nextVector) + 양옆 둘 다 GABLE
- **downRoofEaves** (4768-4777): prevVector === nextVector + currentLine=EAVES + 양옆 중 하나라도 EAVES + originPoint ≠ 현재 좌표 (=형이동된 라인)
## 10. downRoofLines (4780-5158)
### 10-1. type='A' (4782-4971) — U자 케라바 사이 처마
1. `gableLine` = prev 또는 next (currVector × prev/nextVector 8가지 분기)
2. `stdPoint` = currentLine 양 끝 (gableLine 쪽 그대로 + 반대쪽 -currVector × otherLine.offset)
3. `oppLines` = baseLines 중 stdLine 반대쪽 평행 라인들
4. `innerRidge` = innerLines 중 RIDGE 라인 + currVector 와 같은 축 + stdPoint 범위 안
5. `roofLinePoint` = stdPoint 에 currOffset 적용 (currVector.x===0 이면 ±y, 아니면 ∓x)
6. **drawRoofLine(roofLinePoint)** push
7. roofLinePoint 에서 oppFindVector 방향 가선 → innerRidge 와 교점 가장 가까운 것 → **drawHipLine + drawRoofLine** (intersect → 인접한 ridge 끝점)
### 10-2. type='B' (4974-5157) — 안쪽으로 들어간 케라바 양쪽
1. `ridgeFindVector` = nextVector (mid 가 polygon 안일 때) 또는 prevVector
2. `oppLines` = innerLines RIDGE 중 ridgeFindVector 방향 + currentLine 범위 안
3. `roofPoint` = currentLine 좌표에 -nextVector × currOffset 적용
4. **drawRoofLine(roofPoint)** push
5. orthogonalStart/EndPoint = currentLine 양 끝에서 oppLines 의 가까운 끝점에 직각으로 투영
6. orthogonalStartLine === orthogonalEndLine: **drawRoofLine(start→end)** 1개
7. 다르면: **drawRoofLine 2개** (각 ridge 의 먼 끝점까지 연장)
8. 추가로 **drawHipLine 2개** (roofPoint 양 끝 → orthogonalPoint 양 끝)
## 11. downRoofEaves.forEach (5160-5376) — 처마 형이동 흡수 라인
핵심 데이터:
- `flowDistance` = currentLine 과 originPoint 의 axis 거리
- `isFlowInside` = inPolygon 으로 안/밖 결정
- `upLine` (상단 지붕) = isFlowInside 면 otherLine, 아니면 currentLine
- `downLine` (하단 지붕) = 반대
- `joinPoint` = upLine 과 downLine 이 만나는 모서리
상단 지붕 처리:
1. `upRoofPoint` = upRoofLine 끝점에 upRoofVector × addUpOffset(=flowDistance) 더한 좌표 (inPolygon 통과하는 방향)
2. `upHipStartPoint` = upRoofLine 의 다른 끝
3. `upHipVector` = upLine + downLine 사잇각 (inPolygon 정규화)
4. `joinHipLine` = innerLines 중 joinPoint + (joinPoint+upHipVector) 둘 다 위에 있는 라인 (=기존에 그려진 추녀마루)
하단 지붕 처리:
5. `downRoofPoint` = downRoofLine 끝점 + downRoofVector × addDownOffset(=upLine.offset), inPolygon 분기
6. `intersectJoin` = upRoofEdge × joinHipEdge — 상단 지붕선과 추녀마루의 교점 → joinHipLine.set 으로 좌표 갱신 (mutated!)
upSideLine 분기 (upLine 의 다른 인접 라인):
- upSideLine = EAVES (= 처마 끼고 추녀마루 분기 가능):
- innerLines 중 joinHipLine 외에 upRoofEdge 와 교점 + minDistance > EPSILON: **drawHipLine(connectPoint) + drawRoofLine(connectPoint→intersectJoin)**
- 없음: **drawRoofLine(upHipStartPoint→intersectJoin)**
- 아니면: **drawRoofLine(upHipStartPoint→intersectJoin)**
각 그릴 때 `roof.adjustRoofLines.push({point, roofIdx: upRoofLine.idx})` — 회로/모듈 배치에서 보정 참조용.
하단 지붕 추녀마루 (intersectDownJoin = downHipEdge × joinHipEdge):
- 있으면 **drawRoofLine(downRoofPoint) + drawHipLine(downHip1) + drawHipLine(downHip2 → joinEndPoint)** — 지붕선/추녀마루1/추녀마루2 트리오
## 12. 최종 split + finalize (5379-5454)
```js
innerLines.push(...downRoofLines) // 하단 지붕 모두 합류
// roof.lines 각 라인을 innerLines 끝점들로 분할
innerLinesPoints = unique(innerLines.{x1,y1,x2,y2})
roof.lines.forEach((currentLine, idx) => {
if (innerLine 이 currentLine 을 완전 포함 또는 그 반대) skip // hasOverlapLine 가드
splitPoint = innerLinesPoints 중 currentLine 위에 있고 끝점 아닌 것
if (splitPoint > 0):
// 첫 분할 = drawHipLine(prevDegree)
// 중간 = drawRoofLine
// 마지막 = splitPoint===1 이면 drawRoofLine, 아니면 drawHipLine(nextDegree)
else:
// 통째로 drawRoofLine
})
roof.innerLines = innerLines // ← 덮어쓰기 (drawGableRoof 의 push 와 다름!)
canvas 의 'check' 객체 제거 + renderAll
```
## 13. 수정 작업 시 어느 stage 인지 식별 가이드
| 증상 | 의심 stage |
|---|---|
| 한쪽흐름 추녀가 안 그려짐 | 5-1 sheds (양옆 GABLE 가드) |
| 팔작 모서리 잘못 그려짐 | 5-2 hipAndGables, prev/nextHipVector inPolygon 분기 |
| 처마 hip 길이가 비대칭 | 5-3 ridgeEaves proceedEaves alreadyPrev/Next 길이 비교 |
| 마루가 코너 직전에서 끊김/넘침 | getRidgeDrivePoint 또는 isOverlapBefore/After offset 분기 |
| 반절처 추녀마루 안 생김 | 5-4 jerkinHeads hipIntersection 또는 width >= roofLength 가드 |
| 케라바 ridge 가 비뚤어짐 | 5-5 gables stdLine 결정/oppositeLine 보정 |
| `connectCnt`/`gableId` 충돌 | 6-3 GABLE_LINE 분기 |
| 가짜 ridge 가 남음 | 7 가선분 잔여 또는 8 벽취합 잔여 |
| 형이동 처마 위로 추녀마루 누락 | 11 downRoofEaves joinHipLine 분기 |
| roof boundary 가 hip 으로 잘못 분류 | 12 최종 split splitPoint 0개 케이스 |
| 라인이 두 번 그려짐 | `alreadyPoints(innerLines, point)` 가드 누락 또는 splitPoint 의 hasOverlapLine 미작동 |
## 14. 함정 모음
1. **`linesAnalysis``innerLines` 혼동 금지**. 가선분 메타와 그려진 QLine 은 동기화되지 않음. 어떤 코드는 즉시 그리고 어떤 코드는 가선분만 push 한다.
2. **`drawRoofLine` 의 name 이 SUBLINE.HIP** — 후처리(평균화) 에서 hip 로 분류됨. 이름 보고 type 구분 금지.
3. **TYPES.NEW = 가선분 분기 결과** — bisector 로 만들어진 가상 ridge/hip. degree 는 isDiagonal 일 때만 cLine.degree, 아니면 0 (=plane).
4. **GABLE_LINE.connectCnt** — 0/1/2 단계로 increment. 2 이상이면 더미 가선분만 발생, 본인은 더 이상 그리지 않음.
5. **roof.adjustRoofLines** — downRoofEaves 단계에서만 push. 회로/모듈 배치 단계에서 참조. roof.innerLines 와 별도.
6. **MAX_ITERATIONS=1000** — 무한루프 방지지만 도달하면 silent 미완성 (경고 없음). 케이스 추가 시 수렴 보장 확인 필수.
7. **`joinHipLine.set(...)`** (5278) — 기존 라인을 mutate 한다. 즉 5단계 이전에 만들어진 hip 의 좌표가 바뀐다.
8. **`checkWallPolygon`** (1761) — `baseLines.map(line => ({x:line.x1, y:line.y1}))` 로 만듦. zero-merge 후의 baseLines 시작점만 사용. 전체 라인 좌표가 아님.
9. **여러 곳에서 `intersectionsByRoof[0]`/`intersectPoints1[0]` 직접 접근** — 비어있을 가능성 가드 없음. 케이스 추가 시 NPE 위험.
10. **proceedRidges/proceedEaves 는 ridgeEaves 루프 전용 누적기** — 다른 단계(hipedEaves, jerkinHeads, gables)에서 보지 않음. 즉 케이스 간 충돌 가능.

View File

@ -0,0 +1,171 @@
---
name: qpolygon-utils 지붕 생성 구조
description: src/util/qpolygon-utils.js (6507줄) 의 지붕 그리기 3대 진입점(drawGableRoof / drawShedRoof / drawRoofByAttribute) + 하부 헬퍼 + 호출 경로 매핑. 지붕 생성 로직 수정 전 필독.
type: project
---
# qpolygon-utils 지붕 생성 구조 (수정 작업 전 reference)
`src/util/qpolygon-utils.js` 는 6507줄·49개 top-level decl. **지붕 그리기는 이 파일이 단독 책임**(용마루/SK 케이스만 `skeleton-utils.js` 로 분기).
## 1. 호출 진입점 (caller side)
**유일한 호출자**: `QPolygon.drawHelpLine(settingModalFirstOptions, forceRedraw=false)``src/components/fabric/QPolygon.js:472`
분기 (`drawHelpLine` 내부, `QPolygon.js:584-602`):
```
types = lines.map(l => l.attributes.type)
if every type === EAVES:
if verifyMoveBoundary === 'crossed': drawSkeletonRidgeRoofFromBaseLines ← skeleton-utils
else: drawSkeletonRidgeRoof ← skeleton-utils
elif isGableRoof(types): drawGableRoof ← qpolygon-utils
elif isShedRoof(types, lines): drawShedRoof ← qpolygon-utils
else: drawRoofByAttribute ← qpolygon-utils (catch-all)
```
판정 함수 (모두 QPolygon.js inline):
- `isGableRoof(types)`: 홀짝 인덱스로 EAVES/GABLE(or JERKINHEAD) 교차 패턴 — 박공(A/B형)
- `isShedRoof(types, lines)`: SHED 포함 + SHED 라인들이 평행 + SHED 반대편이 모두 EAVES + 나머지가 GABLE/JERKINHEAD
- 그 외(혼합)는 모두 `drawRoofByAttribute`
`drawHelpLine` 호출 지점 (재빌드 트리거):
- `hooks/useMode.js:1561`
- `hooks/roofcover/useMovementSetting.js:841,985`
- `hooks/roofcover/useRoofShapeSetting.js:502`
- `hooks/roofcover/usePropertiesSetting.js:176`
- `hooks/roofcover/useEavesGableEdit.js:276,280`
- `QPolygon.js:664``forceRedraw=true` wrapper (verifyMoveBoundary crossed 가드 우회용)
`drawHelpLine` 직후(`QPolygon.js:606-647`)에 **대칭 hip 쌍 dimension 평균화** 후처리: ±5mm 묶어 평균 plane/actual 산정 → 0.5 단위 round → 라인 attribute + lengthText 동기화. (round-off 비대칭 보정용)
## 2. qpolygon-utils.js 섹션 맵
| 라인 범위 | 카테고리 | 주요 함수 |
|---|---|---|
| 10-100 | 상수·기본 | `EPSILON=1e-10`, `defineQPolygon`, `calculateAngle`, `inward/outwardEdgeNormal`, `createPolygon` |
| 102-372 | 다각형 offset/clip | `edgesIntersection`, `appendArc`, `createOffsetEdge`, `createMargin/PaddingPolygon`, `clipOffsetToOriginal` |
| 373-456 | 자가교차/중복 | `cleanSelfIntersectingPolygon`, `offsetPolygon`(default export), `removeDuplicatePolygons` |
| 478-651 | 검증/유틸 | `doSegmentsIntersect`, `checkPolygonSelfIntersection`, `isValidPoints`, `isSamePoint`(<=2 tol) |
| **659-1585** | **drawGableRoof** | 박공 (A/B 패턴) |
| **1593-1672** | **drawShedRoof** | 한쪽흐름 |
| **1680-5455** | **drawRoofByAttribute** | 변별 설정 (catch-all, ~3777줄) |
| 5467-5493 | createArrow | (디버그/방향 화살표) |
| 5501-5702 | 벡터/교점 헬퍼 | `lineIntersection`, `clamp01`, `isParallel`, `getBisectLines/BaseLines`, `almostEqual`, `getDirection`, `alreadyPoints` |
| **5712-5841** | **drawing primitives** | `drawHipLine`, `drawRidgeLine`, `drawRoofLine` |
| 5848-5883 | 벡터 정규화 | `normalizeVector`, `getHalfAngleVector` |
| 5890-5936 | reDrawPolygon | polygon 재생성 |
| 6167-6262 | 사이즈/변환 | `arePointsEqual`(<=1 tol), `toGeoJSON`, `calcLinePlaneSize`(×10 round), `calcLineActualSize`(/cos), `calcLineActualSize2`(tan, str return), `calcLineActualSizeByLineLength`, `createLinesFromPolygon` |
| 6378-6507 | 정렬 | `getSortedOrthogonalPoints` (시계/반시계 방향 진행규칙 기반) |
## 3. drawGableRoof — 박공 (659-1585)
**입력 데이터**:
- `roof` = `canvas.objects.find(id===roofId)` (POLYGON_TYPE.ROOF)
- `wall` = `canvas.objects.find(name===POLYGON_TYPE.WALL && attributes.roofId===roofId)`
- `baseLines = wall.baseLines.filter(planeSize>0)`
- `eavesLines = baseLines.filter(type===WALLLINE.EAVES)`
**내부 헬퍼** (모두 클로저):
- `analyzeEavesLine(line)` — 처마 분석. horizontal/vertical/diagonal 분류 (tolerance 1°), `roofVector` (안쪽 향한 단위벡터), 매칭되는 roofLine 검색 (offset=0 이면 wall 자기 자신을 fallback)
- `isOverlapLine(curr, check)` — 같은 방향 + 같은 좌표축 + range 겹침
- `analyzeAllEavesLines(lines)``forwardLines`(directionVector x>0 또는 y>0) / `backwardLines` 분리, 같은 방향 겹침은 합병
- `calculateIntersection(distance, angleA, angleB)``tan` 으로 마루 정점까지의 거리 분배
- `findPloygonLineOverlap(polygon, linePoints)` — line 을 polygon 경계 안쪽으로 클립
- `findInnerRidge(currentLine, roofVector, lines, tolerance=1)` — currentLine 안쪽에 있는 ridge 라인들을 거리 정렬해 ridge1/ridge2 매핑
- `drawRoofPlane(currentLine)` — eaves 1개당 지붕면 1개 빌드
**전체 흐름**:
1. `analyzeAllEavesLines(eavesLines)` → forward/backward 페어 묶기
2. `forwardLines.forEach`: backward 매칭 찾고 `calculateIntersection` 으로 각도별 정점 → `findPloygonLineOverlap` 으로 ridge 좌표 클립 → `drawRidgeLine` 호출 → `ridgeLines[]` 추가
3. forward+backward 각각에 `drawRoofPlane` 호출 → 안쪽 ridge 와 inner roof line 매칭, `getSortedOrthogonalPoints` 정렬, 같은 방향이면 `drawRoofLine` 다른 방향이면 `drawHipLine` 호출 → `innerLines[]` 추가
4. `roof.innerLines.push(...ridgeLines, ...innerLines)`
5. `name==='check'` 디버그 객체 제거 + `canvas.renderAll()`
## 4. drawShedRoof — 한쪽흐름 (1593-1672)
**가드**: `roof.lines` 에 대각선(`|dx|>1 && |dy|>1`)이 있으면 **silent return** (alert 주석처리됨).
**처리**:
1. lines 를 `sheds` / `gables` / `eaves` 로 분리 (WALLLINE.SHED/GABLE/EAVES)
2. shed 의 `pitch``shedDegree` 산출
3. `gables.forEach`: `actualSize = calcLineActualSize(coord, shedDegree)` 갱신
4. shed × eaves 페어마다 `pitchSizeLine` (중간축 점선) 생성 — `attributes.type='pitchSizeLine'`, dashArray `[5,5]`
5. **최장 라인 1개만** canvas.add (다른 건 버림 — 의도된 동작인지 확인 필요)
## 5. drawRoofByAttribute — 변별 설정 (1680-5455)
**catch-all**. 박공/한쪽흐름이 아닌 모든 케이스 (요세무네, 맨사드, 형이동, 벽취합 혼합 등).
**전반부 (1680-1759) 형이동 흡수 라인 병합**:
- `wall.baseLines``planeSize<EPSILON` 인덱스를 `zeroLines[]`
- 각 zero 라인의 prev/next 페어를 `mergedLines[]`
- 같은 방향 vector 의 라인들을 `indexList` 합쳐 `[startLine.x1, startLine.y1, endLine.x2, endLine.y2]` 로 한 라인으로 압축, `setLength` fire
- 결과 `baseLines` 재구성
**중반부 (1766-...) 벽취합+offset 보강**:
- `WALLLINE.WALL && offset>0` 인 currentLine 마다 prev/next 페어와 함께 roof.lines 의 대응 라인 찾고
- `addRoofLine1`, `addRoofLine2` (corner extension) 을 `attributes.type=WALLLINE.ETC, name='addRoofLine'` 으로 추가
**후반부 (~5380-5455)**:
- `innerLines` 의 모든 끝점 수집 → `innerLinesPoints[]`
- `roof.lines` 각 라인이 inner 와 겹치면 skip, 아니면 `splitPoint` 로 분할
- splitPoint 첫번째: `drawHipLine(prevDegree)`, 그 다음: `drawRoofLine`, 마지막 splitPoint: `drawHipLine(nextDegree)`
- 분할 없으면 `drawRoofLine` 1개
- `roof.innerLines = innerLines` (덮어쓰기. drawGableRoof 의 push 와 다름!)
- `name==='check'` 제거 + render
## 6. Drawing primitives (5712-5841)
세 함수 모두 `new QLine(points, {...})` 만들어 `canvas.add(line) → bringToFront() → renderAll()` 후 라인 객체 반환.
| 함수 | name | stroke | actualSize 계산 | 비고 |
|---|---|---|---|---|
| `drawHipLine(points, canvas, roof, textMode, prevDegree, currentDegree)` | `SUBLINE.HIP` | `#1083E3` | prev===current 이면 `calcLineActualSize(points, currentDegree)`, 다르면 `0` | 대각선이면 hypotenuse/base/tan 로 prev·current degree 재산출 (`|degreeX-degreeY|<1` 일 때만 적용) |
| `drawRidgeLine(points, canvas, roof, textMode)` | `SUBLINE.RIDGE` | `#1083E3` | `calcLinePlaneSize` (수평 거리 그대로) | actual==plane |
| `drawRoofLine(points, canvas, roof, textMode)` | **`SUBLINE.HIP`** (의도적 재사용) | `#1083E3` | `calcLinePlaneSize` | hip 와 같은 name 으로 분류됨 — 대칭 hip 평균화 후처리에 영향 |
**공통 attributes**: `{ roofId, planeSize, actualSize }`. parentId=roof.id, fontSize=roof.fontSize, strokeWidth=2.
## 7. 데이터 모델 (수정 작업 시 핵심)
### Wall/Roof QPolygon
- `wall.baseLines`: `QLine[]`. 각 라인 `attributes`:
- `type`: `LINE_TYPE.WALLLINE.*` (EAVES, GABLE, SHED, WALL, JERKINHEAD, ETC, ...)
- `planeSize`, `actualSize`, `offset`, `pitch`
- `originPoint: { x1, y1, x2, y2 }` (offset 적용 전 원본)
- `roof.lines`: 지붕 외곽 boundary 라인들 (offset 적용 후 또는 wall 동일)
- `roof.innerLines`: 추녀/마루/지붕선 등 그려진 inner 라인 컬렉션 (drawHelpLine 시작 시 비워짐)
### LINE_TYPE / POLYGON_TYPE (`src/common/common.js`)
- `LINE_TYPE.WALLLINE`: DEFAULT, EAVES, EAVE_HELP_LINE, GABLE(+LEFT/RIGHT), WALL, HIPANDGABLE, JERKINHEAD, SHED, ETC
- `LINE_TYPE.SUBLINE`: HIP(추녀), RIDGE(마루), GABLE, VERGE, ONESIDE_FLOW_RIDGE, YOSEMUNE, VALLEY, L_ABANDON_VALLEY, MANSARD, WALL_COLLECTION(+TYPE/FLOW/+L/R)
- `POLYGON_TYPE`: ROOF, WALL, TRESTLE, MODULE_SETUP_SURFACE, MODULE, OBJECT_SURFACE
## 8. 수정 작업 시 주의사항
1. **drawHelpLine 진입 직후 innerLines 가 비워진다** (`QPolygon.js:489-505`). 즉 매번 from-scratch 그리기 — undo/redo 시도 마찬가지.
2. **drawGableRoof 는 push, drawRoofByAttribute 는 덮어쓰기**`roof.innerLines`. 일관성 없음.
3. **drawShedRoof 의 silent return** — 대각선 케이스가 들어오면 아무것도 안 그린다. (이전엔 alert 였음)
4. **drawRoofLine 의 name=SUBLINE.HIP** — 후처리(대칭 hip 평균화)에서 hip 로 분류됨. 의도적이지만 함정 포인트.
5. **calcLinePlaneSize 는 ×10 round** — 모든 사이즈가 10배 정수로 저장됨 (mm 단위). UI 표시 시 /10.
6. **tolerance 들이 다 다름**:
- `EPSILON=1e-10` 수학 비교
- 방향 분류 1° / 1px
- `arePointsEqual` 1px / `isSamePoint` 2px
- 후처리 hip 평균화 5mm
- SHOULDER_ABSORBED 임계 < 10 (별도 메모리 참조)
7. **wall.baseLines 의 zero-length 라인** = 형이동 흡수 결과. drawRoofByAttribute 만 합병 처리. 다른 두 함수는 `planeSize>0` 으로 필터만 함.
8. **`name==='check'` 객체** = 디버그 화살표/마커. 모든 draw 함수가 마지막에 제거.
9. **skeleton-utils.js (5227줄)** 는 별도 파일. 용마루(전체 EAVES) 케이스 전용. qpolygon-utils 와 데이터 모델 일부 공유하지만 로직 독립.
10. **현재 브랜치 `feature/redo-undo`** — 최근 커밋 3개가 모두 지붕 형상 undo/redo 관련. 수정 시 history 정합성 영향 고려 필요.
## 9. 권장 작업 순서
지붕 생성 로직 수정 시:
1. 어떤 케이스(박공/한쪽흐름/변별) 가 영향 받는지 먼저 식별 (`isGableRoof`/`isShedRoof` 분기 추적)
2. 해당 함수의 입력 데이터 (wall.baseLines / roof.lines) 의 invariant 확인 — `planeSize>0` 필터링 여부, type 분포
3. drawing primitive (drawHip/Ridge/RoofLine) 의 attributes 가 후처리(hip 평균화, lengthText 표시) 와 정합 유지하는지 확인
4. drawHelpLine 호출 7곳 모두에서 동작 검증 (특히 `useMovementSetting`, `useRoofShapeSetting`)
5. undo/redo 의 경우 `roof.innerLines` 가 매 재호출마다 from-scratch 라는 점 유의 (history snapshot 은 wall.baseLines/roof.lines 만으로 결정적)

View File

@ -0,0 +1,77 @@
---
name: skeleton-utils 용마루 지붕 두 경로 차이
description: src/util/skeleton-utils.js 의 drawSkeletonRidgeRoof(일반) vs drawSkeletonRidgeRoofFromBaseLines(오버 전용) 차이 + skeletonBuilder 전처리 파이프라인 + SK_INPUT_USE_WALL_BASELINE_DIRECT 플래그. 용마루(전체 EAVES) 케이스 수정 전 reference.
type: project
---
# skeleton-utils 용마루 지붕 — 일반 vs 오버 두 경로
`src/util/skeleton-utils.js`. **용마루 지붕(라인 type 이 전부 EAVES)** 케이스 전용. straight-skeleton(`SkeletonBuilder.BuildFromGeoJSON`)으로 용마루/추녀 내부선 생성. 박공/한쪽흐름/변별은 `qpolygon-utils.js` 가 담당 (별도 메모리 참조: [[qpolygon-utils 지붕 생성 구조]]).
## 1. 분기 (QPolygon.js:677-684)
`drawHelpLine` 에서 `types.every(EAVES)` 일 때:
```
verdict = verifyMoveBoundary(this.id, this.canvas)
if verdict === 'crossed': drawSkeletonRidgeRoofFromBaseLines ← 오버(경계 넘음) 전용
else: drawSkeletonRidgeRoof ← 일반
```
`'crossed'` = 마루/벽 이동이 roof 경계를 넘어선 "오버" 상태. wall.baseLines 가 이미 오버된 좌표로 mutate 됨.
## 2. drawSkeletonRidgeRoof (일반, :71-75)
```js
export const drawSkeletonRidgeRoof = (roofId, canvas, textMode) => {
skeletonBuilder(roofId, canvas, textMode) // 얇은 래퍼
}
```
실체는 `skeletonBuilder` (:922). 전처리 파이프라인:
1. roof.lines ↔ wall.lines index 매칭으로 `wallLine` id 주입
2. `baseLines = wall.baseLines.filter(planeSize>0)`, `createOrderedBasePoints` 로 roof.points 순서 정렬
3. **45° 대각 확장 계산** (:1011-1066): contactData(baseLine→roofLine 방향벡터) → `maxStep`(전체 꼭짓점 중 min(|dx|,|dy|) 의 max) → centroid 보정 → `changRoofLinePoints` (maxContactDistance=√2·maxStep 확장)
4. 마루이동(`moveFlowLine`/`moveUpDown` ≠ 0) 시 `safeMovedPointsWithFallback``movedPoints` 로 대체 (:1071-1121)
5. **⚠️ `SK_INPUT_USE_WALL_BASELINE_DIRECT = true` (:36, :1128)** → 위 3·4 결과를 **전부 무시**하고 `orderedBaseLinePoints`(=wall.baseLines corner) 직접 사용. + 평행오버 corner 흡수(DUP_EPS=1.0, COLLINEAR_EPS=50.0, 최대 5패스)
6. 좌표 유효성/중복점/일직선/**방향 뒤집힘(isOverDetected, cross product 부호 반전)** 검출 (:1175-1236)
7. `roof.skeletonPoints` 갱신 → SK 빌드 → `createInnerLinesFromSkeleton`
즉 45° 확장 코드는 **계산만 하고 버려지는** 상태(점진 리팩토링 흔적). 실입력은 baseLines corner.
## 3. drawSkeletonRidgeRoofFromBaseLines (오버 전용, :1363-1444)
헤더 주석(:1350-1362) 의 존재 이유: 일반 경로의 45° 확장/roof 경계 클램핑이 오버 상태에선 **폴리곤 자가교차/경계 되돌림**을 일으켜 엉뚱한 SK 를 만든다 → 전처리 다 건너뛰고 baseLines 그대로 입력.
처리:
1. `wall.baseLines` 순차 끝점(`{x:bl.x1, y:bl.y1}`) 수집, **확장·offset 없이 그대로**. `planeSize<1`(SHOULDER_ABSORBED) 스킵
2. rawPoints < 3 이면 중단
3. `roof.skeletonPoints = rawPoints` (오버 좌표 그대로)
4. `perturbPolygonPoints``toGeoJSON``SkeletonBuilder.BuildFromGeoJSON`
5. `createInnerLinesFromSkeleton(..., isOverDetected=true, rawPoints)`
6. `canvas.skeletonStates[roofId]=true`, `canvas.skeletonLines` 갱신
7. **`canvas.skeleton.lastPoints` 는 기존 값 보존** (`__preservedLastPoints`) — 오버 상태에서 새 누적 기준점 안 만듦
**건드리지 않는 것**: `roof.points` / `roof.lines` / `outerLine` / `lengthText` (주석 :1358 명시).
## 4. 핵심 차이 요약
| 항목 | drawSkeletonRidgeRoof (일반) | ...FromBaseLines (오버) |
|---|---|---|
| 트리거 | EAVES 전부 + verdict ≠ crossed | EAVES 전부 + verdict === crossed |
| 실체 | skeletonBuilder 래퍼 | 자체 SK 빌드 |
| SK 입력 | 전처리 파이프라인(but 플래그로 baseLine 직접) | baseLines 끝점 그대로 |
| roof.points/lines/outerLine/lengthText | 마루이동 시 갱신·검증 | **불변 (side-effect free)** |
| lastPoints | 새로 계산/누적 | **보존** |
| isOverDetected | cross 부호 반전 검출로 산출 | 무조건 true 마킹 |
| 공통 | createInnerLinesFromSkeleton, planeSize<1 스킵 | 동일 |
**현 시점 진짜 차이**: 플래그(`SK_INPUT_USE_WALL_BASELINE_DIRECT=true`) 때문에 두 함수의 SK 입력 좌표는 거의 같아짐. 실질 차이는 **주변 상태(roof.points/lastPoints 등) 를 mutate 하느냐 보존하느냐**. 오버를 undo/redo 단축수단으로 쓸 때 history snapshot(wall.baseLines/roof.lines) 오염 없이 SK 만 재그리기 위함 (현재 `feature/redo-undo` 브랜치 맥락).
## 5. 수정 시 주의
1. **45° 확장 코드(:1011-1066) 는 dead 입력**`SK_INPUT_USE_WALL_BASELINE_DIRECT` 끄지 않는 한 결과 미사용. 만지기 전 플래그 확인.
2. `createInnerLinesFromSkeleton` (:1455) 은 두 경로 공유 — 여기 바꾸면 양쪽 영향. `processEavesEdge`(처마) + `processGableEdge`(케라바 후처리) 호출.
3. 오버 경로는 `lastPoints` 보존이 의도 — 무심코 갱신하면 누적 마루이동 기준 붕괴.
4. 예외 시 보호 정책(:1341-1346): SK/innerLines 유지, `skeletonStates[roofId]=false` 만, `skeletonLines` 비우지 않음 (이전 성공 상태 보존).
5. `verifyMoveBoundary` 는 QPolygon.drawHelpLine 과 skeletonBuilder(:1077) **두 곳에서** 호출 — 'crossed' 시 경고 로그만, 흐름은 진행.

15
.claude/settings.json Normal file
View File

@ -0,0 +1,15 @@
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "CMD=$(python3 -c \"import json,sys; d=json.load(sys.stdin); print(d.get('tool_input',d).get('command',''))\" 2>/dev/null || true); set -- $CMD; case \"$1\" in grep|egrep|fgrep|rg|ripgrep|find|fd|ack|ag) [ -f graphify-out/graph.json ] && echo '{\"hookSpecificOutput\":{\"hookEventName\":\"PreToolUse\",\"additionalContext\":\"graphify: Knowledge graph exists. Read graphify-out/GRAPH_REPORT.md for god nodes and community structure before searching raw files.\"}}' || true ;; esac"
}
]
}
]
}
}

15
.codex/hooks.json Normal file
View File

@ -0,0 +1,15 @@
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "CMD=$(python3 -c \"import json,sys; d=json.load(sys.stdin); print(d.get('tool_input',d).get('command',''))\" 2>/dev/null || true); set -- $CMD; case \"$1\" in grep|egrep|fgrep|rg|ripgrep|find|fd|ack|ag) [ -f graphify-out/graph.json ] && echo '{\"hookSpecificOutput\":{\"hookEventName\":\"PreToolUse\",\"additionalContext\":\"graphify: Knowledge graph exists. Read graphify-out/GRAPH_REPORT.md for god nodes and community structure before searching raw files.\"}}' || true ;; esac"
}
]
}
]
}
}

Some files were not shown because too many files have changed in this diff Show More