Compare commits

...

579 Commits

Author SHA1 Message Date
b2ff662335 Merge pull request '물건정보에서 저장이후 그림으로 클릭시 에러발생' (#104) from feature/ysCha into dev
Reviewed-on: #104
2025-06-09 17:09:26 +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
5c1c50bd46 resetCommonUtils 위치 수정 2025-06-05 15:22:14 +09:00
c65615465a 팝업 전 처리 추가 2025-06-05 15:16:11 +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
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
514545cf65 복사 로직 오류 수정
[작업내용] : 복사 로직 오류 수정
2025-06-04 09:03:21 +09:00
387a1fd577 견적서 복사 로직 수정
[작업내용] : 이미지 존재 여부 확인 후 복사 진행
2025-06-04 08:54:00 +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
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
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
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
806c829ada 지붕면 할당 시 alert 제거 2025-06-02 10:42:18 +09:00
286dad7021 [1076] : [金属縦葺・ハゼ式折板・重ね式折板・瓦棒葺(芯木なし)の屋根とラックレス工法の組み合わせの時、働き幅によるモジュール設置制限ができていない。]
[작업내용] : 망둥어 피치 값 변경
2025-06-02 10:16:23 +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
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
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
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
27e923ecd4 Fix build script in package.json to correctly reference the production environment file. 2025-05-29 16:49:17 +09:00
308e41ac26 Fix background image path in useRefFiles hook to use AWS S3 URL format. 2025-05-29 16:47:17 +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
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
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
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
김민식
2ee0dc45f0 [1068] : [[오류] 견적서 생성된 플랜복사 --> 복사된 플랜에서 모듈삭제 --> 견적서 진입이 됨..]
[작업내용] : 회로설정 -> 수동 할당 시 회로 및 가대 삭제
2025-05-28 10:37:02 +09:00
4e8cc409d4 #1068 [오류] 견적서 생성된 플랜복사 --> 복사된 플랜에서 모듈삭제 --> 견적서 진입이 됨.. 2025-05-28 10:26:38 +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
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
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
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
dc6989b303 지붕재 목록 Header에서 조회 2025-05-26 09:52:30 +09:00
2cbcd19b65 - 동선 이동 시 라인 안잡히는 현상 수정 2025-05-23 15:55:26 +09:00
김민식
c029560c4a 외벽선 작성 팝업 안내 문구 추가 및 관련 다국어 추가 2025-05-23 15:49:33 +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
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
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
37b12b5deb 번역 오류 수정
[작업내용] : alert 문구 ? 삭제
2025-05-23 10:24:21 +09:00
8f693478b8 [995] : [回路組について]
[작업내용] : 번역 누락 수정
2025-05-23 10:20:42 +09:00
Jaeyoung Lee
5ab58de70b a,b 타입 버그 수정 2025-05-22 13:31:55 +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
149 changed files with 15323 additions and 4590 deletions

View File

@ -1,11 +1,32 @@
NEXT_PUBLIC_API_SERVER_PATH="http://1.248.227.176:38080"
NEXT_PUBLIC_RUN_MODE="development"
NEXT_PUBLIC_HOST_URL="http://1.248.227.176:4000"
NEXT_PUBLIC_API_SERVER_PATH="https://dev-api.hanasys.jp"
NEXT_PUBLIC_HOST_URL="//1.248.227.176:4000"
NEXT_PUBLIC_API_HOST_URL="https://dev.hanasys.jp"
SESSION_SECRET="i3iHH1yp2/2SpQSIySQ4bpyc4g0D+zCF9FAn5xUG0+Y="
# NEXT_PUBLIC_CONVERTER_API_URL="https://v2.convertapi.com/convert/dwg/to/png?Secret=secret_bV5zuYMyyIYFlOb3"
NEXT_PUBLIC_CONVERTER_API_URL="https://v2.convertapi.com/convert/dwg/to/png?Secret=secret_yAS4QDalL9jgQ7vS"
# NEXT_PUBLIC_CONVERTER_API_URL="https://v2.convertapi.com/convert/dwg/to/png?Secret=secret_yAS4QDalL9jgQ7vS"
NEXT_PUBLIC_CONVERTER_API_URL="https://v2.convertapi.com/convert/dwg/to/png?Secret=secret_a0FLEK6M2oTpXInK"
NEXT_PUBLIC_Q_ORDER_AUTO_LOGIN_URL="http://q-order-stg.q-cells.jp:8120/eos/login/autoLogin"
NEXT_PUBLIC_Q_MUSUBI_AUTO_LOGIN_URL="http://q-musubi-stg.q-cells.jp:8120/qm/login/autoLogin"
# 테스트용
# AWS_REGION="ap-northeast-2"
# AMPLIFY_BUCKET="interplug"
# AWS_ACCESS_KEY_ID="AKIAVWMWJCUXFHEAZ4FR"
# AWS_SECRET_ACCESS_KEY="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"
# NEXT_PUBLIC_AWS_S3_BASE_URL="https://interplug.s3.ap-northeast-2.amazonaws.com"
# 실제 일본 서버
AWS_REGION="ap-northeast-1"
AMPLIFY_BUCKET="files.hanasys.jp"
AWS_ACCESS_KEY_ID="AKIA3K4QWLZHFZRJOM2E"
AWS_SECRET_ACCESS_KEY="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"
NEXT_PUBLIC_AWS_S3_BASE_URL="//files.hanasys.jp"
S3_PROFILE="dev"

32
.env.local.dev Normal file
View File

@ -0,0 +1,32 @@
NEXT_PUBLIC_RUN_MODE="local.dev"
NEXT_PUBLIC_API_SERVER_PATH="https://dev-api.hanasys.jp"
NEXT_PUBLIC_HOST_URL="//1.248.227.176:4000"
NEXT_PUBLIC_API_HOST_URL="http://1.248.227.176:5000"
SESSION_SECRET="i3iHH1yp2/2SpQSIySQ4bpyc4g0D+zCF9FAn5xUG0+Y="
# NEXT_PUBLIC_CONVERTER_API_URL="https://v2.convertapi.com/convert/dwg/to/png?Secret=secret_bV5zuYMyyIYFlOb3"
# NEXT_PUBLIC_CONVERTER_API_URL="https://v2.convertapi.com/convert/dwg/to/png?Secret=secret_yAS4QDalL9jgQ7vS"
NEXT_PUBLIC_CONVERTER_API_URL="https://v2.convertapi.com/convert/dwg/to/png?Secret=secret_a0FLEK6M2oTpXInK"
NEXT_PUBLIC_Q_ORDER_AUTO_LOGIN_URL="http://q-order-stg.q-cells.jp:8120/eos/login/autoLogin"
NEXT_PUBLIC_Q_MUSUBI_AUTO_LOGIN_URL="http://q-musubi-stg.q-cells.jp:8120/qm/login/autoLogin"
# 테스트용
# AWS_REGION="ap-northeast-2"
# AMPLIFY_BUCKET="interplug"
# AWS_ACCESS_KEY_ID="AKIAVWMWJCUXFHEAZ4FR"
# AWS_SECRET_ACCESS_KEY="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"
# NEXT_PUBLIC_AWS_S3_BASE_URL="https://interplug.s3.ap-northeast-2.amazonaws.com"
# 실제 일본 서버
AWS_REGION="ap-northeast-1"
AMPLIFY_BUCKET="files.hanasys.jp"
AWS_ACCESS_KEY_ID="AKIA3K4QWLZHFZRJOM2E"
AWS_SECRET_ACCESS_KEY="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"
NEXT_PUBLIC_AWS_S3_BASE_URL="//files.hanasys.jp"
S3_PROFILE="dev"

32
.env.localhost Normal file
View File

@ -0,0 +1,32 @@
NEXT_PUBLIC_RUN_MODE="local"
NEXT_PUBLIC_API_SERVER_PATH="https://dev-api.hanasys.jp"
NEXT_PUBLIC_HOST_URL="//1.248.227.176:4000"
NEXT_PUBLIC_API_HOST_URL="http://localhost:3000"
SESSION_SECRET="i3iHH1yp2/2SpQSIySQ4bpyc4g0D+zCF9FAn5xUG0+Y="
# NEXT_PUBLIC_CONVERTER_API_URL="https://v2.convertapi.com/convert/dwg/to/png?Secret=secret_bV5zuYMyyIYFlOb3"
# NEXT_PUBLIC_CONVERTER_API_URL="https://v2.convertapi.com/convert/dwg/to/png?Secret=secret_yAS4QDalL9jgQ7vS"
NEXT_PUBLIC_CONVERTER_API_URL="https://v2.convertapi.com/convert/dwg/to/png?Secret=secret_a0FLEK6M2oTpXInK"
NEXT_PUBLIC_Q_ORDER_AUTO_LOGIN_URL="http://q-order-stg.q-cells.jp:8120/eos/login/autoLogin"
NEXT_PUBLIC_Q_MUSUBI_AUTO_LOGIN_URL="http://q-musubi-stg.q-cells.jp:8120/qm/login/autoLogin"
# 테스트용
# AWS_REGION="ap-northeast-2"
# AMPLIFY_BUCKET="interplug"
# AWS_ACCESS_KEY_ID="AKIAVWMWJCUXFHEAZ4FR"
# AWS_SECRET_ACCESS_KEY="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"
# NEXT_PUBLIC_AWS_S3_BASE_URL="https://interplug.s3.ap-northeast-2.amazonaws.com"
# 실제 일본 서버
AWS_REGION="ap-northeast-1"
AMPLIFY_BUCKET="files.hanasys.jp"
AWS_ACCESS_KEY_ID="AKIA3K4QWLZHFZRJOM2E"
AWS_SECRET_ACCESS_KEY="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"
NEXT_PUBLIC_AWS_S3_BASE_URL="//files.hanasys.jp"
S3_PROFILE="dev"

View File

@ -1,13 +1,33 @@
NEXT_PUBLIC_RUN_MODE="production"
NEXT_PUBLIC_API_SERVER_PATH="https://api.hanasys.jp/"
NEXT_PUBLIC_HOST_URL="http://1.248.227.176:4000"
NEXT_PUBLIC_HOST_URL="//1.248.227.176:4000"
NEXT_PUBLIC_API_HOST_URL="https://www.hanasys.jp"
SESSION_SECRET="i3iHH1yp2/2SpQSIySQ4bpyc4g0D+zCF9FAn5xUG0+Y="
# NEXT_PUBLIC_CONVERTER_API_URL="https://v2.convertapi.com/convert/dwg/to/png?Secret=secret_bV5zuYMyyIYFlOb3"
NEXT_PUBLIC_CONVERTER_API_URL="https://v2.convertapi.com/convert/dwg/to/png?Secret=secret_yAS4QDalL9jgQ7vS"
# NEXT_PUBLIC_CONVERTER_API_URL="https://v2.convertapi.com/convert/dwg/to/png?Secret=secret_yAS4QDalL9jgQ7vS"
NEXT_PUBLIC_CONVERTER_API_URL="https://v2.convertapi.com/convert/dwg/to/png?Secret=secret_a0FLEK6M2oTpXInK"
# NEXT_PUBLIC_Q_ORDER_AUTO_LOGIN_URL="https://q-order.q-cells.jp/eos/login/autoLogin"
# NEXT_PUBLIC_Q_MUSUBI_AUTO_LOGIN_URL="https://q-musubi.q-cells.jp/qm/login/autoLogin"
NEXT_PUBLIC_Q_ORDER_AUTO_LOGIN_URL="http://q-order-stg.q-cells.jp:8120/eos/login/autoLogin"
NEXT_PUBLIC_Q_MUSUBI_AUTO_LOGIN_URL="http://q-musubi-stg.q-cells.jp:8120/qm/login/autoLogin"
# AWS_REGION="ap-northeast-2"
# AMPLIFY_BUCKET="interplug"
# AWS_ACCESS_KEY_ID="AKIAVWMWJCUXFHEAZ4FR"
# AWS_SECRET_ACCESS_KEY="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"
# NEXT_PUBLIC_AWS_S3_BASE_URL="//files.hanasys.jp"
# 실제 일본 서버
AWS_REGION="ap-northeast-1"
AMPLIFY_BUCKET="files.hanasys.jp"
AWS_ACCESS_KEY_ID="AKIA3K4QWLZHFZRJOM2E"
AWS_SECRET_ACCESS_KEY="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"
NEXT_PUBLIC_AWS_S3_BASE_URL="//files.hanasys.jp"
S3_PROFILE="prd"

3
.gitmessage.txt Normal file
View File

@ -0,0 +1,3 @@
[일감번호] : [제목]
[작업내용] :

View File

@ -34,3 +34,5 @@ You can check out [the Next.js GitHub repository](https://github.com/vercel/next
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
deploy test

13
dev.ecosystem.config.js Normal file
View File

@ -0,0 +1,13 @@
module.exports = {
apps: [
{
name: 'qcast-front-development',
script: 'node_modules/next/dist/bin/next',
instances: 1,
exec_mode: 'fork',
env: {
PORT: 5010,
},
},
],
}

View File

@ -0,0 +1,13 @@
module.exports = {
apps: [
{
name: 'qcast-front-local-development',
script: 'node_modules/next/dist/bin/next',
instances: 1,
exec_mode: 'fork',
env: {
PORT: 5000,
},
},
],
}

13
ecosystem.config.js Normal file
View File

@ -0,0 +1,13 @@
module.exports = {
apps: [
{
name: 'qcast-front-production',
script: 'node_modules/next/dist/bin/next',
instances: 2,
exec_mode: 'cluster',
env: {
NODE_ENV: 'production',
},
},
],
}

View File

@ -3,27 +3,34 @@
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start -p 5000",
"start:dev": "next start -p 5010",
"dev": "env-cmd -f .env.localhost next dev",
"local:dev": "env-cmd -f .env.local.dev next dev",
"build": "env-cmd -f .env.production next build",
"build:dev": "env-cmd -f .env.development next build",
"build:local.dev": "env-cmd -f .env.local.dev next build",
"start:cluster1": "env-cmd -f .env.production next start -p 5000",
"start:cluster2": "env-cmd -f .env.production next start -p 5001",
"start:dev": "env-cmd -f .env.development next start -p 5010",
"lint": "next lint",
"serve": "node server.js"
},
"dependencies": {
"@aws-sdk/client-s3": "^3.772.0",
"ag-grid-react": "^32.0.2",
"axios": "^1.7.8",
"big.js": "^6.2.2",
"chart.js": "^4.4.6",
"dayjs": "^1.11.13",
"env-cmd": "^10.1.0",
"fabric": "^5.3.0",
"framer-motion": "^11.2.13",
"fs": "^0.0.1-security",
"iron-session": "^8.0.2",
"jimp": "^1.6.0",
"js-cookie": "^3.0.5",
"mathjs": "^13.0.2",
"mssql": "^11.0.1",
"next": "14.2.21",
"next": "14.2.28",
"next-international": "^1.2.4",
"react": "^18",
"react-chartjs-2": "^5.2.0",
@ -37,6 +44,7 @@
"react-responsive-modal": "^6.4.2",
"react-select": "^5.8.1",
"recoil": "^0.7.7",
"sharp": "^0.33.5",
"sqlite": "^5.1.1",
"sqlite3": "^5.1.7",
"sweetalert2": "^11.14.1",

13
prd1.ecosystem.config.js Normal file
View File

@ -0,0 +1,13 @@
module.exports = {
apps: [
{
name: 'qcast-front-production-1',
script: 'node_modules/next/dist/bin/next',
instances: 1,
exec_mode: 'fork',
env: {
PORT: 5000,
},
},
],
}

13
prd2.ecosystem.config.js Normal file
View File

@ -0,0 +1,13 @@
module.exports = {
apps: [
{
name: 'qcast-front-production-2',
script: 'node_modules/next/dist/bin/next',
instances: 1,
exec_mode: 'fork',
env: {
PORT: 5001,
},
},
],
}

View File

@ -0,0 +1,3 @@
<svg width="16" height="14" viewBox="0 0 16 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8 0L15.7942 13.5H0.205771L8 0Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 160 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -0,0 +1,4 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="12" cy="12" r="8.25" stroke="#101010" stroke-width="1.5"/>
<path d="M7.94995 16.5C10.0485 14.302 13.9289 14.1986 16.05 16.5M14.2455 9.75C14.2455 10.9926 13.2367 12 11.9923 12C10.7479 12 9.73912 10.9926 9.73912 9.75C9.73912 8.50736 10.7479 7.5 11.9923 7.5C13.2367 7.5 14.2455 8.50736 14.2455 9.75Z" stroke="#101010" stroke-width="1.5" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 474 B

Binary file not shown.

View File

@ -1,21 +1,13 @@
'use client'
import { createContext, useEffect, useState } from 'react'
import { useLocalStorage } from 'usehooks-ts'
import { createContext, useState } from 'react'
export const GlobalDataContext = createContext(null)
const GlobalDataProvider = ({ children }) => {
const [managementState, setManagementState] = useState(null)
const [managementStateLoaded, setManagementStateLoaded] = useLocalStorage('managementStateLoaded', null)
useEffect(() => {
if (managementState !== null) {
setManagementStateLoaded(managementState)
}
}, [managementState])
return <GlobalDataContext.Provider value={{ managementState, setManagementState, managementStateLoaded }}>{children}</GlobalDataContext.Provider>
return <GlobalDataContext.Provider value={{ managementState, setManagementState }}>{children}</GlobalDataContext.Provider>
}
export default GlobalDataProvider

View File

@ -0,0 +1,70 @@
import { NextResponse } from 'next/server'
import { S3Client, PutObjectCommand, DeleteObjectCommand } from '@aws-sdk/client-s3'
const Bucket = process.env.AMPLIFY_BUCKET
const s3 = new S3Client({
region: process.env.AWS_REGION,
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
},
})
const uploadImage = async (file) => {
console.log('🚀 ~ uploadImage ~ file:', file)
const Body = Buffer.from(await file.arrayBuffer())
const Key = `cads/${file.name}`
const ContentType = 'image/png'
await s3.send(
new PutObjectCommand({
Bucket,
Key,
Body,
ContentType,
}),
)
return {
filePath: `https://${process.env.AMPLIFY_BUCKET}.s3.${process.env.AWS_REGION}.amazonaws.com/${Key}`,
fileName: Key,
}
}
export async function POST(req) {
try {
const formData = await req.formData()
const file = formData.get('file')
const result = await uploadImage(file)
return NextResponse.json(result)
} catch (error) {
console.error(error)
return NextResponse.json({ error: 'Failed to upload image' }, { status: 500 })
}
}
export async function DELETE(req) {
try {
const searchParams = req.nextUrl.searchParams
const Key = `cads/${searchParams.get('fileName')}`
console.log('🚀 ~ DELETE ~ Key:', Key)
if (!Key) {
return NextResponse.json({ error: 'fileName parameter is required' }, { status: 400 })
}
await s3.send(
new DeleteObjectCommand({
Bucket,
Key,
}),
)
return NextResponse.json({ message: '이미지 삭제 성공' }, { status: 200 })
} catch (error) {
console.error('S3 Delete Error:', error)
return NextResponse.json({ error: 'Failed to delete image' }, { status: 500 })
}
}

View File

@ -0,0 +1,149 @@
import { NextResponse } from 'next/server'
import { DeleteObjectCommand, GetObjectCommand, PutObjectCommand, S3Client } from '@aws-sdk/client-s3'
import { v4 as uuidv4 } from 'uuid'
import { Jimp } from 'jimp'
const Bucket = process.env.AMPLIFY_BUCKET
const s3 = new S3Client({
region: process.env.AWS_REGION,
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
},
})
const checkArea = (obj) => {
const { width, height, left, top } = obj
if (left < 0 || top < 0 || width > 1600 || height > 1000) {
return false
}
return true
}
const cropImage = async (Key, width, height, left, top) => {
try {
// Get the image from S3
const { Body } = await s3.send(
new GetObjectCommand({
Bucket,
Key,
}),
)
const chunks = []
for await (const chunk of Body) {
chunks.push(chunk)
}
const buffer = Buffer.concat(chunks)
const image = await Jimp.read(buffer)
image.autocrop({ tolerance: 0.0002, leaveBorder: 10 })
return await image.getBuffer('image/png')
// Convert stream to buffer
// const chunks = []
// for await (const chunk of Body) {
// chunks.push(chunk)
// }
// const imageBuffer = Buffer.concat(chunks)
// const image = await Jimp.read(Body)
// if (!checkResult) {
// processedImage = await image.toBuffer()
// }
//let processedImage
// if (!checkResult) {
// processedImage = await sharp(imageBuffer).toBuffer()
// } else {
// processedImage = await sharp(imageBuffer)
// .extract({
// width: parseInt(width),
// height: parseInt(height),
// left: parseInt(left),
// top: parseInt(top),
// })
// .png()
// .toBuffer()
// }
// return processedImage
} catch (error) {
console.error('Error processing image:', error)
throw error
}
}
export async function POST(req) {
try {
const formData = await req.formData()
const file = formData.get('file')
const objectNo = formData.get('objectNo')
const planNo = formData.get('planNo')
const type = formData.get('type')
const width = formData.get('width')
const height = formData.get('height')
const left = formData.get('left')
const top = formData.get('top')
const OriginalKey = `Drawing/${uuidv4()}`
/**
* 원본 이미지를 우선 저장한다.
* 이미지 이름이 겹지는 현상을 방지하기 위해 uuid 사용한다.
*/
await s3.send(
new PutObjectCommand({
Bucket,
Key: OriginalKey,
Body: Buffer.from(await file.arrayBuffer()),
ContentType: 'image/png',
}),
)
/**
* 저장된 원본 이미지를 기준으로 크롭여부를 결정하여 크롭 이미지를 저장한다.
*/
const bufferImage = await cropImage(OriginalKey, width, height, left, top)
/**
* 크롭 이미지 이름을 결정한다.
*/
const Key = `Drawing/${process.env.S3_PROFILE}/${objectNo}_${planNo}_${type}.png`
/**
* 크롭이 완료된 이미지를 업로드한다.
*/
await s3.send(
new PutObjectCommand({
Bucket,
Key,
Body: bufferImage,
ContentType: 'image/png',
}),
)
/**
* 크롭이미지 저장이 완료되면 원본 이미지를 삭제한다.
*/
await s3.send(
new DeleteObjectCommand({
Bucket,
Key: OriginalKey,
}),
)
const result = {
filePath: `https://${process.env.AMPLIFY_BUCKET}.s3.${process.env.AWS_REGION}.amazonaws.com/${Key}`,
fileName: Key,
}
return NextResponse.json(result)
} catch (error) {
console.error('Error in POST:', error)
return NextResponse.json({ error: 'Failed to process and upload image' }, { status: 500 })
}
}

View File

@ -0,0 +1,59 @@
import { NextResponse } from 'next/server'
import { S3Client, CopyObjectCommand, GetObjectCommand } from '@aws-sdk/client-s3'
import sharp from 'sharp'
import { v4 as uuidv4 } from 'uuid'
const Bucket = process.env.AMPLIFY_BUCKET
const s3 = new S3Client({
region: process.env.AWS_REGION,
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
},
})
export async function POST(req) {
const { objectNo, planNo, newObjectNo, newPlanNo } = await req.json()
const responseArray = []
//견적서1 번 이미지
const isExistImage1 = await s3.send(
new GetObjectCommand({
Bucket,
Key: `Drawing/${process.env.S3_PROFILE}/${objectNo}_${planNo}_1.png`,
}),
)
//견적서2 번 이미지
const isExistImage2 = await s3.send(
new GetObjectCommand({
Bucket,
Key: `Drawing/${process.env.S3_PROFILE}/${objectNo}_${planNo}_2.png`,
}),
)
//견적서1,2 번 이미지 둘다 있어야함
if (isExistImage1.$metadata.httpStatusCode === 200 && isExistImage2.$metadata.httpStatusCode === 200) {
//견적서1 번 이미지 복사
const copyCommand = new CopyObjectCommand({
Bucket,
CopySource: encodeURI(`${Bucket}/Drawing/${process.env.S3_PROFILE}/${objectNo}_${planNo}_1.png`),
Key: `Drawing/${process.env.S3_PROFILE}/${newObjectNo}_${newPlanNo}_1.png`,
})
const response = await s3.send(copyCommand)
const copyCommand2 = new CopyObjectCommand({
Bucket,
CopySource: encodeURI(`${Bucket}/Drawing/${process.env.S3_PROFILE}/${objectNo}_${planNo}_2.png`),
Key: `Drawing/${process.env.S3_PROFILE}/${newObjectNo}_${newPlanNo}_2.png`,
})
const response2 = await s3.send(copyCommand2)
responseArray.push(response, response2)
return NextResponse.json({ message: '견적서 이미지 복사 성공', responseArray }, { status: 200 })
} else {
return NextResponse.json({ message: '견적서 이미지 복사 실패(존재하지 않는 이미지)', responseArray }, { status: 400 })
}
}

View File

@ -0,0 +1,78 @@
import { NextResponse } from 'next/server'
import { S3Client, PutObjectCommand, DeleteObjectCommand } from '@aws-sdk/client-s3'
const Bucket = process.env.AMPLIFY_BUCKET
const s3 = new S3Client({
region: process.env.AWS_REGION,
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
},
})
export async function GET(req) {
try {
const searchParams = req.nextUrl.searchParams
const q = searchParams.get('q')
const fileNm = searchParams.get('fileNm')
const zoom = searchParams.get('zoom')
/** 구글 맵을 이미지로 변경하기 위한 API */
const API_KEY = 'AIzaSyDO7nVR1N_D2tKy60hgGFavpLaXkHpiHpc'
const targetUrl = `https://maps.googleapis.com/maps/api/staticmap?center=${q}&zoom=${zoom}&maptype=satellite&size=640x640&scale=1&key=${API_KEY}`
const decodeUrl = decodeURIComponent(targetUrl)
/** 구글 맵을 이미지로 변경하기 위한 API 호출 */
const response = await fetch(decodeUrl)
const data = await response.arrayBuffer()
// const buffer = Buffer.from(data)
/** 변경된 이미지를 S3에 업로드 */
const Body = Buffer.from(data)
const Key = `maps/${fileNm}`
const ContentType = 'image/png'
await s3.send(
new PutObjectCommand({
Bucket,
Key,
Body,
ContentType,
}),
)
const result = {
filePath: `https://${process.env.AMPLIFY_BUCKET}.s3.${process.env.AWS_REGION}.amazonaws.com/${Key}`,
fileName: Key,
}
return NextResponse.json(result)
} catch (error) {
console.error(error)
return NextResponse.json({ error: 'Failed to upload image' }, { status: 500 })
}
}
export async function DELETE(req) {
try {
const searchParams = req.nextUrl.searchParams
const Key = `maps/${searchParams.get('fileName')}`
console.log('🚀 ~ DELETE ~ Key:', Key)
if (!Key) {
return NextResponse.json({ error: 'fileName parameter is required' }, { status: 400 })
}
await s3.send(
new DeleteObjectCommand({
Bucket,
Key,
}),
)
return NextResponse.json({ message: '이미지 삭제 성공' }, { status: 200 })
} catch (error) {
console.error('S3 Delete Error:', error)
return NextResponse.json({ error: 'Failed to delete image' }, { status: 500 })
}
}

View File

@ -0,0 +1,72 @@
import { NextResponse } from 'next/server'
import { S3Client, PutObjectCommand, DeleteObjectCommand } from '@aws-sdk/client-s3'
const Bucket = process.env.AMPLIFY_BUCKET
const s3 = new S3Client({
region: process.env.AWS_REGION,
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
},
})
const uploadImage = async (file) => {
const Body = Buffer.from(await file.arrayBuffer())
const Key = `upload/${file.name}`
const ContentType = file.ContentType
await s3.send(
new PutObjectCommand({
Bucket,
Key,
Body,
ContentType,
}),
)
return {
filePath: `https://${process.env.AMPLIFY_BUCKET}.s3.${process.env.AWS_REGION}.amazonaws.com/${Key}`,
fileName: Key,
}
}
export async function POST(req) {
try {
const formData = await req.formData()
const file = formData.get('file')
const result = await uploadImage(file)
result.message = '이미지 업로드 성공'
return NextResponse.json(result)
} catch (error) {
console.error(error)
return NextResponse.json({ error: 'Failed to upload image' }, { status: 500 })
}
}
export async function DELETE(req) {
try {
const searchParams = req.nextUrl.searchParams
const fileName = searchParams.get('fileName')
if (!fileName) {
return NextResponse.json({ error: 'fileName parameter is required' }, { status: 400 })
}
const Key = `upload/${fileName}`
console.log('🚀 ~ DELETE ~ Key:', Key)
await s3.send(
new DeleteObjectCommand({
Bucket,
Key,
}),
)
return NextResponse.json({ message: '이미지 삭제 성공' }, { status: 200 })
} catch (error) {
console.error('S3 Delete Error:', error)
return NextResponse.json({ error: 'Failed to delete image' }, { status: 500 })
}
}

View File

@ -0,0 +1,9 @@
import Qna from '@/components/community/Qna'
export default async function CommunityQnaPage() {
return (
<>
<Qna />
</>
)
}

View File

@ -45,8 +45,19 @@ const FloorPlanProvider = ({ children }) => {
// const pathname = usePathname()
// const setCorrentObjectNo = useSetRecoilState(correntObjectNoState)
const searchParams = useSearchParams()
const path = usePathname()
const objectNo = searchParams.get('objectNo')
const pid = searchParams.get('pid')
useEffect(() => {
setFloorPlanState((prev) => {
return {
...prev,
objectNo,
pid,
}
})
}, [path])
// useEffect(() => {
// console.log('🚀 ~ useEffect ~ objectNo:')
// if (pathname === '/floor-plan') {

View File

@ -21,8 +21,8 @@ import GlobalLoadingProvider from './GlobalLoadingProvider'
* 서버 컴포넌트에 한해서 개별로 설정할 있음
*/
export const metadata = {
title: 'HANASYS設計',
description: 'HANASYS設計',
title: 'HANASYS DESIGN',
description: 'HANASYS DESIGN',
}
/**
@ -58,6 +58,7 @@ export default async function RootLayout({ children }) {
pwdInitYn: session.pwdInitYn,
custCd: session.custCd,
isLoggedIn: session.isLoggedIn,
builderNo: session.builderNo
}
}
if (!headerPathname.includes('/login') && !session.isLoggedIn) {

View File

@ -125,6 +125,11 @@ export const TRESTLE_MATERIAL = {
BRACKET: 'bracket',
}
export const MODULE_SETUP_TYPE = {
LAYOUT: 'layout',
AUTO: 'auto',
}
export const SAVE_KEY = [
'selectable',
'name',
@ -203,6 +208,10 @@ export const SAVE_KEY = [
'fontWeight',
'dormerAttributes',
'toFixed',
'startPoint',
'endPoint',
'editable',
'isSortedPoints',
]
export const OBJECT_PROTOTYPE = [fabric.Line.prototype, fabric.Polygon.prototype, fabric.Triangle.prototype, fabric.Group.prototype]

View File

@ -14,6 +14,7 @@ import { sessionStore } from '@/store/commonAtom'
import { isObjectNotEmpty } from '@/util/common-utils'
import BoardDetailModal from './community/modal/BoardDetailModal'
import Config from '@/config/config.export'
export default function MainPage() {
const [sessionState, setSessionState] = useRecoilState(sessionStore)

View File

@ -2,15 +2,51 @@
import { useState } from 'react'
import { useMessage } from '@/hooks/useMessage'
import { setSession, login } from '@/lib/authActions'
import { sessionStore } from '@/store/commonAtom'
import { useRecoilState } from 'recoil'
import { useAxios } from '@/hooks/useAxios'
import { globalLocaleStore } from '@/store/localeAtom'
import { useRouter } from 'next/navigation'
import GlobalSpinner from '@/components/common/spinner/GlobalSpinner'
export default function AutoLoginPage() {
const [isLoading, setIsLoading] = useState(true)
export default function AutoLoginPage({ autoLoginParam }) {
const router = useRouter()
const [isLoading, setIsLoading] = useState(autoLoginParam === 'Y' ? false : true)
const [globalLocaleState, setGlbalLocaleState] = useRecoilState(globalLocaleStore)
const { promisePost } = useAxios(globalLocaleState)
const { getMessage } = useMessage()
const [userId, setUserId] = useState('')
const [sessionState, setSessionState] = useRecoilState(sessionStore)
const [idFocus, setIdFocus] = useState(false)
const loginProcess = async () => {
setIsLoading(true)
await promisePost({ url: '/api/login/v1.0/user', data: { loginId: userId } }).then((response) => {
setIsLoading(false)
if (response.data) {
const res = response.data
const result = { ...res, storeLvl: res.groupId === '60000' ? '1' : '2', pwdInitYn: 'Y' }
setSession(result)
setSessionState(result)
login()
} else {
alert(getMessage('login.fail'))
router.push('/login?autoLoginParam1=Y')
}
})
}
return (
<>
{isLoading && <GlobalSpinner />}
{autoLoginParam !== 'Y' ? (
<>
<div className="login-input-frame">
<div className="login-frame-tit ">
<span>{getMessage('site.name')}</span>
@ -23,5 +59,54 @@ export default function AutoLoginPage() {
</div>
</div>
</>
) : (
<>
<div className="login-input-frame">
<form
onSubmit={(e) => {
e.preventDefault()
loginProcess()
}}
className="space-y-6"
>
<div className="login-frame-tit">
<span>{getMessage('site.name')}</span>
{getMessage('site.sub_name')}
</div>
<div className="login-input-wrap">
<div className={`login-area id ${idFocus ? 'focus' : ''}`}>
<input
type="text"
className="login-input"
id="userId"
name="id"
required
value={userId}
placeholder={getMessage('login.id.placeholder')}
onChange={(e) => {
setUserId(e.target.value)
}}
onFocus={() => setIdFocus(true)}
onBlur={() => setIdFocus(false)}
/>
<button
type="button"
className="id-delete"
onClick={(e) => {
setUserId('')
}}
></button>
</div>
<div className="login-btn-box">
<button type="submit" className="login-btn">
{getMessage('login')}
</button>
</div>
</div>
</form>
</div>
</>
)}
</>
)
}

View File

@ -25,8 +25,10 @@ export default function Login() {
useEffect(() => {
if (autoLoginParam) {
if (autoLoginParam !== 'Y') {
autoLoginProcess(autoLoginParam)
}
}
// console.log('🚀 ~ checkSession ~ checkSession():', checkSession())
// checkSession().then((res) => {
@ -334,7 +336,7 @@ export default function Login() {
</div>
</>
)}
{autoLoginParam && <AutoLogin />}
{autoLoginParam && <AutoLogin autoLoginParam={autoLoginParam} />}
</div>
<div className="login-copyright">COPYRIGHT©2024 Hanwha Japan All Rights Reserved.</div>
</div>

View File

@ -6,29 +6,19 @@ import { contextMenuListState, contextMenuState } from '@/store/contextMenu'
import { useTempGrid } from '@/hooks/useTempGrid'
import { useContextMenu } from '@/hooks/useContextMenu'
import { useEvent } from '@/hooks/useEvent'
import { canvasState } from '@/store/canvasAtom'
import { canvasState, currentObjectState } from '@/store/canvasAtom'
export default function QContextMenu(props) {
const canvas = useRecoilValue(canvasState)
const { contextRef, canvasProps } = props
const [contextMenu, setContextMenu] = useRecoilState(contextMenuState)
const contextMenuList = useRecoilValue(contextMenuListState)
const activeObject = canvasProps?.getActiveObject() //
const currentObject = useRecoilValue(currentObjectState)
const { tempGridMode, setTempGridMode } = useTempGrid()
const { handleKeyup } = useContextMenu()
const { addDocumentEventListener, removeDocumentEvent } = useEvent()
// const { addDocumentEventListener, removeDocumentEvent } = useContext(EventContext)
let contextType = ''
if (activeObject) {
if (activeObject.initOptions && activeObject.initOptions.name) {
//
if (activeObject.initOptions?.name?.indexOf('guide') > -1) {
contextType = 'surface' //
}
}
}
const getYPosition = (e) => {
const contextLength = contextMenuList.reduce((acc, cur, index) => {
return acc + cur.length
@ -36,11 +26,13 @@ export default function QContextMenu(props) {
return e?.clientY - (contextLength * 25 + contextMenuList.length * 2 * 17)
}
useEffect(() => {
if (!contextRef.current) return
const handleContextMenu = (e) => {
e.preventDefault() // contextmenu
// e.preventDefault() // contextmenu
if (currentObject) {
const isArray = currentObject.hasOwnProperty('arrayData')
if (isArray && currentObject.arrayData.length === 0) return
if (tempGridMode) return
const position = {
x: window.innerWidth / 2 < e.pageX ? e.pageX - 240 : e.pageX,
@ -48,7 +40,7 @@ export default function QContextMenu(props) {
}
setContextMenu({ visible: true, ...position, currentMousePos: canvasProps.getPointer(e) })
addDocumentEventListener('keyup', document, handleKeyup)
canvasProps?.upperCanvasEl.removeEventListener('contextmenu', handleContextMenu) //
}
}
const handleClick = (e) => {
@ -64,6 +56,9 @@ export default function QContextMenu(props) {
}
}
useEffect(() => {
if (!contextRef.current) return
canvasProps?.upperCanvasEl.addEventListener('contextmenu', handleContextMenu)
document.addEventListener('click', handleClick)
document.addEventListener('click', handleOutsideClick)
@ -72,43 +67,9 @@ export default function QContextMenu(props) {
removeDocumentEvent('keyup')
document.removeEventListener('click', handleClick)
document.removeEventListener('click', handleOutsideClick)
canvasProps?.upperCanvasEl.removeEventListener('contextmenu', handleContextMenu) //
}
}, [contextRef, contextMenuList])
const handleObjectMove = () => {
activeObject.set({
lockMovementX: false, // X
lockMovementY: false, // Y
})
canvasProps?.on('object:modified', function (e) {
activeObject.set({
lockMovementX: true, // X
lockMovementY: true, // Y
})
})
}
const handleObjectDelete = () => {
if (confirm('삭제하실거?')) {
canvasProps.remove(activeObject)
}
}
const handleObjectCopy = () => {
activeObject.clone((cloned) => {
cloned.set({
left: activeObject.left + activeObject.width + 20,
initOptions: { ...activeObject.initOptions },
lockMovementX: true, // X
lockMovementY: true, // Y
lockRotation: true, //
lockScalingX: true, // X
lockScalingY: true, // Y
})
canvasProps?.add(cloned)
})
}
}, [contextRef, contextMenuList, currentObject])
return (
<>

View File

@ -26,6 +26,7 @@ export default function QSelectBox({
targetKey = '',
showKey = '',
params = {},
tagTitle = '',
}) {
const { getMessage } = useMessage()
@ -39,7 +40,7 @@ export default function QSelectBox({
if (showKey !== '' && !value) {
//value showKey
// return options[0][showKey]
return title
return title !== '' ? title : getMessage('selectbox.title')
} else if (showKey !== '' && value) {
//value sourceKey targetKey
@ -82,12 +83,13 @@ export default function QSelectBox({
className={`sort-select ${openSelect ? 'active' : ''} ${disabled ? 'disabled' : ''}`}
ref={ref}
onClick={disabled ? () => {} : () => setOpenSelect(!openSelect)}
title={tagTitle}
>
<p>{selected}</p>
<ul className="select-item-wrap">
{options?.length > 0 &&
options?.map((option, index) => (
<li key={option.id || index} className="select-item" onClick={() => handleClickSelectOption(option)}>
<li key={option.id + '_' + index} className="select-item" onClick={() => handleClickSelectOption(option)}>
<button key={option.id + 'btn'}>{showKey !== '' ? option[showKey] : option.name}</button>
</li>
))}

View File

@ -0,0 +1,212 @@
'use client'
import Link from 'next/link'
import Image from 'next/image'
import Search from '@/components/community/Search'
import Pagination from '@/components/community/Pagination'
import { useContext } from 'react'
import { useEffect, useState } from 'react'
import { useResetRecoilState, useRecoilValue, useRecoilState } from 'recoil'
import { useMessage } from '@/hooks/useMessage'
import { searchState } from '@/store/boardAtom'
import { QcastContext } from '@/app/QcastProvider'
import QnaBoardDetailModal from '@/components/community/modal/QnaDetailModal'
import { sessionStore } from '@/store/commonAtom'
import { useAxios } from '@/hooks/useAxios'
import { useCommonCode } from '@/hooks/common/useCommonCode'
export default function Qna() {
const { getMessage } = useMessage()
const resetSearch = useResetRecoilState(searchState)
const [isInitialized, setIsInitialized] = useState(false)
//const search = useRecoilValue(searchState)
const [searchForm, setSearchForm] = useRecoilState(searchState)
const { findCommonCode } = useCommonCode()
const { setIsGlobalLoading } = useContext(QcastContext)
const { get } = useAxios()
const [boardList, setBoardList] = useState([])
const [sessionState, setSessionState] = useRecoilState(sessionStore)
const [search, setSearch] = useRecoilState(searchState)
//
const [open, setOpen] = useState(false)
const [modalQnaNo, setModalQnaNo] = useState('')
const [modalQnaType, setModalQnaType] = useState('')
//
useEffect(() => {
async function fetchData() {
setIsGlobalLoading(true)
const startRow = (search.currentPage - 1) * search.pageBlock > 0 ? (search.currentPage - 1) * search.pageBlock + 1 : 1
const endRow = search.currentPage * search.pageBlock
const url = `/api/board/list`
const params = new URLSearchParams({
schNoticeTpCd : 'QC',
schNoticeClsCd: 'QNA',
compCd : 5200,
storeId : sessionState.storeId,
loginId : sessionState.userId,
schTitle : search.searchValue ? search.searchValue : '',
startRow : startRow,
endRow : endRow,
schMainYn : 'N',
siteTpCd : 'QC',
})
const apiUrl = `${url}?${params.toString()}`
const resultData = await get({ url: apiUrl })
if (resultData) {
if (resultData.result.code === 200) {
if (resultData.data.length > 0) {
setBoardList(resultData.data)
setSearch({ ...search, totalCount: resultData.data[0].totCnt })
} else {
setBoardList([])
setSearch({ ...search, totalCount: 0 })
}
} else {
alert(resultData.result.message)
}
}
setIsGlobalLoading(false)
}
fetchData()
}, [search.currentPage, search.searchValue, search.pageBlock, search.searchFlag])
useEffect(() => {
if (search.mainFlag === 'N') {
resetSearch()
} else {
// FAQ
setIsGlobalLoading(false)
setSearchForm({ ...searchForm, mainFlag: 'N' })
}
setIsInitialized(true)
//
// const codeL = findCommonCode(204200)
// const codeM = findCommonCode(204300)
// const codeS = findCommonCode(204400)
}, [])
if (!isInitialized) {
return null
}
const boardType = {
boardTitle: getMessage('qna.title'),
subTitle: getMessage('qna.sub.title'),
clsCode: 'QNA',
}
return (
<>
<div className="sub-header">
<div className="sub-header-inner">
<ul className="sub-header-title-wrap">
<li className="title-item">
<Link className="sub-header-title" href={'#'}>
{getMessage('qna.title')}
</Link>
</li>
</ul>
<ul className="sub-header-location">
<li className="location-item">
<span className="home">
<Image src="/static/images/main/home_icon.svg" alt="react" width={16} height={16} />
</span>
</li>
<li className="location-item">
<span>{getMessage('header.menus.community')}</span>
</li>
<li className="location-item">
<span>{getMessage('qna.title')}</span>
</li>
</ul>
</div>
</div>
<div className="sub-content">
<div className="sub-content-inner">
<div className="sub-table-box">
<Search title={boardType.boardTitle} subTitle={boardType.subTitle} isSelectUse={true} clsCode={boardType.clsCode} />
{/*<QnaTable clsCode={boardType.clsCode} />*/}
<div className="community-table">
<table>
<colgroup>
<col width={100}/>
<col width={150}/>
<col />
<col width={150}/>
<col width={150}/>
</colgroup>
<tbody>
{boardList.length > 0 ? (
boardList?.map((board) => (
<tr
key={board.qnaNo}
onClick={() => {
setOpen(true)
setModalQnaNo(board.qnaNo)
setModalQnaType("["+board?.qnaClsLrgCd+"/"+board?.qnaClsMidCd+"/"+board?.qnaClsSmlCd+"]")
}}
>
<td className="al-c">
{/* 번호 */}
{board.totCnt - board.rowNumber + 1}
</td>
{/* 답변 */}
{board?.answerYn === 'Y'? (<td className="al-c "> {getMessage('qna.list.header.answer.yes')}</td>) : (<td className="al-c org"> {getMessage('qna.list.header.answer.no')}</td>)}
<td>
<div className="mb5">[{board?.qnaClsLrgCd} / {board?.qnaClsMidCd} / {board?.qnaClsSmlCd}]</div>
{/* 제목 */}
<div className="text-frame">
<div className="text-overflow">{board.title}{board.qstTitle}</div>
{board.attachYn === 'Y' && <span className="clip"></span>}
</div>
</td>
<td>
<div className="renewal">
{/*{board.uptDt && (*/}
{/* <>*/}
{/* (<span>{getMessage('board.uptDt')}</span> : {board.uptDt})*/}
{/* </>*/}
{/*)}*/}
{board.regUserNm}
</div>
</td>
<td className="al-c">
{/* 등록일 */}
{board.regDt.split(' ')[0]}
</td>
</tr>
))
) : (
<tr>
<td className="al-c no-data" colSpan={5}>{getMessage('common.message.no.data')}</td>
</tr>
)}
</tbody>
</table>
</div>
<Pagination />
</div>
</div>
</div>
{open && <QnaBoardDetailModal qnaNo={modalQnaNo} setOpen={setOpen} qnaType = {modalQnaType} />}
</>
)
}

View File

@ -4,10 +4,11 @@ import { searchState } from '@/store/boardAtom'
import { useRecoilState, useRecoilValue } from 'recoil'
import { useState } from 'react'
import { useMessage } from '@/hooks/useMessage'
import QnaRegModal from '@/components/community/modal/QnaRegModal'
export default function Search({ title = '', subTitle = '', isSelectUse = false }) {
export default function Search({ title = '', subTitle = '', isSelectUse = false, clsCode = '' }) {
const { getMessage } = useMessage()
const [open, setOpen] = useState(false)
const search = useRecoilValue(searchState)
const [searchForm, setSearchForm] = useRecoilState(searchState)
@ -32,7 +33,13 @@ export default function Search({ title = '', subTitle = '', isSelectUse = false
} else {
setSearchView(false)
setSearchViewText('')
setSearchForm({ ...searchForm, currentPage: 1, searchValue: '', pageBlock: block, searchFlag: !searchForm.searchFlag })
setSearchForm({
...searchForm,
currentPage: 1,
searchValue: '',
pageBlock : block,
searchFlag : !searchForm.searchFlag,
})
}
//
setSearchValue('')
@ -57,7 +64,10 @@ export default function Search({ title = '', subTitle = '', isSelectUse = false
onKeyDown={handleKeyDown}
value={searchValue}
/>
<button type="button" className="community-search-ico" onClick={() => handleSearch(searchValue, selectPageBlock)}></button>
<button type="button" className="community-search-ico"
onClick={() => handleSearch(searchValue, selectPageBlock)}></button>
</div>
{searchView && (
<div className="community-search-keyword">
@ -92,6 +102,14 @@ export default function Search({ title = '', subTitle = '', isSelectUse = false
</div>
{isSelectUse && (
<div className="left-unit-box">
{clsCode === 'QNA' &&
<div>
<button className="btn-origin navy mr10"
onClick={() => {
setOpen(true)
}}> {getMessage('qna.sub.btn.inquiry')}</button>
</div>
}
<div className="select-box" style={{ width: '80px' }}>
<select
className="select-light black"
@ -112,6 +130,7 @@ export default function Search({ title = '', subTitle = '', isSelectUse = false
</div>
)}
</div>
{open && <QnaRegModal setOpen={setOpen} setReload={handleSearch} searchValue={searchValue ? searchValue : searchViewText} selectPageBlock = {selectPageBlock}/>}
</>
)
}

View File

@ -0,0 +1,127 @@
'use client'
import { useEffect, useState } from 'react'
import { useAxios } from '@/hooks/useAxios'
import { handleFileDown } from '@/util/board-utils'
import { useMessage } from '@/hooks/useMessage'
import { sessionStore } from '@/store/commonAtom'
import { useRecoilState } from 'recoil'
export default function QnaDetailModal({ qnaNo, setOpen, qnaType }) {
const { getMessage } = useMessage()
// api
const { get } = useAxios()
const [boardDetail, setBoardDetail] = useState({})
const [sessionState, setSessionState] = useRecoilState(sessionStore)
useEffect(() => {
//
const fetchDetail = async (qnaNo) => {
const url = `/api/board/detail`
const params = new URLSearchParams({
noticeNo : qnaNo,
qnaNo : qnaNo,
schNoticeClsCd: 'QNA',
compCd : 5200,
loginId : sessionState.userId,
langCd : 'JA',
})
const apiUrl = `${url}?${params.toString()}`
const resultData = await get({ url: apiUrl })
if (resultData) {
if (resultData.result.code === 200) {
const boardDetail = resultData.data
setBoardDetail(boardDetail)
} else {
alert(resultData.result.message)
}
}
}
fetchDetail(qnaNo)
}, [])
return (
<>
<div key={qnaNo} className="modal-popup community">
<div className="modal-dialog">
<div className="modal-content">
<div className="modal-header">
<button
type="button"
className="modal-close"
onClick={() => {
setOpen(false)
}}
>
{getMessage('board.sub.btn.close')}
</button>
</div>
<div className="modal-body">
<div className="oneonone-header-wrap">
<div className="oneonone-title"> {qnaType} {boardDetail.qstTitle}</div>
<div className="oneonone-infor">
<div className="profile">{boardDetail.regUserNm}</div>
<div className="date">{boardDetail.regDt}</div>
</div>
</div>
<div className="oneonone-detail">
{boardDetail.listFile && (
<dl className="community_detail-file-wrap">
<dt>{getMessage('qna.detail.sub.fileList')}</dt>
{boardDetail.listFile.map((boardFile) => (
<dd key={boardFile.encodeFileNo}>
<button type="button" className="down" onClick={() => handleFileDown(boardFile.fileNo, 'NO')}>
{boardFile.srcFileNm}
</button>
</dd>
))}
</dl>
)}
<div
className="community_detail-inner"
dangerouslySetInnerHTML={{
__html: boardDetail.qstContents ? boardDetail.qstContents.replaceAll('\n', '<br/>') : '',
}}
></div>
</div>
{boardDetail?.answerYn === 'Y' && (
<div className="oneonone-answer">
<div className="answer-title-wrap">
<div className="answer-title">Hanwha Japan {getMessage('qna.detail.sub.answer')}</div>
<div className="oneonone-infor">
<div className="profile">{boardDetail.ansRegNm}</div>
<div className="date">{boardDetail.ansRegDt}</div>
</div>
</div>
<div
className="community_detail-inner"
dangerouslySetInnerHTML={{
__html: boardDetail.ansContents ? boardDetail.ansContents.replaceAll('\n', '<br/>') : '',
}}
></div>
{boardDetail.ansListFile && (
<dl className="community_detail-file-wrap">
<dt>{getMessage('qna.detail.sub.fileList')}</dt>
{boardDetail.ansListFile.map((boardFile) => (
<dd key={boardFile.encodeFileNo}>
<button type="button" className="down" onClick={() => handleFileDown(boardFile.fileNo, 'N')}>
{boardFile.srcFileNm}
</button>
</dd>
))}
</dl>
)}
</div>
)}
</div>
</div>
</div>
</div>
</>
)
}

View File

@ -0,0 +1,140 @@
'use client'
import { useRef } from 'react'
import { v4 as uuidv4 } from 'uuid'
import { useMessage } from '@/hooks/useMessage'
import { useSwal } from '@/hooks/useSwal'
export default function QnaFileUploader({ uploadFiles, setUploadFiles, qnaData, setQnaData }) {
const fileInputRef = useRef(null)
const { getMessage } = useMessage()
const { swalFire } = useSwal()
const handleButtonClick = (e) => {
e.preventDefault()
fileInputRef.current.click()
}
const onChangeFiles = async (e) => {
if (e.target.files.length <= 0) {
return
}
const fileList = []
let passFlag = true
const allowedFileTypes = [
'image/',
'application/pdf',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'application/vnd.ms-excel',
'application/vnd.openxmlformats-officedocument.presentationml.presentation', // PPTX
'application/vnd.ms-powerpoint', // PPT
]
Array.from(e.target.files).forEach((file) => {
//, pdf,
const fileType = file.type
if (!allowedFileTypes.some((type) => fileType.includes(type))) {
passFlag = false
} else {
fileList.push({ data: file, id: uuidv4() })
}
})
if (!passFlag) {
swalFire({ text: getMessage('estimate.detail.fileList.extCheck'), type: 'alert', icon: 'error' })
}
setUploadFiles([...uploadFiles, ...fileList])
setQnaData({...qnaData, files:[...uploadFiles, ...fileList]})
e.target.value = ''
}
const deleteFile = (id) => {
setUploadFiles(uploadFiles.filter((file) => file.id !== id))
setQnaData({...qnaData, files:uploadFiles.filter((file) => file.id !== id)})
}
const handleDrop = (e) => {
e.preventDefault()
e.stopPropagation()
const fileList = []
let passFlag = true
const allowedFileTypes = [
'image/',
'application/pdf',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'application/vnd.ms-excel',
'application/vnd.openxmlformats-officedocument.presentationml.presentation', // PPTX
'application/vnd.ms-powerpoint', // PPT
]
Array.from(e.dataTransfer.files).forEach((file) => {
//, pdf,
let fileType = file.type
if (!allowedFileTypes.some((type) => fileType.includes(type))) {
passFlag = false
} else {
fileList.push({ data: file, id: uuidv4() })
}
})
if (!passFlag) {
swalFire({ text: getMessage('estimate.detail.fileList.extCheck'), type: 'alert', icon: 'error' })
}
setUploadFiles([...uploadFiles, ...fileList])
setQnaData({...qnaData, files:[...uploadFiles, ...fileList]})
}
const handleDragOver = (e) => {
e.preventDefault()
e.stopPropagation()
}
const handleDragEnd = (e) => {
e.preventDefault()
e.stopPropagation()
}
const handleDragLeave = (e) => {
e.preventDefault()
e.stopPropagation()
}
return (
<div className="design-request-grid mt15">
<div className="design-request-count">
<div className="design-request-grid-tit">{getMessage("qna.reg.header.fileList")}</div>
<div className="btn-area one-on-one">
<label className="file-upload" htmlFor="img" onClick={handleButtonClick}>
Attach File
</label>
<input type="file" multiple name="file" ref={fileInputRef} style={{ display: 'none' }} onChange={(e) => onChangeFiles(e)} />
</div>
</div>
<div className="drag-file-box one-on-one">
<div className="drag-file-area"
draggable
onDrop={(e) => handleDrop(e)}
onDragOver={(e) => handleDragOver(e)}
onDragEnd={(e) => handleDragEnd(e)}
onDragLeave={(e) => handleDragLeave(e)}>
<p>Drag file here</p>
<ul className="file-list">
{uploadFiles.length > 0 &&
uploadFiles.map((file) => (
<li className="file-item" key={file.id}>
<span>
{file.data.name} <button className="delete" onClick={() => deleteFile(file.id)}></button>
</span>
</li>
))}
</ul>
</div>
</div>
</div>
)
}

View File

@ -0,0 +1,454 @@
'use client'
import { useMessage } from '@/hooks/useMessage'
import { sessionStore } from '@/store/commonAtom'
import { useRecoilState, useRecoilValue } from 'recoil'
import QnaFileUploader from '@/components/community/modal/QnaFileUploader'
import { useContext, useEffect, useRef, useState } from 'react'
import { useCommonCode } from '@/hooks/common/useCommonCode'
import Select from 'react-select'
import dayjs from 'dayjs'
import { useSwal } from '@/hooks/useSwal'
import { QcastContext } from '@/app/QcastProvider'
import { useAxios } from '@/hooks/useAxios'
import { globalLocaleStore } from '@/store/localeAtom'
import { e } from 'mathjs'
import { set } from 'react-hook-form'
export default function QnaRegModal({ setOpen, setReload, searchValue, selectPageBlock }) {
const { getMessage } = useMessage()
const [fileList, setFileList] = useState([])
const [sessionState, setSessionState] = useRecoilState(sessionStore)
const globalLocaleState = useRecoilValue(globalLocaleStore)
const [files, setFiles] = useState([])
const [qnaData, setQnaData] = useState([])
const [closeMdFlg, setCloseMdFlg] = useState(true)
const [closeSmFlg, setCloseSmFlg] = useState(true)
const [hideSmFlg, setHideSmFlg] = useState(false)
const qnaTypeLgCodeRef = useRef(null)
const qnaTypeMdCodeRef = useRef(null)
const qnaTypeSmCodeRef = useRef(null)
const qstMail = useRef(null);
const regUserNmRef = useRef(null)
const regUserTelNoRef = useRef(null)
const titleRef = useRef(null)
const contentsRef = useRef(null)
const { findCommonCode } = useCommonCode()
const [qnaTypeLgCodeList, setQnaTypeLgCodeList] = useState([])
const [qnaTypeMdCodeList, setQnaTypeMdCodeList] = useState([])
const [qnaTypeSmCodeList, setQnaTypeSmCodeList] = useState([])
const [phoneNumber, setPhoneNumber] = useState("");
const { swalFire } = useSwal()
const { setIsGlobalLoading } = useContext(QcastContext)
const [isBtnDisable, setIsBtnDisable] = useState(false);
const { promiseGet, post, promisePost } = useAxios(globalLocaleState)
let fileCheck = false;
const regPhoneNumber = (e) => {
const result = e.target.value
.replace(/[^0-9.]/g, "")
//.replace(/^(\d{0,3})(\d{0,4})(\d{0,4})$/g, "$1-$2-$3")
//.replace(/(-{1,2})$/g, "");
//setPhoneNumber(result);
setQnaData({...qnaData, regUserTelNo: result })
}
const fileUploadProps = {
uploadFiles: files,
setUploadFiles: setFiles,
}
// const fileSave = (qnaData, fileUploadProps) => {
// return qnaData.files.push(fileUploadProps.uploadFiles)
// }
const initQnaReg = async () => {
qstMail.current.value = ''
regUserNmRef.current.value = ''
regUserTelNoRef.current.value = ''
qnaTypeLgCodeRef.current.setValue();
qnaTypeMdCodeRef.current.setValue();
qnaTypeSmCodeRef.current?.setValue();
titleRef.current.value = ''
contentsRef.current.value = ''
//setQnaData([])
setQnaData({
...qnaData,
compCd: "5200",
siteTpCd: "QC",
schNoticeClsCd: "QNA",
regId: sessionState.userId,
storeId: sessionState.userId,
qstMail : sessionState.email
})
const codeL = findCommonCode(204200)
if (codeL != null) {
setQnaTypeLgCodeList(codeL)
}
setIsGlobalLoading(false)
setIsBtnDisable(false);
}
const onChangeQnaTypeL = (e) => {
if(e === undefined || e === null) return;
const codeM = findCommonCode(204300)
if (codeM != null) {
let codeList = []
codeM.map((item) => {
if(item.clRefChr1 === e.clCode) {
codeList.push(item);
}
})
setQnaTypeMdCodeList(codeList)
setQnaData({ ...qnaData, qnaClsLrgCd:e.clCode})
setCloseMdFlg(false)
qnaTypeMdCodeRef.current.setValue();
qnaTypeSmCodeRef.current?.setValue();
}
}
const onChangeQnaTypeM = (e) => {
if(e === undefined || e === null) return;
const codeS = findCommonCode(204400)
if (codeS != null) {
let codeList = []
codeS.map((item) => {
if (item.clRefChr1 === e.clCode) {
codeList.push(item);
}
})
setQnaData({ ...qnaData, qnaClsMidCd: e.clCode })
setCloseSmFlg(false)
setQnaTypeSmCodeList(codeList)
qnaTypeSmCodeRef.current?.setValue();
if(codeList.length > 0) {
setHideSmFlg(false)
}else{
setHideSmFlg(true)
}
}
}
const onChangeQnaTypeS = (e) => {
if(e === undefined || e === null) return;
setQnaData({ ...qnaData, qnaClsSmlCd:e.clCode})
}
const onFileSave = () => {
const formData= []
if(fileUploadProps.uploadFiles.length === 0) return;
if(!fileCheck) return;
fileUploadProps.uploadFiles.forEach((file) => {
//console.log("file::::::::",file)
formData.push(file)
})
setQnaData({ ...qnaData, files:formData })
fileCheck = false;
}
const isValidEmail = (email) => {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
};
const isEmpty = (value) => {
return value === null || value === undefined || value.trim() === "";
};
const handleQnaSubmit = async () => {
//
//console.log("1::::",qnaData)
let regUserNm = qnaData?.regUserNm??'';
if (!isValidEmail(qnaData.qstMail)) {
qstMail.current.focus();
swalFire({
title: getMessage('qna.reg.alert.require.qstMail'),
icon: 'warning',
});
return;
}
if (isEmpty(regUserNm)) {
regUserNmRef.current.value = '';
regUserNmRef.current.focus()
swalFire({
title: getMessage('qna.reg.alert.require.regUserNm'),
icon: 'warning',
})
return false
}
let qnaClsLrgCd = qnaData?.qnaClsLrgCd??'';
let qnaClsMidCd = qnaData?.qnaClsMidCd??'';
if (isEmpty(qnaClsLrgCd) || isEmpty(qnaClsMidCd) ) {
(isEmpty(qnaClsLrgCd))?qnaTypeLgCodeRef.current.focus():qnaTypeMdCodeRef.current.focus()
swalFire({
title: getMessage('qna.reg.alert.select.type'),
icon: 'warning',
})
return false
}
let title = qnaData?.title??'';
if (isEmpty(title)) {
titleRef.current.value = '';
titleRef.current.focus()
swalFire({
title: getMessage('qna.reg.alert.require.title'),
icon: 'warning',
})
return false
}
//console.log("5::::",qnaData)
let contents = qnaData?.contents??'';
if (isEmpty(contents)) {
contentsRef.current.value = '';
contentsRef.current.focus()
swalFire({
title: getMessage('qna.reg.alert.require.contents'),
icon: 'warning',
})
return false
}
const formData = new FormData()
if(qnaData?.files?.length > 0) {
qnaData?.files.forEach((file) => {
formData.append('files', file.data)
})
}
formData.append("compCd", qnaData.compCd)
formData.append("siteTpCd", qnaData.siteTpCd)
formData.append("qnaClsLrgCd", qnaData.qnaClsLrgCd)
formData.append("qnaClsMidCd", qnaData.qnaClsMidCd)
formData.append("qnaClsSmlCd", qnaData.qnaClsSmlCd)
formData.append("title", qnaData.title)
formData.append("contents", qnaData.contents)
formData.append("regId", qnaData.regId)
formData.append("storeId", qnaData.storeId)
formData.append("regUserNm", qnaData.regUserNm)
formData.append("regUserTelNo", qnaData.regUserTelNo)
formData.append("qstMail", qnaData.qstMail)
formData.append("schNoticeClsCd", qnaData.schNoticeClsCd)
//console.log(Array.from(formData));
swalFire({
html: getMessage('qna.reg.confirm.save'),
type: 'confirm',
confirmFn: async () => {
setIsBtnDisable(true);
setIsGlobalLoading(true)
try {
const apiUrl = 'api/board'
//console.log("7::::",qnaData)
await post({ url: `${apiUrl}/saveQna`, data: formData }).then((res) => {
if (res?.result.code === 200) {
//qnaData.newFileList = []
setIsGlobalLoading(false)
swalFire({ text: getMessage('qna.reg.alert.save'), type: 'alert' })
setOpen(false)
setReload(searchValue, selectPageBlock);
}else{
setIsGlobalLoading(false)
swalFire({ text: getMessage('qna.reg.alert.saveFail'), type: 'alert', icon: 'error' })
console.error('error::::::::::::', res)
}
setIsBtnDisable(false)
})
} catch (e) {
setIsGlobalLoading(false)
setIsBtnDisable(false);
console.error('error::::::::::::', e.message)
swalFire({ text: e.message, type: 'alert' , icon: 'error'})
console.error('error::::::::::::', e.message)
}
}
})
}
useEffect(() => {
initQnaReg()
},[])
// useEffect(() => {
// onFileSave()
//
// }, [onFileSave])
return (
<div className="modal-popup">
<div className="modal-dialog big">
<div className="modal-content">
<div className="modal-header">
<h1 className="title">{getMessage('qna.title')}</h1>
<button className="modal-close"
onClick={() => {
setOpen(false)
}}>{getMessage('board.sub.btn.close')}</button>
</div>
<div className="modal-body">
<div className="modal-body-inner">
<div className="design-request-table">
<div className="common-table">
<table>
<colgroup>
<col style={{ width: '100px' }} />
<col />
<col style={{ width: '120px' }} />
<col />
<col style={{ width: '150px' }} />
<col />
</colgroup>
<tbody>
<tr>
<th>{getMessage('qna.list.header.regNm')}</th>
<td><input type="text" className="input-light" value={sessionState?.userNm || ''} readOnly /></td>
<th>E-Mail<span className="red">*</span></th>
<td ><input type="text" className="input-light" required
ref={qstMail}
value={qnaData?.qstMail || ''}
onChange={(e) => setQnaData({...qnaData, qstMail: e.target.value })}
onBlur={(e) => setQnaData({ ...qnaData, qstMail: e.target.value })} />
</td>
<th>{getMessage('qna.reg.header.regDt')}</th>
<td>{dayjs(new Date()).format('YYYY-MM-DD')}</td>
</tr>
<tr>
<th>{getMessage('qna.reg.header.regUserNm')}<span className="red">*</span></th>
<td ><input type="text" className="input-light" required
ref={regUserNmRef}
value={qnaData?.regUserNm || '' }
onChange={(e) => setQnaData({...qnaData, regUserNm: e.target.value })}
onBlur={(e) => setQnaData({ ...qnaData, regUserNm: e.target.value })} /> </td>
<th>{getMessage('qna.reg.header.regUserTelNo')}</th>
<td colSpan={3}><input type="text" className="input-light"
ref={regUserTelNoRef}
maxLength={13}
value={qnaData?.regUserTelNo || '' }
onChange={regPhoneNumber}
/></td>
</tr>
</tbody>
</table>
</div>
</div>
<div className="design-request-grid">
<div className="design-request-count">
<div className="design-request-grid-tit">{getMessage("qna.reg.header.type")}, {getMessage("qna.reg.header.title")} <span
className="red">*</span></div>
</div>
<div className="flx-box one-on-one">
<div className="select-wrap mr5" >
<Select name="" ref={qnaTypeLgCodeRef}
options={qnaTypeLgCodeList}
placeholder="Select"
onChange={(e) => onChangeQnaTypeL(e)}
getOptionLabel={(x) => x.clCodeNm}
getOptionValue={(x) => x.clCode}
isClearable={false}
isSearchable={false}
/>
</div>
<div className="select-wrap mr5" >
<Select name="" ref={qnaTypeMdCodeRef}
options={qnaTypeMdCodeList}
placeholder="Select"
onChange={(e) => onChangeQnaTypeM(e)}
getOptionLabel={(x) => x.clCodeNm}
getOptionValue={(x) => x.clCode}
isClearable={false}
isSearchable={false}
isDisabled={closeMdFlg}
defaultValue={''}
/>
</div>
<div className="select-wrap" >
{!hideSmFlg && (
<Select name="" ref={qnaTypeSmCodeRef}
options={qnaTypeSmCodeList}
placeholder="Select"
onChange={(e) => onChangeQnaTypeS(e)}
getOptionLabel={(x) => x.clCodeNm}
getOptionValue={(x) => x.clCode}
isClearable={false}
isSearchable={false}
isDisabled={closeSmFlg}
/>)}
</div>
</div>
<div className="input-wrap mt5">
<input type="text" className="input-light" maxLength={200}
ref = {titleRef}
value={qnaData?.title || '' }
onChange={(e) => {setQnaData({ ...qnaData, title: e.target.value })}}
/>
</div>
</div>
<div className="design-request-grid mt15">
<div className="design-request-count">
<div className="design-request-grid-tit">{getMessage("qna.reg.header.contents")} <span className="red">*</span></div>
</div>
<div>
<textarea className="textarea-form" name="" id="" maxLength={4000}
ref={contentsRef}
value={qnaData?.contents || '' }
onChange={(e) => {setQnaData({ ...qnaData, contents: e.target.value })}} ></textarea>
</div>
</div>
<QnaFileUploader {...fileUploadProps} qnaData={qnaData} setQnaData={setQnaData} />
</div>
<div className="footer-btn-wrap">
{isBtnDisable === false && <button className="btn-origin navy mr5" onClick={handleQnaSubmit}>{getMessage("qna.reg.header.save")}</button>}
<button className="btn-origin grey" onClick={() => {
setOpen(false)
}}>{getMessage("board.sub.btn.close")}</button>
</div>
</div>
</div>
</div>
</div>
)
}

View File

@ -13,7 +13,7 @@ import dayjs from 'dayjs'
import { useCommonCode } from '@/hooks/common/useCommonCode'
import { useEstimateController } from '@/hooks/floorPlan/estimate/useEstimateController'
import { SessionContext } from '@/app/SessionProvider'
import Select from 'react-select'
import Select, { components } from 'react-select'
import { convertNumberToPriceDecimal, convertNumberToPriceDecimalToFixed } from '@/util/common-utils'
import ProductFeaturesPop from './popup/ProductFeaturesPop'
import { v4 as uuidv4 } from 'uuid'
@ -60,7 +60,7 @@ export default function Estimate({}) {
const [cableItemList, setCableItemList] = useState([]) //
const [cableItem, setCableItem] = useState('') //
const [cableDbItem, setCableDbItem] = useState('') //
const [startDate, setStartDate] = useState(new Date())
const singleDatePickerProps = {
startDate,
@ -98,7 +98,7 @@ export default function Estimate({}) {
}
const initEstimate = (currPid = currentPid) => {
console.log('🚀 ~ initEstimate ~ currPid:', currPid)
// console.log('🚀 ~ initEstimate ~ currPid:', currPid)
closeAll()
setObjectNo(objectRecoil.floorPlanObjectNo)
@ -117,6 +117,7 @@ export default function Estimate({}) {
item.value = item.clRefChr1
item.label = item.clRefChr2
})
// console.log(code2)
setCableItemList(code2)
}
@ -152,8 +153,8 @@ export default function Estimate({}) {
}
useEffect(() => {
console.log('🚀 ~ Estimate ~ selectedPlan:', selectedPlan)
if (selectedPlan) initEstimate(selectedPlan.planNo)
// console.log('🚀 ~ Estimate ~ selectedPlan:', selectedPlan)
if (selectedPlan) initEstimate(selectedPlan?.planNo?? currentPid)
}, [selectedPlan])
useEffect(() => {
@ -175,7 +176,10 @@ export default function Estimate({}) {
row.check = false
estimateOption.map((row2) => {
if (row.pkgYn === '0') {
if (row2 === row.code) {
// if (row2 === row.code) {
// row.check = true
// }
if (row.code.split('、').includes(row2)) {
row.check = true
}
} else {
@ -217,7 +221,10 @@ export default function Estimate({}) {
row.check = false
estimateOption.map((row2) => {
if (row.pkgYn === '0') {
if (row2 === row.code) {
// if (row2 === row.code) {
// row.check = true
// }
if (row.code.split('、').includes(row2)) {
row.check = true
}
} else {
@ -240,7 +247,6 @@ export default function Estimate({}) {
}
}
})
setSpecialNoteList(res)
setSpecialNoteFirstFlg(true)
@ -377,8 +383,8 @@ export default function Estimate({}) {
useEffect(() => {
if (estimateContextState.estimateType !== '') {
const param = {
saleStoreId: session.storeId,
sapSalesStoreCd: session.custCd,
saleStoreId: estimateContextState.sapSaleStoreId,
sapSalesStoreCd: estimateContextState.sapSalesStoreCd,
docTpCd: estimateContextState?.estimateType,
}
@ -387,6 +393,8 @@ export default function Estimate({}) {
if (isNotEmptyArray(res?.data)) {
setStorePriceList(res.data)
}
setItemChangeYn(true)
})
if (estimateContextState.estimateType === 'YJSS') {
@ -416,8 +424,6 @@ export default function Estimate({}) {
handlePricing('UNIT_PRICE')
}
}
setItemChangeYn(true)
}
}, [estimateContextState?.estimateType])
@ -469,6 +475,21 @@ export default function Estimate({}) {
} else {
item.check = false
}
} else {
let codes = item.code.split('、')
let flg = '0'
if (codes.length > 1) {
for (let i = 0; i < pushData.length; i++) {
if (codes.indexOf(pushData[i]) > -1) {
flg = '1'
}
}
if (flg === '1') {
item.check = true
} else {
item.check = false
}
}
}
})
@ -478,12 +499,26 @@ export default function Estimate({}) {
})
}
//Pricing confirm
const handlePricingBtn = (showPriceCd) => {
swalFire({
text: getMessage('estimate.detail.showPrice.pricingBtn.confirm'),
type: 'confirm',
icon: 'warning',
confirmFn: () => {
handlePricing(showPriceCd)
},
})
}
//Pricing
const handlePricing = async (showPriceCd) => {
const param = {
saleStoreId: session.storeId,
sapSalesStoreCd: session.custCd,
saleStoreId: estimateContextState.sapSaleStoreId,
sapSalesStoreCd: estimateContextState.sapSalesStoreCd,
docTpCd: estimateContextState.estimateType,
secSapSalesStoreCd:
estimateContextState.secSapSalesStoreCd?.length > 0 && showPriceCd === 'QSP_PRICE' ? estimateContextState.secSapSalesStoreCd : '',
priceCd: showPriceCd,
itemIdList: estimateContextState.itemList.filter((item) => item.delFlg === '0' && item.paDispOrder === null),
}
@ -506,7 +541,6 @@ export default function Estimate({}) {
})
}
}
setIsGlobalLoading(true)
await promisePost({ url: '/api/estimate/price/item-price-list', data: param }).then((res) => {
let updateList = []
@ -531,6 +565,7 @@ export default function Estimate({}) {
updateList.push({
...item,
openFlg: data.data2[i].unitPrice === '0.0' ? '1' : '0',
unitOpenFlg: (showPriceCd === 'QSP_PRICE' && item.openFlg === '1') ? '1' : '0',
salePrice: data.data2[i].unitPrice === null ? '0' : data.data2[i].unitPrice,
saleTotPrice: (item.amount * data.data2[i].unitPrice).toString(),
})
@ -696,7 +731,7 @@ export default function Estimate({}) {
/* 케이블 select 변경시 */
const onChangeDisplayCableItem = (value, itemList) => {
itemList.map((item, index) => {
if (item.dispCableFlg === '1') {
if (item.dispCableFlg === '1' && item.itemTpCd !== 'M12') {
if (value !== '') {
onChangeDisplayItem(value, item.dispOrder, index, true)
}
@ -705,6 +740,18 @@ export default function Estimate({}) {
setCableItem(value)
}
/* 케이블 select 변경시 */
const onChangeDisplayDoubleCableItem = (value, itemList) => {
itemList.map((item, index) => {
if (item.dispCableFlg === '1' && item.itemTpCd === 'M12') {
if (value !== '') {
onChangeDisplayItem(value, item.dispOrder, index, true)
}
}
})
setCableDbItem(value)
}
// /
const onChangeDisplayItem = (itemId, dispOrder, index, flag) => {
const param = {
@ -1054,15 +1101,20 @@ export default function Estimate({}) {
item.showSaleTotPrice = '0'
}
if (item.dispCableFlg === '1') {
if (item.dispCableFlg === '1' ) {
dispCableFlgCnt++
if(item.itemTpCd === 'M12') {
setCableDbItem(item.itemId)
}else{
setCableItem(item.itemId)
}
}
}
})
if (dispCableFlgCnt === 0) {
setCableItem('100038')
setCableDbItem('100037')
}
let pkgAsp = estimateContextState.pkgAsp ? Number(estimateContextState.pkgAsp.replaceAll(',', '')) : 0
@ -1125,14 +1177,20 @@ export default function Estimate({}) {
dispCableFlgCnt++
}
if (item.dispCableFlg === '1') {
if (item.dispCableFlg === '1'){
if(item.itemTpCd === 'M12') {
setCableDbItem(item.itemId)
}else{
setCableItem(item.itemId)
}
}
}
})
if (dispCableFlgCnt === 0) {
setCableItem('100038')
setCableDbItem('100037')
}
totals.vatPrice = totals.supplyPrice * 0.1
@ -1193,10 +1251,26 @@ export default function Estimate({}) {
if (dispCableFlgCnt === 0) {
setCableItem('100038')
setCableDbItem('100037')
}
}
}, [estimateContextState?.itemList, cableItemList])
const [agencyCustList, setAgencyCustList] = useState([])
useEffect(() => {
// 952 - 2 sapSalesStoreCd
if (estimateContextState?.sapSalesStoreCd && session?.storeLvl === '1') {
const param = {
sapSalesStoreCd: estimateContextState.sapSalesStoreCd,
}
const apiUrl = `api/estimate/agency-cust-list?${queryStringFormatter(param)}`
get({ url: apiUrl }).then((res) => {
if (isNotEmptyArray(res?.data)) {
setAgencyCustList(res?.data)
}
})
}
}, [estimateContextState?.sapSalesStoreCd])
return (
<div className="sub-content estimate">
<div className="sub-content-inner">
@ -1207,7 +1281,7 @@ export default function Estimate({}) {
<div className="estimate-box">
<div className="estimate-tit">{getMessage('estimate.detail.objectNo')}</div>
<div className="estimate-name">
{currentObjectNo} (Plan No: {planNo})
{currentObjectNo} (Plan No: {currentPid})
</div>
</div>
<div className="estimate-box">
@ -1341,8 +1415,19 @@ export default function Estimate({}) {
{getMessage('estimate.detail.estimateType')} <span className="important">*</span>
</th>
<td colSpan={3}>
<div className="form-flex-wrap">
<div className="radio-wrap">
<div className="d-check-radio light mr10">
{/*pkgRank is null, empty 인 경우 : 사용불가, 이전에 등록된 경우 사용가능, style로 제어*/}
<div
className="d-check-radio light mr10"
style={{
display:
(isNotEmptyArray(storePriceList) > 0 && storePriceList[0].pkgRank !== null && storePriceList[0].pkgRank !== '') ||
estimateContextState?.estimateType === 'YJSS'
? ''
: 'none',
}}
>
<input
type="radio"
name="estimateType"
@ -1372,6 +1457,38 @@ export default function Estimate({}) {
<label htmlFor="YJOD">{getMessage('estimate.detail.estimateType.yjod')}</label>
</div>
</div>
{session?.storeLvl === '1' && agencyCustList.length > 0 ? (
<div className="form-flex-select ml10">
<label htmlFor="">{getMessage('estimate.detail.agency')}</label>
<div className="select-wrap" style={{ width: '400px' }}>
<Select
id="agencyName"
instanceId="agencyName"
className="react-select-custom"
classNamePrefix="custom"
placeholder="Select"
options={agencyCustList}
onChange={(e) => {
if (isObjectNotEmpty(e)) {
setEstimateContextState({ secSapSalesStoreCd: e.sapSalesStoreCd })
} else {
setEstimateContextState({ secSapSalesStoreCd: '' })
}
}}
getOptionLabel={(x) => x.sapSalesStoreNm}
getOptionValue={(x) => x.sapSalesStoreCd}
isClearable={true}
isSearchable={true}
value={agencyCustList.filter(function (option) {
return option.sapSalesStoreCd === estimateContextState.secSapSalesStoreCd
})}
/>
</div>
</div>
) : (
''
)}
</div>
</td>
</tr>
<tr>
@ -1729,15 +1846,17 @@ export default function Estimate({}) {
<button
type="button"
className="btn-origin grey ml5"
onClick={() => {
onClick={(event) => {
setHandlePricingFlag(true)
handlePricing(showPriceCd)
handlePricingBtn(showPriceCd)
}}
>
{getMessage('estimate.detail.showPrice.pricingBtn')}
</button>
</div>
<div className="product-price-wrap ml10">
<div className="product-price-tit">{getMessage('estimate.detail.header.singleCable')}</div>
<div className="select-wrap">
<select
className="select-light"
@ -1747,11 +1866,34 @@ export default function Estimate({}) {
value={cableItem}
>
{cableItemList.length > 0 &&
cableItemList.map((row) => (
<option key={row.clRefChr1} value={row.clRefChr1}>
cableItemList.map((row) => {
if(!row.clRefChr2.includes('S')){
return <option key={row.clRefChr1} value={row.clRefChr1}>
{row.clRefChr2}
</option>
))}
}
})}
</select>
</div>
</div>
<div className="product-price-wrap ml10">
<div className="product-price-tit">{getMessage('estimate.detail.header.doubleCable')}</div>
<div className="select-wrap">
<select
className="select-light"
onChange={(e) => {
onChangeDisplayDoubleCableItem(e.target.value, estimateContextState.itemList)
}}
value={cableDbItem}
>
{cableItemList.length > 0 &&
cableItemList.map((row) => {
if(row.clRefChr2.includes('S')){
return <option key={row.clRefChr1} value={row.clRefChr1}>
{row.clRefChr2.replace('S','')}
</option>
}
})}
</select>
</div>
</div>
@ -1834,7 +1976,7 @@ export default function Estimate({}) {
<input
type="checkbox"
id={item?.dispOrder}
disabled={!!item?.paDispOrder || item.dispCableFlg === '1'}
disabled={!!item?.paDispOrder || item.dispCableFlg === '1X'}
onChange={() => onChangeSelect(item.dispOrder)}
checked={!!selection.has(item.dispOrder)}
/>
@ -1859,8 +2001,13 @@ export default function Estimate({}) {
}
}}
menuPlacement={'auto'}
getOptionLabel={(x) => x.itemName}
getOptionLabel={(x) => x.itemName + ' (' + x.itemNo + ')'}
getOptionValue={(x) => x.itemNo}
components={{
SingleValue: ({ children, ...props }) => {
return <components.SingleValue {...props}>{props.data.itemName}</components.SingleValue>
},
}}
isClearable={false}
isDisabled={!!item?.paDispOrder}
value={displayItemList.filter(function (option) {
@ -1880,12 +2027,17 @@ export default function Estimate({}) {
placeholder="Select"
options={cableItemList}
menuPlacement={'auto'}
getOptionLabel={(x) => x.clRefChr3}
getOptionLabel={(x) => x.clRefChr3 + ' (' + x.clRefChr1 + ')'}
getOptionValue={(x) => x.clRefChr1}
components={{
SingleValue: ({ children, ...props }) => {
return <components.SingleValue {...props}>{(item.itemTpCd === 'M12')? item.itemName : props.data.clRefChr3}</components.SingleValue>
},
}}
isClearable={false}
isDisabled={true}
value={cableItemList.filter(function (option) {
return option.clRefChr1 === item.itemId
return (item.itemTpCd === 'M12')? item.itemId : option.clRefChr1 === item.itemId
})}
/>
)}
@ -1936,7 +2088,11 @@ export default function Estimate({}) {
<input
type="text"
className="input-light al-r"
value={convertNumberToPriceDecimal(item?.showSalePrice === '0' ? null : item?.salePrice?.replaceAll(',', ''))}
value={
item.openFlg === '1'
? 'OPEN'
: convertNumberToPriceDecimal(item?.showSalePrice === '0' ? null : item?.salePrice?.replaceAll(',', ''))
}
disabled={
item.openFlg === '1'
? true
@ -1964,7 +2120,9 @@ export default function Estimate({}) {
</div>
</td>
<td className="al-r">
{convertNumberToPriceDecimal(
{item?.openFlg === '1'
? 'OPEN'
: convertNumberToPriceDecimal(
item?.showSaleTotPrice === '0'
? null
: item?.amount === ''

View File

@ -2,7 +2,7 @@ import { fabric } from 'fabric'
import { v4 as uuidv4 } from 'uuid'
import { QLine } from '@/components/fabric/QLine'
import { distanceBetweenPoints, findTopTwoIndexesByDistance, getDirectionByPoint, sortedPointLessEightPoint, sortedPoints } from '@/util/canvas-util'
import { calculateAngle, drawGabledRoof, drawRidgeRoof, drawShedRoof, toGeoJSON } from '@/util/qpolygon-utils'
import { calculateAngle, drawRidgeRoof, drawShedRoof, toGeoJSON } from '@/util/qpolygon-utils'
import * as turf from '@turf/turf'
import { LINE_TYPE, POLYGON_TYPE } from '@/common/common'
import Big from 'big.js'
@ -29,12 +29,13 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
this.texts = []
this.hips = []
this.ridges = []
this.connectRidges = []
this.cells = []
this.innerLines = []
this.children = []
this.separatePolygon = []
this.toFixed = options.toFixed ?? 1
this.baseLines = []
// this.colorLines = []
// 소수점 전부 제거
points.forEach((point) => {
@ -45,8 +46,11 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
options.sort = options.sort ?? true
options.parentId = options.parentId ?? null
this.isSortedPoints = false
if (!options.sort && points.length <= 8) {
points = sortedPointLessEightPoint(points)
this.isSortedPoints = true
} else {
let isDiagonal = false
points.forEach((point, i) => {
@ -62,6 +66,7 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
if (!isDiagonal) {
points = sortedPoints(points)
this.isSortedPoints = true
}
}
@ -119,10 +124,12 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
this.addLengthText()
this.on('moving', () => {
this.initLines()
this.addLengthText()
})
this.on('modified', (e) => {
this.initLines()
this.addLengthText()
})
@ -183,8 +190,8 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
this.lines = []
this.points.forEach((point, i) => {
const nextPoint = this.points[(i + 1) % this.points.length]
this.getCurrentPoints().forEach((point, i) => {
const nextPoint = this.getCurrentPoints()[(i + 1) % this.points.length]
const line = new QLine([point.x, point.y, nextPoint.x, nextPoint.y], {
stroke: this.stroke,
strokeWidth: this.strokeWidth,
@ -211,6 +218,12 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
* @param settingModalFirstOptions
*/
drawHelpLine(settingModalFirstOptions) {
/* innerLines 초기화 */
this.innerLines.forEach((line) => {
this.canvas.remove(line)
})
this.canvas.renderAll()
let textMode = 'plane'
const dimensionDisplay = settingModalFirstOptions?.dimensionDisplay.find((opt) => opt.selected).id
@ -240,12 +253,13 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
const hasShed = types.includes(LINE_TYPE.WALLLINE.SHED)
// A형, B형 박공 지붕
if (
/* if (
(gableOdd.every((type) => type === LINE_TYPE.WALLLINE.EAVES) && gableEven.every((type) => gableType.includes(type))) ||
(gableEven.every((type) => type === LINE_TYPE.WALLLINE.EAVES) && gableOdd.every((type) => gableType.includes(type)))
) {
drawGabledRoof(this.id, this.canvas, textMode)
} else if (hasShed) {
} else*/
if (hasShed) {
const sheds = this.lines.filter((line) => line.attributes !== undefined && line.attributes.type === LINE_TYPE.WALLLINE.SHED)
const areLinesParallel = function (line1, line2) {
const angle1 = calculateAngle(line1.startPoint, line1.endPoint)

View File

@ -12,7 +12,7 @@ import { usePlan } from '@/hooks/usePlan'
import { useContextMenu } from '@/hooks/useContextMenu'
import { useCanvasConfigInitialize } from '@/hooks/common/useCanvasConfigInitialize'
import { currentMenuState } from '@/store/canvasAtom'
import { totalDisplaySelector } from '@/store/settingAtom'
import { roofMaterialsAtom, totalDisplaySelector } from '@/store/settingAtom'
import { MENU, POLYGON_TYPE } from '@/common/common'
import { FloorPlanContext } from '@/app/floor-plan/FloorPlanProvider'
import { QcastContext } from '@/app/QcastProvider'
@ -30,11 +30,16 @@ import { useCanvasSetting } from '@/hooks/option/useCanvasSetting'
import { useCanvasMenu } from '@/hooks/common/useCanvasMenu'
import { useEvent } from '@/hooks/useEvent'
import { compasDegAtom } from '@/store/orientationAtom'
import { ROOF_MATERIAL_LAYOUT } from '@/components/floor-plan/modal/placementShape/PlacementShapeSetting'
import { useMasterController } from '@/hooks/common/useMasterController'
import { hotkeyStore } from '@/store/hotkeyAtom'
import { usePopup } from '@/hooks/usePopup'
export default function CanvasFrame() {
const canvasRef = useRef(null)
const { canvas } = useCanvas('canvas')
const { canvasLoadInit, gridInit } = useCanvasConfigInitialize()
const { closeAll } = usePopup()
const currentMenu = useRecoilValue(currentMenuState)
const { floorPlanState } = useContext(FloorPlanContext)
const { contextMenu, handleClick } = useContextMenu()
@ -71,8 +76,12 @@ export default function CanvasFrame() {
}
initEvent()
})
} else {
setSelectedMenu(null)
}
Object.keys(currentCanvasPlan).length > 0 && canvas && handleModuleSelectionTotal()
} else {
setSelectedMenu(null)
}
gridInit()
}
@ -92,6 +101,8 @@ export default function CanvasFrame() {
useEffect(() => {
setIsGlobalLoading(false)
// .
closeAll()
return () => {
canvas?.clear()
@ -110,6 +121,38 @@ export default function CanvasFrame() {
resetPcsCheckState()
}
/**
* 캔버스가 있을 경우 핫키 이벤트 처리
* hotkeyStore에 핫키 이벤트 리스너 추가
*
* const hotkeys = [
{ key: 'c', fn: () => asdf() },
{ key: 'v', fn: () => qwer() },
]
setHotkeyStore(hotkeys)
*/
const hotkeyState = useRecoilValue(hotkeyStore)
const hotkeyHandlerRef = useRef(null)
useEffect(() => {
hotkeyHandlerRef.current = (e) => {
hotkeyState.forEach((hotkey) => {
if (e.key === hotkey.key) {
hotkey.fn()
}
})
}
document.addEventListener('keyup', hotkeyHandlerRef.current)
return () => {
if (hotkeyHandlerRef.current) {
document.removeEventListener('keyup', hotkeyHandlerRef.current)
}
}
}, [hotkeyState])
/** 핫키 이벤트 처리 끝 */
return (
<div className="canvas-frame">
<canvas ref={canvasRef} id="canvas" style={{ position: 'relative' }}></canvas>

View File

@ -31,12 +31,13 @@ export default function CanvasLayout({ children }) {
return (
<div className="canvas-layout">
<div className={`canvas-page-list ${['outline', 'surface', 'module'].includes(selectedMenu) ? 'active' : ''}`}>
<div className="canvas-id">{objectNo}</div>
<div className="canvas-plane-wrap">
{plans.map((plan, index) => (
<button
key={`plan-${plan.id}`}
className={`canvas-page-box ${plan.isCurrent === true ? 'on' : ''}`}
onClick={() => handleCurrentPlan(plan.id)}
onClick={() => (plan.isCurrent ? '' : handleCurrentPlan(plan.id))}
>
<span>{`Plan ${plan.planNo}`}</span>
{plans.length > 1 && !pathname.includes('/estimate') && !pathname.includes('/simulator') && (

View File

@ -4,7 +4,7 @@ import { useContext, useEffect, useState } from 'react'
import { usePathname, useRouter, useSearchParams } from 'next/navigation'
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'
import { useRecoilState, useRecoilValue, useResetRecoilState, useSetRecoilState } from 'recoil'
import { v4 as uuidv4 } from 'uuid'
@ -50,6 +50,7 @@ import JA from '@/locales/ja.json'
import { QcastContext } from '@/app/QcastProvider'
import { useRoofFn } from '@/hooks/common/useRoofFn'
import { usePolygon } from '@/hooks/usePolygon'
import { useTrestle } from '@/hooks/module/useTrestle'
export default function CanvasMenu(props) {
const { selectedMenu, setSelectedMenu } = props
const pathname = usePathname()
@ -67,6 +68,7 @@ export default function CanvasMenu(props) {
const globalLocale = useRecoilValue(globalLocaleStore)
const canvas = useRecoilValue(canvasState)
const { handleZoomClear, handleZoom } = useCanvasEvent()
const { setAllModuleSurfaceIsComplete, isAllComplete } = useTrestle()
const { handleMenu } = useMenu()
// const urlParams = useSearchParams()
const { handleEstimateSubmit, fetchSetting, estimateContextState, setEstimateContextState } = useEstimateController()
@ -96,7 +98,7 @@ export default function CanvasMenu(props) {
const [lockButtonStyle, setLockButtonStyle] = useState('') //
const setFloorPlanObjectNo = useSetRecoilState(floorPlanObjectState) //
const resetCommonUtils = useResetRecoilState(commonUtilsState)
//
const { objectNo, pid } = floorPlanState
@ -164,6 +166,7 @@ export default function CanvasMenu(props) {
}
const onClickNav = async (menu) => {
resetCommonUtils()
switch (menu.type) {
case 'drawing':
swalFire({
@ -194,6 +197,7 @@ export default function CanvasMenu(props) {
confirmFn: () => {
//
setAllModuleSurfaceIsComplete(false)
const moduleSurfacesArray = canvas
.getObjects()
.filter((obj) => [POLYGON_TYPE.MODULE_SETUP_SURFACE, POLYGON_TYPE.MODULE, POLYGON_TYPE.OBJECT_SURFACE].includes(obj.name))
@ -233,8 +237,12 @@ export default function CanvasMenu(props) {
await reloadCanvasStatus(objectNo, pid)
break
case 'estimate':
if (!isAllComplete()) {
swalFire({ text: getMessage('estimate.menu.move.valid1') })
return
}
setIsGlobalLoading(true)
promiseGet({ url: `/api/estimate/${objectNo}/${selectedPlan.planNo}/detail` }).then((res) => {
promiseGet({ url: `/api/estimate/${objectNo}/${selectedPlan?.planNo ?? pid}/detail` }).then((res) => {
if (res.status === 200) {
const estimateDetail = res.data
if (estimateDetail.estimateDate !== null) {
@ -242,7 +250,7 @@ export default function CanvasMenu(props) {
setCurrentMenu(menu.title)
setFloorPlanObjectNo({ floorPlanObjectNo: objectNo })
setIsGlobalLoading(false)
router.push(`/floor-plan/estimate/5?pid=${selectedPlan.planNo}&objectNo=${objectNo}`)
router.push(`/floor-plan/estimate/5?pid=${selectedPlan?.planNo ?? pid}&objectNo=${objectNo}`)
if (pathname === '/floor-plan/estimate/5') {
setIsGlobalLoading(false)
}
@ -255,13 +263,13 @@ export default function CanvasMenu(props) {
break
case 'simulation':
setIsGlobalLoading(true)
promiseGet({ url: `/api/estimate/${objectNo}/${selectedPlan.planNo}/detail` }).then((res) => {
promiseGet({ url: `/api/estimate/${objectNo}/${selectedPlan?.planNo ?? pid}/detail` }).then((res) => {
if (res.status === 200) {
const estimateDetail = res.data
if (estimateDetail.estimateDate !== null && estimateDetail.docNo) {
setSelectedMenu(menu.type)
setCurrentMenu(menu.title)
router.push(`/floor-plan/simulator/6?pid=${selectedPlan.planNo}&objectNo=${objectNo}`)
router.push(`/floor-plan/simulator/6?pid=${selectedPlan?.planNo ?? pid}&objectNo=${objectNo}`)
if (pathname === '/floor-plan/simulator/6') {
setIsGlobalLoading(false)
}
@ -548,13 +556,26 @@ export default function CanvasMenu(props) {
{
<div className={`vertical-horizontal ${verticalHorizontalMode ? 'on' : ''}`}>
<span>{getMessage('plan.mode.vertical.horizontal')}</span>
<button onClick={() => setVerticalHorizontalMode(!verticalHorizontalMode)}>{verticalHorizontalMode ? 'ON' : 'OFF'}</button>
<button
title={`${getMessage('plan.mode.vertical.horizontal')} ${verticalHorizontalMode ? 'ON' : 'OFF'}`}
onClick={() => setVerticalHorizontalMode(!verticalHorizontalMode)}
>
{verticalHorizontalMode ? 'ON' : 'OFF'}
</button>
</div>
}
<div className="btn-from">
<button className={`btn01 ${commonUtils.text ? 'active' : ''}`} onClick={() => commonFunctions('text')}></button>
<button className={`btn02 ${commonUtils.dimension ? 'active' : ''} `} onClick={() => commonFunctions('dimension')}></button>
<button className={`btn03 ${commonUtils.distance ? 'active' : ''} `} onClick={() => commonFunctions('distance')}></button>
<button className={`btn01 ${commonUtils.text ? 'active' : ''}`} onClick={() => commonFunctions('text')} title="文字作成"></button>
<button
className={`btn02 ${commonUtils.dimension ? 'active' : ''} `}
onClick={() => commonFunctions('dimension')}
title="寸法作成"
></button>
<button
className={`btn03 ${commonUtils.distance ? 'active' : ''} `}
onClick={() => commonFunctions('distance')}
title="定規"
></button>
</div>
{isObjectNotEmpty(selectedRoofMaterial) && addedRoofs.length > 0 && (
<div className="select-box">
@ -580,6 +601,7 @@ export default function CanvasMenu(props) {
sourceKey={'index'}
targetKey={'index'}
disabled={+basicSetting.roofSizeSet === 3}
tagTitle={'屋根材変更'}
/>
}
</div>
@ -588,9 +610,10 @@ export default function CanvasMenu(props) {
<button
className={`btn10 ${floorPlanState.refFileModalOpen && 'active'}`}
onClick={() => setFloorPlanState({ ...floorPlanState, refFileModalOpen: true })}
title="読込"
></button>
{/*<button className="btn04" onClick={() => setShowCanvasSettingModal(true)}></button>*/}
<button className="btn04" onClick={handlePopup}></button>
<button className="btn04" onClick={handlePopup} title="設定"></button>
</div>
<div className="size-control">
<button
@ -599,7 +622,9 @@ export default function CanvasMenu(props) {
handleZoom(false)
}}
></button>
<span onClick={handleZoomClear}>{canvasZoom}%</span>
<span onClick={handleZoomClear} title="拡大・縮小">
{canvasZoom}%
</span>
<button
className="control-btn plus"
onClick={() => {
@ -608,8 +633,8 @@ export default function CanvasMenu(props) {
></button>
</div>
<div className="btn-from">
<button className="btn08" onClick={handleSaveCanvas}></button>
<button className="btn09" onClick={handleLeaveCanvas}></button>
<button className="btn08" onClick={handleSaveCanvas} title="保存"></button>
<button className="btn09" onClick={handleLeaveCanvas} title="物件検索画面へ移動"></button>
</div>
</>
)}
@ -634,7 +659,7 @@ export default function CanvasMenu(props) {
onClick={() => setEstimatePopupOpen(true)}
>
<span className="ico ico01"></span>
<span className="name">{getMessage('plan.menu.estimate.docDown')}</span>
<span className="name">{getMessage('plan.menu.estimate.docDownload')}</span>
</button>
<button type="button" style={{ display: saveButtonStyle }} className="btn-frame gray ico-flx" onClick={handleEstimateSubmit}>
<span className="ico ico02"></span>

View File

@ -1,15 +1,18 @@
'use client'
import { useEffect } from 'react'
import { useContext, useEffect } from 'react'
import CanvasMenu from '@/components/floor-plan/CanvasMenu'
import { useCanvasMenu } from '@/hooks/common/useCanvasMenu'
import { useCanvasSetting } from '@/hooks/option/useCanvasSetting'
import { usePopup } from '@/hooks/usePopup'
import '@/styles/contents.scss'
import { notFound, useSearchParams } from 'next/navigation'
import { useRecoilState, useResetRecoilState } from 'recoil'
import { useRecoilState, useRecoilValue, useResetRecoilState } from 'recoil'
import { correntObjectNoState } from '@/store/settingAtom'
import { currentMenuState } from '@/store/canvasAtom'
import { globalLocaleStore } from '@/store/localeAtom'
import { useAxios } from '@/hooks/useAxios'
import { GlobalDataContext } from '@/app/GlobalDataProvider'
export default function FloorPlan({ children }) {
const [correntObjectNo, setCurrentObjectNo] = useRecoilState(correntObjectNoState)
@ -20,12 +23,39 @@ export default function FloorPlan({ children }) {
const { selectedMenu, setSelectedMenu } = useCanvasMenu()
const { fetchSettings } = useCanvasSetting()
const resetCurrentMenu = useResetRecoilState(currentMenuState)
const globalLocaleState = useRecoilValue(globalLocaleStore)
const { promiseGet } = useAxios(globalLocaleState)
const { setManagementState } = useContext(GlobalDataContext)
useEffect(() => {
getStuffDetailInfo()
return () => {
resetCurrentMenu()
}
}, [])
const getStuffDetailInfo = () => {
promiseGet({ url: `/api/object/${objectNo}/detail` }).then((res) => {
if (res.status === 200) {
const { data } = res
console.log(data)
let surfaceTypeValue
if (res.data.surfaceType === 'Ⅲ・Ⅳ') {
surfaceTypeValue = '3'
} else if (res.data.surfaceType === 'Ⅱ') {
surfaceTypeValue = '2'
}
// 0
if (res.data.installHeight === '0') {
res.data.installHeight = ''
}
setManagementState({ ...res.data, surfaceTypeValue: surfaceTypeValue })
}
})
}
/**
* URL 파라미터에서 objectNo 설정
*/

View File

@ -5,9 +5,10 @@ import { useEffect } from 'react'
import { useMessage } from '@/hooks/useMessage'
import useMenu from '@/hooks/common/useMenu'
import { canvasState, currentMenuState } from '@/store/canvasAtom'
import { useRecoilState, useRecoilValue } from 'recoil'
import { useRecoilState, useRecoilValue, useResetRecoilState } from 'recoil'
import { subMenusState } from '@/store/menuAtom'
import { useCanvasMenu } from '@/hooks/common/useCanvasMenu'
import { commonUtilsState } from '@/store/commonUtilsAtom'
export default function MenuDepth01() {
const canvas = useRecoilValue(canvasState)
@ -16,8 +17,10 @@ export default function MenuDepth01() {
const { selectedMenu, setSelectedMenu } = useCanvasMenu()
const [currentMenu, setCurrentMenu] = useRecoilState(currentMenuState)
const subMenus = useRecoilValue(subMenusState)
const resetCommonUtils = useResetRecoilState(commonUtilsState)
const onClickMenu = ({ id, menu }) => {
resetCommonUtils()
if (menu === currentMenu) {
handleMenu(selectedMenu)
} else {

View File

@ -120,7 +120,7 @@ export default function ImgLoad() {
value={refImage ? (refImage?.name ?? '') : (currentCanvasPlan?.bgImageName ?? '')}
readOnly
/>
{refImage && <button className="img-check" onClick={handleFileDelete}></button>}
{currentCanvasPlan?.bgImageName && <button className="img-check" onClick={handleFileDelete}></button>}
</div>
</div>
</div>

View File

@ -40,15 +40,15 @@ export default function AuxiliaryEdit(props) {
if (currentObject) {
copy(
currentObject,
arrow2 ? (arrow2 === '←' ? Number(horizonSize) * -1 : Number(horizonSize)) : 0,
arrow1 ? (arrow1 === '↑' ? Number(verticalSize) * -1 : Number(verticalSize)) : 0,
arrow2 ? (arrow2 === '←' ? Number(+horizonSize / 10) * -1 : Number(+horizonSize / 10)) : 0,
arrow1 ? (arrow1 === '↑' ? Number(+verticalSize / 10) * -1 : Number(+verticalSize / 10)) : 0,
)
}
} else {
move(
currentObject,
arrow2 ? (arrow2 === '←' ? Number(horizonSize) * -1 : Number(horizonSize)) : 0,
arrow1 ? (arrow1 === '↑' ? Number(verticalSize) * -1 : Number(verticalSize)) : 0,
arrow2 ? (arrow2 === '←' ? Number(+horizonSize / 10) * -1 : Number(+horizonSize / 10)) : 0,
arrow1 ? (arrow1 === '↑' ? Number(+verticalSize / 10) * -1 : Number(+verticalSize / 10)) : 0,
)
}

View File

@ -1,109 +1,88 @@
import { useMessage } from '@/hooks/useMessage'
import { POLYGON_TYPE, MODULE_SETUP_TYPE } from '@/common/common'
import WithDraggable from '@/components/common/draggable/WithDraggable'
import { useContext, useEffect, useRef, useState } from 'react'
import Module from '@/components/floor-plan/modal/basic/step/Module'
import PitchModule from '@/components/floor-plan/modal/basic/step/pitch/PitchModule'
import { Orientation } from '@/components/floor-plan/modal/basic/step/Orientation'
import PitchPlacement from '@/components/floor-plan/modal/basic/step/pitch/PitchPlacement'
import Placement from '@/components/floor-plan/modal/basic/step/Placement'
import { useRecoilValue, useRecoilState } from 'recoil'
import { canvasSettingState, canvasState, checkedModuleState, isManualModuleSetupState } from '@/store/canvasAtom'
import { usePopup } from '@/hooks/usePopup'
import { Orientation } from '@/components/floor-plan/modal/basic/step/Orientation'
import { useModuleBasicSetting } from '@/hooks/module/useModuleBasicSetting'
import { useEvent } from '@/hooks/useEvent'
import { moduleSelectionDataState, selectedModuleState } from '@/store/selectedModuleOptions'
import { addedRoofsState, corridorDimensionSelector, basicSettingState } from '@/store/settingAtom'
import { isObjectNotEmpty } from '@/util/common-utils'
import Swal from 'sweetalert2'
import { useCanvasPopupStatusController } from '@/hooks/common/useCanvasPopupStatusController'
import { useMasterController } from '@/hooks/common/useMasterController'
import { useModuleBasicSetting } from '@/hooks/module/useModuleBasicSetting'
import { useModuleSelection } from '@/hooks/module/useModuleSelection'
import { useOrientation } from '@/hooks/module/useOrientation'
import { useMessage } from '@/hooks/useMessage'
import { usePopup } from '@/hooks/usePopup'
import {
canvasState,
checkedModuleState,
currentCanvasPlanState,
isManualModuleLayoutSetupState,
isManualModuleSetupState,
toggleManualSetupModeState,
} from '@/store/canvasAtom'
import { loginUserStore } from '@/store/commonAtom'
import { currentCanvasPlanState } from '@/store/canvasAtom'
import { POLYGON_TYPE } from '@/common/common'
import { roofsState } from '@/store/roofAtom'
import { moduleSelectionDataState } from '@/store/selectedModuleOptions'
import { addedRoofsState, basicSettingState } from '@/store/settingAtom'
import { isObjectNotEmpty } from '@/util/common-utils'
import { useEffect, useRef, useState } from 'react'
import { useRecoilState, useRecoilValue } from 'recoil'
import Swal from 'sweetalert2'
import Trestle from './step/Trestle'
export default function BasicSetting({ id, pos = { x: 50, y: 230 } }) {
const { getMessage } = useMessage()
const { closePopup } = usePopup()
const [tabNum, setTabNum] = useState(1)
const canvasSetting = useRecoilValue(canvasSettingState)
const orientationRef = useRef(null)
const { initEvent } = useEvent()
const [isManualModuleSetup, setIsManualModuleSetup] = useRecoilState(isManualModuleSetupState)
const moduleSelectionData = useRecoilValue(moduleSelectionDataState)
const addedRoofs = useRecoilValue(addedRoofsState)
const [isManualModuleLayoutSetup, setIsManualModuleLayoutSetup] = useRecoilState(isManualModuleLayoutSetupState)
const trestleRef = useRef(null)
const [moduleSelectionData, setModuleSelectionData] = useRecoilState(moduleSelectionDataState)
const [addedRoofs, setAddedRoofs] = useRecoilState(addedRoofsState)
const loginUserState = useRecoilValue(loginUserStore)
const currentCanvasPlan = useRecoilValue(currentCanvasPlanState)
const canvas = useRecoilValue(canvasState)
const [basicSetting, setBasicSettings] = useRecoilState(basicSettingState)
const [isClosePopup, setIsClosePopup] = useState({ close: false, id: 0 })
const [checkedModules, setCheckedModules] = useRecoilState(checkedModuleState)
const [roofs, setRoofs] = useState(addedRoofs)
const [manualSetupMode, setManualSetupMode] = useRecoilState(toggleManualSetupModeState)
const [layoutSetup, setLayoutSetup] = useState([{}])
const {
selectedModules,
roughnessCodes,
windSpeedCodes,
managementState,
setManagementState,
moduleList,
setSelectedModules,
selectedSurfaceType,
setSelectedSurfaceType,
installHeight,
setInstallHeight,
standardWindSpeed,
setStandardWindSpeed,
verticalSnowCover,
setVerticalSnowCover,
handleChangeModule,
handleChangeSurfaceType,
handleChangeWindSpeed,
handleChangeInstallHeight,
handleChangeVerticalSnowCover,
} = useModuleSelection({ addedRoofs })
const { nextStep, compasDeg, setCompasDeg } = useOrientation()
const { trigger: orientationTrigger } = useCanvasPopupStatusController(1)
const { trigger: trestleTrigger } = useCanvasPopupStatusController(2)
const { trigger: placementTrigger } = useCanvasPopupStatusController(3)
const [roofsStore, setRoofsStore] = useRecoilState(roofsState)
// const { initEvent } = useContext(EventContext)
const { manualModuleSetup, autoModuleSetup, manualFlatroofModuleSetup, autoFlatroofModuleSetup } = useModuleBasicSetting(tabNum)
const { manualModuleSetup, autoModuleSetup, manualFlatroofModuleSetup, autoFlatroofModuleSetup, manualModuleLayoutSetup, restoreModuleInstArea } =
useModuleBasicSetting(tabNum)
const { updateObjectDate } = useMasterController()
const handleBtnNextStep = () => {
if (tabNum === 1) {
orientationRef.current.handleNextStep()
} else if (tabNum === 2) {
if (basicSetting.roofSizeSet !== '3') {
if (!isObjectNotEmpty(moduleSelectionData.module)) {
Swal.fire({
title: getMessage('module.not.found'),
icon: 'warning',
})
return
}
if (addedRoofs.length !== moduleSelectionData.roofConstructions.length) {
Swal.fire({
title: getMessage('construction.length.difference'),
icon: 'warning',
})
return
}
//
updateObjectDataApi({
objectNo: currentCanvasPlan.objectNo, //_no
standardWindSpeedId: moduleSelectionData.common.stdWindSpeed, //
verticalSnowCover: moduleSelectionData.common.stdSnowLd, //
surfaceType: moduleSelectionData.common.illuminationTpNm, //
installHeight: moduleSelectionData.common.instHt, //
userId: loginUserState.userId, //
})
} else {
if (!isObjectNotEmpty(moduleSelectionData.module)) {
Swal.fire({
title: getMessage('module.not.found'),
icon: 'warning',
})
return
}
}
}
setTabNum(tabNum + 1)
}
const placementRef = {
isChidori: useRef('false'),
setupLocation: useRef('eaves'),
isMaxSetup: useRef('false'),
}
const placementFlatRef = {
setupLocation: useRef('south'),
}
const handleManualModuleSetup = () => {
setIsManualModuleSetup(!isManualModuleSetup)
}
const updateObjectDataApi = async (params) => {
const res = await updateObjectDate(params)
}
useEffect(() => {
const moduleTabNum = basicSetting.roofSizeSet != 3 ? 3 : 2
let hasModules = canvas
.getObjects()
.filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE)
@ -111,23 +90,42 @@ export default function BasicSetting({ id, pos = { x: 50, y: 230 } }) {
if (hasModules) {
orientationRef.current.handleNextStep()
setTabNum(3)
setTabNum(moduleTabNum)
}
}, [])
//
const handleClosePopup = (id) => {
if (tabNum == 3) {
if (isManualModuleSetup) {
setIsManualModuleSetup(false)
useEffect(() => {
if (roofsStore && addedRoofs) {
setRoofs(
addedRoofs.map((roof, index) => {
return {
...roof,
...roofsStore[index]?.addRoof,
construction: roofsStore[index]?.construction,
trestle: roofsStore[index]?.trestle,
trestleDetail: roofsStore[index]?.trestleDetail,
}
}),
)
setModuleSelectionData({
...moduleSelectionData,
roofConstructions: roofsStore.map((roof) => {
return {
roofIndex: roof.roofIndex,
addRoof: roof.addRoof,
construction: roof.construction,
trestle: roof.trestle,
trestleDetail: roof.trestleDetail,
}
setIsClosePopup({ close: true, id: id })
}),
})
}
}, [roofsStore, addedRoofs])
useEffect(() => {
if (basicSetting.roofSizeSet !== '3') {
manualModuleSetup(placementRef)
manualModuleSetup()
} else {
manualFlatroofModuleSetup(placementFlatRef)
}
@ -140,55 +138,224 @@ export default function BasicSetting({ id, pos = { x: 50, y: 230 } }) {
setIsManualModuleSetup(false)
}, [checkedModules])
useEffect(() => {
if (basicSetting.roofSizeSet !== '3') {
if (manualSetupMode.indexOf('manualSetup') > -1) {
manualModuleSetup()
} else if (manualSetupMode.indexOf('manualLayoutSetup') > -1) {
manualModuleLayoutSetup(layoutSetup)
} else if (manualSetupMode.indexOf('off') > -1) {
manualModuleSetup()
manualModuleLayoutSetup(layoutSetup)
}
} else {
manualFlatroofModuleSetup(placementFlatRef)
}
if (isClosePopup.close) {
closePopup(isClosePopup.id)
}
}, [manualSetupMode, isClosePopup])
useEffect(() => {
if (isManualModuleLayoutSetup) {
manualModuleLayoutSetup(layoutSetup)
}
}, [layoutSetup])
useEffect(() => {
setIsManualModuleSetup(false)
setIsManualModuleLayoutSetup(false)
setManualSetupMode(`off`)
}, [checkedModules])
const handleBtnNextStep = () => {
if (tabNum === 1) {
orientationRef.current.handleNextStep()
setAddedRoofs(roofs)
// setTabNum(tabNum + 1)
return
} else if (tabNum === 2) {
if (basicSetting.roofSizeSet !== '3') {
// if (addedRoofs.length !== moduleSelectionData.roofConstructions.length) {
// Swal.fire({
// title: getMessage('construction.length.difference'),
// icon: 'warning',
// })
// return
// }
trestleRef.current.isComplete().then((res) => {
if (!res) return
})
//
} else {
if (!isObjectNotEmpty(moduleSelectionData.module)) {
Swal.fire({
title: getMessage('module.not.found'),
icon: 'warning',
})
return
}
setTabNum(tabNum + 1)
}
}
}
const placementFlatRef = {
setupLocation: useRef('south'),
}
const handleManualModuleSetup = () => {
setManualSetupMode(`manualSetup_${!isManualModuleSetup}`)
setIsManualModuleSetup(!isManualModuleSetup)
}
const handleManualModuleLayoutSetup = () => {
setManualSetupMode(`manualLayoutSetup_${!isManualModuleLayoutSetup}`)
setIsManualModuleLayoutSetup(!isManualModuleLayoutSetup)
}
const updateObjectDataApi = async (params) => {
const res = await updateObjectDate(params)
}
//
const handleClosePopup = (id) => {
if (tabNum == 3) {
if (isManualModuleSetup) {
setIsManualModuleSetup(false)
}
if (isManualModuleLayoutSetup) {
setIsManualModuleLayoutSetup(false)
}
}
setIsClosePopup({ close: true, id: id })
}
const orientationProps = {
roofs,
setRoofs,
tabNum,
setTabNum,
compasDeg, //
setCompasDeg,
selectedModules,
moduleSelectionData,
setModuleSelectionData,
roughnessCodes, //
windSpeedCodes, //
managementState,
setManagementState,
moduleList, //
setSelectedModules,
selectedSurfaceType,
setSelectedSurfaceType,
installHeight, //
setInstallHeight,
standardWindSpeed, //
setStandardWindSpeed,
verticalSnowCover, //
setVerticalSnowCover,
currentCanvasPlan,
loginUserState,
handleChangeModule,
handleChangeSurfaceType,
handleChangeWindSpeed,
handleChangeInstallHeight,
handleChangeVerticalSnowCover,
orientationTrigger,
nextStep,
updateObjectDataApi,
}
const trestleProps = {
roofs,
setRoofs,
setRoofsStore,
tabNum,
setTabNum,
moduleSelectionData,
setModuleSelectionData,
trestleTrigger,
}
const placementProps = {}
return (
<WithDraggable isShow={true} pos={pos} className="lx-2">
<WithDraggable isShow={true} pos={pos} className={basicSetting.roofSizeSet && basicSetting.roofSizeSet != '3' ? 'll' : 'lx-2'}>
<WithDraggable.Header title={getMessage('plan.menu.module.circuit.setting.default')} onClose={() => handleClosePopup(id)} />
<WithDraggable.Body>
<div className="roof-module-tab">
<div className={`module-tab-bx act`}>{getMessage('modal.module.basic.setting.orientation.setting')}</div>
<span className={`tab-arr ${tabNum !== 1 ? 'act' : ''}`}></span>
{basicSetting.roofSizeSet && basicSetting.roofSizeSet != '3' && (
<>
<div className={`module-tab-bx ${tabNum !== 1 ? 'act' : ''}`}>{getMessage('modal.module.basic.setting.module.setting')}</div>
<span className={`tab-arr ${tabNum === 3 ? 'act' : ''}`}></span>
<div className={`module-tab-bx ${tabNum === 3 ? 'act' : ''}`}>{getMessage('modal.module.basic.setting.module.placement')}</div>
</>
)}
{basicSetting.roofSizeSet && basicSetting.roofSizeSet == '3' && (
<>
<div className={`module-tab-bx ${tabNum === 2 ? 'act' : ''}`}>{getMessage('modal.module.basic.setting.module.placement')}</div>
</>
)}
</div>
{tabNum === 1 && <Orientation ref={orientationRef} tabNum={tabNum} setTabNum={setTabNum} />}
{tabNum === 1 && <Orientation ref={orientationRef} {...orientationProps} />}
{/*배치면 초기설정 - 입력방법: 복시도 입력 || 실측값 입력*/}
{basicSetting.roofSizeSet && basicSetting.roofSizeSet != '3' && tabNum === 2 && <Module setTabNum={setTabNum} />}
{basicSetting.roofSizeSet && basicSetting.roofSizeSet != '3' && tabNum === 3 && <Placement setTabNum={setTabNum} ref={placementRef} />}
{basicSetting.roofSizeSet && basicSetting.roofSizeSet != '3' && tabNum === 2 && <Trestle ref={trestleRef} {...trestleProps} />}
{basicSetting.roofSizeSet && basicSetting.roofSizeSet != '3' && tabNum === 3 && (
<Placement setTabNum={setTabNum} layoutSetup={layoutSetup} setLayoutSetup={setLayoutSetup} />
)}
{/*배치면 초기설정 - 입력방법: 육지붕*/}
{basicSetting.roofSizeSet && basicSetting.roofSizeSet == '3' && tabNum === 2 && <PitchModule setTabNum={setTabNum} />}
{basicSetting.roofSizeSet && basicSetting.roofSizeSet == '3' && tabNum === 3 && (
{/* {basicSetting.roofSizeSet && basicSetting.roofSizeSet == '3' && tabNum === 3 && <PitchModule setTabNum={setTabNum} />} */}
{basicSetting.roofSizeSet && basicSetting.roofSizeSet == '3' && tabNum === 2 && (
<PitchPlacement setTabNum={setTabNum} ref={placementFlatRef} />
)}
<div className="grid-btn-wrap">
{/* {tabNum === 1 && <button className="btn-frame modal mr5">{getMessage('modal.common.save')}</button>} */}
{basicSetting.roofSizeSet && basicSetting.roofSizeSet != '3' && (
<>
{tabNum !== 1 && (
<button className="btn-frame modal mr5" onClick={() => setTabNum(tabNum - 1)}>
{getMessage('modal.module.basic.setting.prev')}
</button>
)}
{/*{tabNum !== 3 && <button className="btn-frame modal act mr5">{getMessage('modal.common.save')}</button>}*/}
{tabNum !== 3 && (
<button className="btn-frame modal" onClick={handleBtnNextStep}>
Next
</button>
)}
{tabNum === 3 && (
<>
{basicSetting.roofSizeSet && basicSetting.roofSizeSet != '3' && (
<>
<button className={`btn-frame modal mr5 ${isManualModuleLayoutSetup ? 'act' : ''}`} onClick={handleManualModuleLayoutSetup}>
{getMessage('modal.module.basic.setting.row.batch')}
</button>
<button className="btn-frame modal mr5" onClick={() => autoModuleSetup(MODULE_SETUP_TYPE.LAYOUT, layoutSetup)}>
{getMessage('modal.module.basic.setting.auto.row.batch')}
</button>
<button className={`btn-frame modal mr5 ${isManualModuleSetup ? 'act' : ''}`} onClick={handleManualModuleSetup}>
{getMessage('modal.module.basic.setting.passivity.placement')}
</button>
<button className="btn-frame modal act" onClick={() => autoModuleSetup(placementRef)}>
<button className="btn-frame modal act mr5" onClick={() => autoModuleSetup(MODULE_SETUP_TYPE.AUTO)}>
{getMessage('modal.module.basic.setting.auto.placement')}
</button>
</>
)}
</>
)}
{basicSetting.roofSizeSet && basicSetting.roofSizeSet == '3' && (
<>
{tabNum === 1 && (
<button className="btn-frame modal" onClick={handleBtnNextStep}>
Next
</button>
)}
{tabNum === 2 && (
<>
<button className="btn-frame modal mr5" onClick={() => setTabNum(tabNum - 1)}>
{getMessage('modal.module.basic.setting.prev')}
</button>
<button className={`btn-frame modal mr5 ${isManualModuleSetup ? 'act' : ''}`} onClick={handleManualModuleSetup}>
{getMessage('modal.module.basic.setting.passivity.placement')}
</button>

View File

@ -1,54 +1,273 @@
import { forwardRef, useContext, useEffect, useImperativeHandle, useState } from 'react'
import { forwardRef, use, useContext, useEffect, useImperativeHandle, useState } from 'react'
import { useMessage } from '@/hooks/useMessage'
import { useOrientation } from '@/hooks/module/useOrientation'
import { getDegreeInOrientation } from '@/util/canvas-util'
import { numberCheck } from '@/util/common-utils'
import { useCanvasPopupStatusController } from '@/hooks/common/useCanvasPopupStatusController'
import { addedRoofsState, basicSettingState } from '@/store/settingAtom'
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'
import QSelectBox from '@/components/common/select/QSelectBox'
import { roofsState } from '@/store/roofAtom'
import { useModuleBasicSetting } from '@/hooks/module/useModuleBasicSetting'
import Swal from 'sweetalert2'
export const Orientation = forwardRef(({ tabNum }, ref) => {
export const Orientation = forwardRef((props, ref) => {
const { getMessage } = useMessage()
const { trigger: canvasPopupStatusTrigger } = useCanvasPopupStatusController(1)
const { nextStep, compasDeg, setCompasDeg } = useOrientation()
const [hasAnglePassivity, setHasAnglePassivity] = useState(false)
const basicSetting = useRecoilValue(basicSettingState)
const [addedRoofs, setAddedRoofs] = useRecoilState(addedRoofsState) //
const [roofsStore, setRoofsStore] = useRecoilState(roofsState)
const [roofTab, setRoofTab] = useState(0) //
const {
roofs,
setRoofs,
tabNum,
setTabNum,
compasDeg,
setCompasDeg,
selectedModules,
roughnessCodes,
windSpeedCodes,
managementState,
setManagementState,
moduleList,
moduleSelectionData,
setModuleSelectionData,
setSelectedModules,
selectedSurfaceType,
setSelectedSurfaceType,
installHeight,
setInstallHeight,
standardWindSpeed,
setStandardWindSpeed,
verticalSnowCover,
setVerticalSnowCover,
orientationTrigger,
nextStep,
currentCanvasPlan,
loginUserState,
updateObjectDataApi,
} = props
const [inputCompasDeg, setInputCompasDeg] = useState(compasDeg ?? 0)
const [inputInstallHeight, setInputInstallHeight] = useState('0')
const [inputMargin, setInputMargin] = useState('0')
const [inputVerticalSnowCover, setInputVerticalSnowCover] = useState('0')
const [inputRoughness, setInputRoughness] = useState(selectedSurfaceType)
const [inputStandardWindSpeed, setInputStandardWindSpeed] = useState(standardWindSpeed)
const { restoreModuleInstArea } = useModuleBasicSetting()
const moduleData = {
header: [
{ name: getMessage('module'), width: 150, prop: 'module', type: 'color-box' },
{
name: `${getMessage('height')} (mm)`,
prop: 'height',
},
{ name: `${getMessage('width')} (mm)`, prop: 'width' },
{ name: `${getMessage('output')} (W)`, prop: 'output' },
],
}
useEffect(() => {
if (basicSetting.roofSizeSet == '3') {
restoreModuleInstArea()
}
}, [])
useEffect(() => {
if (moduleSelectionData?.common) {
setInputMargin(moduleSelectionData?.common?.margin)
}
}, [moduleSelectionData])
useEffect(() => {
if (selectedModules) {
setSelectedModules(moduleList.find((module) => module.itemId === selectedModules.itemId))
}
}, [selectedModules])
useEffect(() => {
if (selectedSurfaceType) {
setInputRoughness(roughnessCodes.find((code) => code.clCode === managementState?.surfaceTypeValue))
}
}, [selectedSurfaceType])
useEffect(() => {
if (standardWindSpeed) setInputStandardWindSpeed(windSpeedCodes.find((code) => code.clCode === managementState?.standardWindSpeedId))
}, [standardWindSpeed])
useEffect(() => {
if (managementState?.installHeight && managementState?.installHeight) {
setSelectedSurfaceType(roughnessCodes.find((code) => code.clCode === managementState?.surfaceTypeValue))
setInputInstallHeight(managementState?.installHeight)
setStandardWindSpeed(windSpeedCodes.find((code) => code.clCode === managementState?.standardWindSpeedId))
setInputVerticalSnowCover(managementState?.verticalSnowCover)
}
}, [managementState])
useImperativeHandle(ref, () => ({
handleNextStep,
}))
const handleNextStep = () => {
nextStep()
canvasPopupStatusTrigger(compasDeg)
if (isComplete()) {
const common = {
illuminationTp: inputRoughness.clCode,
illuminationTpNm: inputRoughness.clCodeNm,
instHt: inputInstallHeight,
stdWindSpeed: inputStandardWindSpeed?.clCode,
stdSnowLd: inputVerticalSnowCover,
saleStoreNorthFlg: managementState?.saleStoreNorthFlg,
moduleTpCd: selectedModules.itemTp,
moduleItemId: selectedModules.itemId,
margin: inputMargin,
}
setCompasDeg(inputCompasDeg)
setInstallHeight(inputInstallHeight)
setVerticalSnowCover(inputVerticalSnowCover)
setSelectedSurfaceType(inputRoughness)
setStandardWindSpeed(inputStandardWindSpeed)
nextStep(inputCompasDeg)
setManagementState({
...managementState,
installHeight: inputInstallHeight,
verticalSnowCover: inputVerticalSnowCover,
standardWindSpeedId: inputStandardWindSpeed?.clCode,
surfaceType: inputRoughness.clCodeNm,
surfaceTypeValue: inputRoughness.clCode,
})
setModuleSelectionData({
...moduleSelectionData,
module: {
...selectedModules,
},
common,
})
orientationTrigger({
compasDeg: inputCompasDeg,
common: common,
module: {
...selectedModules,
},
margin: inputMargin,
})
updateObjectDataApi({
objectNo: currentCanvasPlan.objectNo, //_no
standardWindSpeedId: inputStandardWindSpeed?.clCode, //
verticalSnowCover: inputVerticalSnowCover, //
surfaceType: inputRoughness.clCodeNm, //
installHeight: inputInstallHeight, //
userId: loginUserState.userId, //
})
setTabNum(2)
} else {
if (!selectedModules || !selectedModules.itemId) {
Swal.fire({
title: getMessage('module.not.found'),
icon: 'warning',
})
return
}
}
}
useEffect(() => {
checkDegree(compasDeg)
}, [compasDeg])
const checkDegree = (e) => {
if (e === '-0' || e === '-') {
setCompasDeg('-')
setInputCompasDeg('-')
return
}
if (e === '0-') {
setCompasDeg('-0')
setInputCompasDeg('-0')
return
}
if (Number(e) >= -180 && Number(e) <= 180) {
if (numberCheck(Number(e))) {
setCompasDeg(Number(e))
setInputCompasDeg(Number(e))
}
} else {
setCompasDeg(compasDeg)
setInputCompasDeg(compasDeg)
}
}
const isComplete = () => {
if (!selectedModules || !selectedModules.itemId) return false
if (basicSetting && basicSetting.roofSizeSet !== '3') {
if (inputInstallHeight <= 0) {
return false
}
if (+inputVerticalSnowCover <= 0) {
return false
}
if (!inputStandardWindSpeed) return false
if (!inputRoughness) return false
}
return true
}
const handleChangeModule = (e) => {
resetRoofs()
setSelectedModules(e)
}
const handleChangeRoughness = (e) => {
resetRoofs()
setInputRoughness(e)
}
const handleChangeInstallHeight = (e) => {
resetRoofs()
setInputInstallHeight(e)
}
const handleChangeStandardWindSpeed = (e) => {
resetRoofs()
setInputStandardWindSpeed(e)
}
const handleChangeVerticalSnowCover = (e) => {
resetRoofs()
setInputVerticalSnowCover(e)
}
const resetRoofs = () => {
const newRoofs = addedRoofs.map((roof) => {
return {
...roof,
trestle: {
lengthBase: null,
trestleMkrCd: null,
constMthdCd: null,
constTp: null,
roofBaseCd: null,
roofPchBase: null,
},
addRoof: {
...roof.addRoof,
lengthBase: null,
eavesMargin: null,
kerabaMargin: null,
ridgeMargin: null,
},
construction: {
constTp: null,
cvrYn: 'N',
snowGdPossYn: 'N',
cvrChecked: false,
snowGdChecked: false,
},
}
})
// setRoofs(newRoofs)
// setAddedRoofs(newRoofs)
setRoofsStore(newRoofs)
}
return (
<>
<div className="properties-setting-wrap">
<div className="outline-wrap">
<div className="roof-module-inner">
<div className="compas-wrapper">
<div className="guide">{getMessage('modal.module.basic.setting.orientation.setting.info')}</div>
<div className="roof-module-compas">
<div className="compas-box">
@ -56,13 +275,13 @@ export const Orientation = forwardRef(({ tabNum }, ref) => {
{Array.from({ length: 180 / 15 }).map((dot, index) => (
<div
key={index}
className={`circle ${getDegreeInOrientation(compasDeg) === -1 * (-15 * index + 180) || (index === 0 && compasDeg >= 172 && index === 0 && compasDeg <= 180) || (compasDeg === -180 && index === 0) ? 'act' : ''}`}
className={`circle ${getDegreeInOrientation(inputCompasDeg) === -1 * (-15 * index + 180) || (index === 0 && inputCompasDeg >= 172 && index === 0 && inputCompasDeg <= 180) || (inputCompasDeg === -180 && index === 0) ? 'act' : ''}`}
onClick={() => {
if (index === 0) {
setCompasDeg(180)
setInputCompasDeg(180)
return
}
setCompasDeg(-1 * (-15 * index + 180))
setInputCompasDeg(-1 * (-15 * index + 180))
}}
>
{index === 0 && <i>180°</i>}
@ -72,44 +291,175 @@ export const Orientation = forwardRef(({ tabNum }, ref) => {
{Array.from({ length: 180 / 15 }).map((dot, index) => (
<div
key={index}
className={`circle ${compasDeg !== 180 && getDegreeInOrientation(compasDeg) === 15 * index ? 'act' : ''}`}
onClick={() => setCompasDeg(15 * index)}
className={`circle ${inputCompasDeg !== 180 && getDegreeInOrientation(inputCompasDeg) === 15 * index ? 'act' : ''}`}
onClick={() => setInputCompasDeg(15 * index)}
>
{index === 0 && <i>0°</i>}
{index === 6 && <i>90°</i>}
</div>
))}
<div className="compas">
<div className="compas-arr" style={{ transform: `rotate(${getDegreeInOrientation(compasDeg)}deg)` }}></div>
<div className="compas-arr" style={{ transform: `rotate(${getDegreeInOrientation(inputCompasDeg)}deg)` }}></div>
</div>
</div>
</div>
</div>
<div className="center-wrap">
<div className="d-check-box pop">
<input type="checkbox" id="ch99" checked={hasAnglePassivity} onChange={() => setHasAnglePassivity(!hasAnglePassivity)} />
<label htmlFor="ch99">{getMessage('modal.module.basic.setting.orientation.setting.angle.passivity')}-180 180</label>
</div>
<div className="outline-form">
<div className="input-grid mr10" style={{ width: '160px' }}>
<div className="d-check-box pop mr10">
<input type="checkbox" id="ch99" checked={hasAnglePassivity} onChange={() => setHasAnglePassivity(!hasAnglePassivity)} />
<label htmlFor="ch99">{getMessage('modal.module.basic.setting.orientation.setting.angle.passivity')}</label>
</div>
<div className="input-grid mr10" style={{ width: '60px' }}>
<input
type="text"
className="input-origin block"
value={compasDeg}
value={inputCompasDeg}
readOnly={!hasAnglePassivity}
placeholder={0}
onChange={
(e) => checkDegree(e.target.value)
// setCompasDeg(
// e.target.value === '-' || (e.target.value !== '' && parseInt(e.target.value) <= 180 && parseInt(e.target.value) >= -180)
// ? e.target.value
// : 0,
// )
}
onChange={(e) => checkDegree(e.target.value)}
/>
</div>
<span className="thin">°</span>
<span className="thin"> -180 180 </span>
</div>
</div>
</div>
<div className="compas-table-wrap">
<div className="compas-table-box mb10">
<div className="outline-form mb10">
<span>{getMessage('modal.module.basic.setting.module.setting')}</span>
<div className="grid-select">
{moduleList && (
<QSelectBox
options={moduleList}
value={selectedModules}
targetKey={'itemId'}
sourceKey={'itemId'}
showKey={'itemNm'}
onChange={(e) => handleChangeModule(e)}
/>
)}
</div>
</div>
<div className="roof-module-table">
<table>
<thead>
<tr>
{moduleData.header.map((header) => {
return (
<th key={header.prop} style={{ width: header.width ? header.width + 'px' : '' }}>
{header.name}
</th>
)
})}
</tr>
</thead>
<tbody>
{Array.from({ length: 3 }).map((_, index) => {
return selectedModules && selectedModules?.itemList && selectedModules?.itemList?.length >= index + 1 ? (
<tr key={index}>
<td>
<div className="color-wrap">
<span
className="color-box"
style={{
backgroundColor: selectedModules.itemList[index].color,
}}
></span>
<span className="name">{selectedModules.itemList[index].itemNm}</span>
</div>
</td>
<td className="al-r">{Number(selectedModules.itemList[index].shortAxis).toFixed(0)}</td>
<td className="al-r">{Number(selectedModules.itemList[index].longAxis).toFixed(0)}</td>
<td className="al-r">{Number(selectedModules.itemList[index].wpOut).toFixed(0)}</td>
</tr>
) : (
<tr key={index}>
<td>
<div className="color-wrap"></div>
</td>
<td className="al-r"></td>
<td className="al-r"></td>
<td className="al-r"></td>
</tr>
)
})}
</tbody>
</table>
</div>
{basicSetting && basicSetting.roofSizeSet == '3' && (
<div className="outline-form mt15">
<span>{getMessage('modal.module.basic.setting.module.placement.area')}</span>
<div className="input-grid mr10" style={{ width: '60px' }}>
<input type="number" className="input-origin block" value={inputMargin} onChange={(e) => setInputMargin(e.target.value)} />
</div>
<span className="thin">m</span>
</div>
)}
</div>
{basicSetting && basicSetting.roofSizeSet != '3' && (
<div className="compas-table-box">
<div className="compas-grid-table">
<div className="outline-form">
<span>{getMessage('modal.module.basic.setting.module.surface.type')}</span>
<div className="grid-select">
{roughnessCodes.length > 0 && managementState && (
<QSelectBox
options={roughnessCodes}
value={inputRoughness}
targetKey={'clCode'}
sourceKey={'clCode'}
showKey={'clCodeNm'}
onChange={(e) => handleChangeRoughness(e)}
/>
)}
</div>
</div>
<div className="outline-form">
<span>{getMessage('modal.module.basic.setting.module.fitting.height')}</span>
<div className="input-grid mr10">
<input
type="number"
className="input-origin block"
value={inputInstallHeight}
onChange={(e) => handleChangeInstallHeight(e.target.value)}
/>
</div>
<span className="thin">m</span>
</div>
<div className="outline-form">
<span>{getMessage('modal.module.basic.setting.module.standard.wind.speed')}</span>
<div className="grid-select">
{windSpeedCodes.length > 0 && managementState && (
<QSelectBox
title={''}
options={windSpeedCodes}
value={inputStandardWindSpeed}
targetKey={'clCode'}
sourceKey={'clCode'}
showKey={'clCodeNm'}
onChange={(e) => handleChangeStandardWindSpeed(e)}
/>
)}
</div>
</div>
<div className="outline-form">
<span>{getMessage('modal.module.basic.setting.module.standard.snowfall.amount')}</span>
<div className="input-grid mr10">
<input
type="number"
className="input-origin block"
value={inputVerticalSnowCover}
onChange={(e) => handleChangeVerticalSnowCover(e.target.value)}
/>
</div>
<span className="thin">cm</span>
</div>
</div>
</div>
)}
</div>
</div>
</div>

View File

@ -1,29 +1,45 @@
import { forwardRef, useEffect, useState } from 'react'
import { useMessage } from '@/hooks/useMessage'
import { useModuleBasicSetting } from '@/hooks/module/useModuleBasicSetting'
import { checkedModuleState, currentCanvasPlanState, isManualModuleSetupState } from '@/store/canvasAtom'
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'
import {
checkedModuleState,
isManualModuleLayoutSetupState,
isManualModuleSetupState,
moduleRowColArrayState,
moduleSetupOptionState,
toggleManualSetupModeState,
} from '@/store/canvasAtom'
import { useRecoilState, useRecoilValue, useResetRecoilState, useSetRecoilState } from 'recoil'
import { moduleSelectionDataState, selectedModuleState } from '@/store/selectedModuleOptions'
import { isObjectNotEmpty } from '@/util/common-utils'
import Image from 'next/image'
const Placement = forwardRef((props, refs) => {
const { getMessage } = useMessage()
const [isChidori, setIsChidori] = useState(false)
const [useTab, setUseTab] = useState(true)
const [guideType, setGuideType] = useState('batch')
const [isChidoriNotAble, setIsChidoriNotAble] = useState(false)
const [setupLocation, setSetupLocation] = useState('eaves')
const [isMaxSetup, setIsMaxSetup] = useState('false')
const [selectedItems, setSelectedItems] = useState({})
const [selectedModules, setSelectedModules] = useRecoilState(selectedModuleState)
const setCheckedModules = useSetRecoilState(checkedModuleState)
const moduleSelectionData = useRecoilValue(moduleSelectionDataState)
const { makeModuleInitArea } = useModuleBasicSetting(3)
const { makeModuleInitArea, roofOutlineColor } = useModuleBasicSetting(3)
const [isMultiModule, setIsMultiModule] = useState(false)
const [isManualModuleSetup, setIsManualModuleSetup] = useRecoilState(isManualModuleSetupState)
//
const setIsManualModuleSetup = useSetRecoilState(isManualModuleSetupState)
const setIsManualModuleLayoutSetup = useSetRecoilState(isManualModuleLayoutSetupState)
const setManualSetupMode = useSetRecoilState(toggleManualSetupModeState)
const [moduleSetupOption, setModuleSetupOption] = useRecoilState(moduleSetupOptionState) //
const resetModuleSetupOption = useResetRecoilState(moduleSetupOptionState)
const [colspan, setColspan] = useState(1)
const moduleRowColArray = useRecoilValue(moduleRowColArrayState)
//
useEffect(() => {
@ -36,11 +52,24 @@ const Placement = forwardRef((props, refs) => {
makeModuleInitArea(moduleSelectionData)
}
if (moduleSelectionData.module.itemList.length > 1) {
setColspan(2)
}
return () => {
// refs.isChidori.current = 'false'
// refs.setupLocation.current = 'eaves'
setIsManualModuleSetup(false)
setIsManualModuleLayoutSetup(false)
setManualSetupMode('off')
resetModuleSetupOption()
}
}, [])
// useEffect(() => {
// console.log('moduleRowColArray', moduleRowColArray)
// }, [moduleRowColArray])
//
useEffect(() => {
if (isObjectNotEmpty(moduleSelectionData)) {
@ -54,8 +83,10 @@ const Placement = forwardRef((props, refs) => {
initCheckedModule = { ...initCheckedModule, [obj.itemId]: true }
}
})
setSelectedItems(initCheckedModule)
setSelectedModules(moduleSelectionData.module)
props.setLayoutSetup(moduleSelectionData.module.itemList.map((item) => ({ moduleId: item.itemId, col: 0, row: 0, checked: true })))
}
//
@ -80,46 +111,63 @@ const Placement = forwardRef((props, refs) => {
header: [
{ type: 'check', name: '', prop: 'check', width: 70 },
{ type: 'color-box', name: getMessage('module'), prop: 'module' },
{ type: 'text', name: `${getMessage('output')} (W)`, prop: 'output', width: 70 },
{ type: 'text', name: getMessage('modal.module.basic.setting.module.placement.mix.asg.yn'), prop: 'mixAsgYn', width: 50 },
{ type: 'text', name: `段数`, prop: 'rows', width: 60 },
{ type: 'text', name: `列数`, prop: 'cols', width: 60 },
],
rows: [],
}
const handleChangeChidori = (e) => {
const bool = e.target.value === 'true' ? true : false
setIsChidori(bool)
refs.isChidori.current = e.target.value
setModuleSetupOption({ ...moduleSetupOption, isChidori: bool })
//
setIsManualModuleSetup(false)
setIsManualModuleLayoutSetup(false)
setManualSetupMode('off')
}
const handleSetupLocation = (e) => {
setSetupLocation(e.target.value)
refs.setupLocation.current = e.target.value
}
setModuleSetupOption({ ...moduleSetupOption, setupLocation: e.target.value })
const handleMaxSetup = (e) => {
if (e.target.checked) {
setIsMaxSetup('true')
refs.isMaxSetup.current = 'true'
} else {
setIsMaxSetup('false')
refs.isMaxSetup.current = 'false'
}
//
setIsManualModuleSetup(false)
setIsManualModuleLayoutSetup(false)
setManualSetupMode('off')
}
//
const handleSelectedItem = (e) => {
const handleSelectedItem = (e, itemId) => {
setSelectedItems({ ...selectedItems, [e.target.name]: e.target.checked })
const newLayoutSetup = [...props.layoutSetup]
props.layoutSetup.forEach((item, index) => {
if (item.moduleId === itemId) {
newLayoutSetup[index] = { ...props.layoutSetup[index], checked: e.target.checked }
}
})
props.setLayoutSetup(newLayoutSetup)
}
const handleLayoutSetup = (e, itemId, index) => {
const newLayoutSetup = [...props.layoutSetup]
newLayoutSetup[index] = {
...newLayoutSetup[index],
moduleId: itemId,
[e.target.name]: Number(e.target.value),
}
props.setLayoutSetup(newLayoutSetup)
}
return (
<>
<div className="module-table-flex-wrap mb10">
<div className="module-table-flex-wrap">
<div className="module-table-box">
<div className="module-table-inner">
<div className="roof-module-table">
<table>
<thead>
<tr>
{moduleData.header.map((data) => (
<th key={data.prop} style={{ width: data.width ? data.width : '' }}>
{data.type === 'check' ? (
@ -132,11 +180,10 @@ const Placement = forwardRef((props, refs) => {
)}
</th>
))}
</tr>
</thead>
<tbody>
{selectedModules.itemList &&
selectedModules.itemList.map((item, index) => (
{selectedModules?.itemList &&
selectedModules?.itemList?.map((item, index) => (
<tr key={index}>
<td className="al-c">
<div className="d-check-box no-text pop">
@ -145,7 +192,7 @@ const Placement = forwardRef((props, refs) => {
id={item.itemId}
name={item.itemId}
checked={selectedItems[item.itemId]}
onChange={handleSelectedItem}
onChange={(e) => handleSelectedItem(e, item.itemId)}
/>
<label htmlFor={item.itemId}></label>
</div>
@ -156,7 +203,199 @@ const Placement = forwardRef((props, refs) => {
<span className="name">{item.itemNm}</span>
</div>
</td>
<td className="al-r">{item.wpOut}</td>
<td className="al-c">
<div className="color-wrap">
<span className="name">{item.mixAsgYn}</span>
</div>
</td>
<td className="al-r">
<div className="input-grid">
<input
type="text"
className="input-origin block"
name="row"
value={props.layoutSetup[index]?.row ?? 1}
defaultValue={0}
onChange={(e) => handleLayoutSetup(e, item.itemId, index)}
/>
</div>
</td>
<td className="al-r">
<div className="input-grid">
<input
type="text"
className="input-origin block"
name="col"
value={props.layoutSetup[index]?.col ?? 1}
defaultValue={0}
onChange={(e) => handleLayoutSetup(e, item.itemId, index)}
/>
</div>
</td>
</tr>
))}
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div className="module-table-box non-flex">
<div className="module-table-inner">
<div className="roof-module-table">
<table>
<thead>
<tr>
<th>{getMessage('modal.module.basic.setting.module.placement.waterfowl.arrangement')}</th>
<th>{getMessage('modal.module.basic.setting.module.placement.arrangement.standard')}</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<div className="hexagonal-radio-wrap">
<div className="d-check-radio pop mb10">
<input
type="radio"
name="radio02"
id="ra03"
checked={moduleSetupOption.isChidori}
disabled={isChidoriNotAble}
value={'true'}
onChange={(e) => handleChangeChidori(e)}
/>
<label htmlFor="ra03">{getMessage('modal.module.basic.setting.module.placement.do')}</label>
</div>
<div className="d-check-radio pop">
<input
type="radio"
name="radio02"
id="ra04"
checked={!moduleSetupOption.isChidori}
value={'false'}
onChange={(e) => handleChangeChidori(e)}
/>
<label htmlFor="ra04">{getMessage('modal.module.basic.setting.module.placement.do.not')}</label>
</div>
</div>
</td>
<td>
<div className="hexagonal-radio-wrap">
<div className="d-check-radio pop mb10">
<input
type="radio"
name="radio03"
id="ra05"
checked={moduleSetupOption.setupLocation === 'eaves'}
value={'eaves'}
onChange={handleSetupLocation}
/>
<label htmlFor="ra05">{getMessage('modal.module.basic.setting.module.placement.arrangement.standard.eaves')}</label>
</div>
<div className="d-check-radio pop">
<input
type="radio"
name="radio03"
id="ra06"
checked={moduleSetupOption.setupLocation === 'ridge'}
value={'ridge'}
onChange={handleSetupLocation}
disabled={isMultiModule}
/>
<label htmlFor="ra06">{getMessage('modal.module.basic.setting.module.placement.arrangement.standard.ridge')}</label>
</div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<div className="hide-tab-wrap">
<div className="hide-check-guide">
{getMessage('modal.module.basic.setting.module.placement.info')}
<button className={`arr ${useTab ? 'act' : ''}`} onClick={() => setUseTab(!useTab)}></button>
</div>
<div className={`hide-tab-contents ${!useTab ? 'hide' : ''}`}>
<div className="roof-content-tab-wrap">
<button className={`btn-frame block modal mr5 ${guideType === 'batch' ? 'act' : ''} `} onClick={() => setGuideType('batch')}>
{getMessage('modal.module.basic.setting.module.placement.info.batch')}
</button>
<button className={`btn-frame block modal mr5 ${guideType === 'module' ? 'act' : ''}`} onClick={() => setGuideType('module')}>
{getMessage('modal.module.basic.setting.module.placement.info.module')}
</button>
</div>
{guideType === 'batch' && (
<div className={`roof-warning-wrap mt10`}>
<div className="guide">
{getMessage('modal.module.basic.setting.module.placement.info.batch.content1')}
<br />
{getMessage('modal.module.basic.setting.module.placement.info.batch.content2')}
</div>
<div className="roof-warning-img-wrap">
<div className="roof-warning-img">
<Image src={'/static/images/canvas/roof_warning_correct.png'} width={350} height={198} alt="" />
</div>
<div className="roof-warning-img">
<Image src={'/static/images/canvas/roof_warning_wrong.png'} width={350} height={198} alt="" />
</div>
</div>
</div>
)}
{guideType === 'module' && (
<div className={`module-table-box mt10 ${!useTab ? 'hide' : ''}`}>
<div className="module-table-inner">
<div className="roof-module-table">
<table className="">
<thead>
<tr>
<th rowSpan={2} style={{ width: '22%' }}></th>
{selectedModules &&
selectedModules.itemList?.map((item) => (
// <th colSpan={colspan}>
<th>
<div className="color-wrap">
<span className="color-box" style={{ backgroundColor: item.color }}></span>
<span className="name">{item.itemNm}</span>
</div>
</th>
))}
{colspan > 1 && <th rowSpan={2}>{getMessage('modal.module.basic.setting.module.placement.max.rows.multiple')}</th>}
</tr>
<tr>
{selectedModules &&
selectedModules.itemList?.map((item) => (
<>
<th>{getMessage('modal.module.basic.setting.module.placement.max.row')}</th>
{/* {colspan > 1 && <th>{getMessage('modal.module.basic.setting.module.placement.max.rows.multiple')}</th>} */}
</>
))}
</tr>
</thead>
<tbody>
{moduleSelectionData.roofConstructions.map((item, index) => (
<tr>
<td>
<div className="color-wrap">
<span className="color-box" style={{ backgroundColor: roofOutlineColor(item.addRoof?.index) }}></span>
<span className="name">{item.addRoof?.roofMatlNmJp}</span>
</div>
</td>
{moduleRowColArray[index]?.map((item, index2) => (
<>
<td className="al-c">{item.moduleMaxRows}</td>
{/* {colspan > 1 && <td className="al-c">{item.mixModuleMaxRows}</td>} */}
{colspan > 1 && index2 === moduleRowColArray[index].length - 1 && <td className="al-c">{item.maxRow}</td>}
</>
))}
</tr>
))}
</tbody>
@ -164,83 +403,7 @@ const Placement = forwardRef((props, refs) => {
</div>
</div>
</div>
<div className="module-table-box">
<div className="module-table-inner">
<div className="self-table-tit">{getMessage('modal.module.basic.setting.module.placement.select.fitting.type')}</div>
<div className="module-self-table">
<div className="self-table-item">
<div className="self-item-th">{getMessage('modal.module.basic.setting.module.placement.waterfowl.arrangement')}</div>
<div className="self-item-td">
<div className="pop-form-radio">
<div className="d-check-radio pop">
<input
type="radio"
name="radio01"
id="ra01"
checked={isChidori}
disabled={isChidoriNotAble}
value={'true'}
onChange={(e) => handleChangeChidori(e)}
/>
<label htmlFor="ra01">{getMessage('modal.module.basic.setting.module.placement.do')}</label>
</div>
<div className="d-check-radio pop">
<input type="radio" name="radio02" id="ra02" checked={!isChidori} value={'false'} onChange={(e) => handleChangeChidori(e)} />
<label htmlFor="ra02">{getMessage('modal.module.basic.setting.module.placement.do.not')}</label>
</div>
</div>
</div>
</div>
<div className="self-table-item">
<div className="self-item-th">{getMessage('modal.module.basic.setting.module.placement.arrangement.standard')}</div>
<div className="self-item-td">
<div className="pop-form-radio">
<div className="d-check-radio pop">
<input
type="radio"
name="radio03"
id="ra03"
checked={setupLocation === 'center'}
value={'center'}
onChange={handleSetupLocation}
disabled={isMultiModule}
/>
<label htmlFor="ra03">{getMessage('modal.module.basic.setting.module.placement.arrangement.standard.center')}</label>
</div>
<div className="d-check-radio pop">
<input
type="radio"
name="radio04"
id="ra04"
checked={setupLocation === 'eaves'}
value={'eaves'}
onChange={handleSetupLocation}
/>
<label htmlFor="ra04">{getMessage('modal.module.basic.setting.module.placement.arrangement.standard.eaves')}</label>
</div>
<div className="d-check-radio pop">
<input
type="radio"
name="radio05"
id="ra05"
checked={setupLocation === 'ridge'}
value={'ridge'}
onChange={handleSetupLocation}
disabled={isMultiModule}
/>
<label htmlFor="ra05">{getMessage('modal.module.basic.setting.module.placement.arrangement.standard.ridge')}</label>
</div>
</div>
</div>
</div>
</div>
<div className="self-table-flx">
{/* <div className="d-check-box pop">
<input type="checkbox" id="ch04" checked={isMaxSetup === 'true'} value={'true'} onChange={handleMaxSetup} />
<label htmlFor="ch04">{getMessage('modal.module.basic.setting.module.placement.maximum')}</label>
</div> */}
</div>
</div>
)}
</div>
</div>
</>

View File

@ -0,0 +1,770 @@
import { GlobalDataContext } from '@/app/GlobalDataProvider'
import QSelectBox from '@/components/common/select/QSelectBox'
import { useModuleBasicSetting } from '@/hooks/module/useModuleBasicSetting'
import { useModuleTrestle } from '@/hooks/module/useModuleTrestle'
import { useMessage } from '@/hooks/useMessage'
import { currentAngleTypeSelector, pitchTextSelector } from '@/store/canvasAtom'
import { roofsState } from '@/store/roofAtom'
import { moduleSelectionDataState, selectedModuleState } from '@/store/selectedModuleOptions'
import { forwardRef, useContext, useEffect, useImperativeHandle, useRef, useState } from 'react'
import { useRecoilState, useRecoilValue } from 'recoil'
import Swal from 'sweetalert2'
const Trestle = forwardRef((props, ref) => {
const { tabNum, setTabNum, trestleTrigger, roofs, setRoofs, moduleSelectionData, setModuleSelectionData, setRoofsStore } = props
const { getMessage } = useMessage()
// const [selectedTrestle, setSelectedTrestle] = useState()
const currentAngleType = useRecoilValue(currentAngleTypeSelector)
const pitchText = useRecoilValue(pitchTextSelector)
const [selectedRoof, setSelectedRoof] = useState(null)
const {
trestleState,
trestleDetail,
dispatch,
raftBaseList,
trestleList,
constMthdList,
roofBaseList,
constructionList,
eavesMargin,
ridgeMargin,
kerabaMargin,
setEavesMargin,
setRidgeMargin,
setKerabaMargin,
lengthBase,
setLengthBase,
hajebichi,
setHajebichi,
cvrYn,
cvrChecked,
snowGdPossYn,
snowGdChecked,
setCvrYn,
setCvrChecked,
setSnowGdPossYn,
setSnowGdChecked,
} = useModuleTrestle({
selectedRoof,
})
const selectedModules = useRecoilValue(selectedModuleState) //
// const [moduleSelectionData, setModuleSelectionData] = useRecoilState(moduleSelectionDataState)
const [selectedRaftBase, setSelectedRaftBase] = useState(null)
const [selectedTrestle, setSelectedTrestle] = useState(null)
const [selectedConstMthd, setSelectedConstMthd] = useState(null)
const [selectedConstruction, setSelectedConstruction] = useState(null)
const [selectedRoofBase, setSelectedRoofBase] = useState(null)
const { managementState } = useContext(GlobalDataContext)
const { restoreModuleInstArea } = useModuleBasicSetting()
const [flag, setFlag] = useState(false)
const tempModuleSelectionData = useRef(null)
useEffect(() => {
if (roofs && !selectedRoof) {
setSelectedRoof(roofs[0])
}
//
restoreModuleInstArea()
}, [roofs])
useEffect(() => {
if (flag && moduleSelectionData) {
if (JSON.stringify(tempModuleSelectionData.current) === JSON.stringify(moduleSelectionData)) {
setTabNum(tabNum + 1)
}
}
}, [flag, moduleSelectionData])
useEffect(() => {
if (selectedRoof) {
if (moduleSelectionData?.roofConstructions?.length >= selectedRoof.index + 1) {
const { construction, trestle, trestleDetail } = moduleSelectionData?.roofConstructions[selectedRoof.index]
dispatch({
type: 'SET_INITIALIZE',
roof: { common: moduleSelectionData.common, module: moduleSelectionData.module, construction, trestle, trestleDetail, ...selectedRoof },
})
} else {
dispatch({ type: 'SET_INITIALIZE', roof: { ...selectedRoof, common: moduleSelectionData.common, module: moduleSelectionData.module } })
}
}
}, [selectedRoof])
useEffect(() => {
if (raftBaseList.length > 0) {
setSelectedRaftBase(raftBaseList.find((raft) => raft.clCode === selectedRoof?.raft) ?? null)
} else {
setSelectedRaftBase(null)
}
}, [raftBaseList])
useEffect(() => {
if (trestleList.length > 0) {
setSelectedTrestle(trestleList.find((trestle) => trestle.trestleMkrCd === trestleState?.trestleMkrCd) ?? null)
} else {
setSelectedTrestle(null)
}
}, [trestleList])
useEffect(() => {
if (roofBaseList.length > 0) {
setSelectedRoofBase(roofBaseList.find((roofBase) => roofBase.roofBaseCd === trestleState?.roofBaseCd) ?? null)
} else {
setSelectedRoofBase(null)
}
}, [roofBaseList])
useEffect(() => {
if (constMthdList.length > 0) {
setSelectedConstMthd(constMthdList.find((constMthd) => constMthd.constMthdCd === trestleState?.constMthdCd) ?? null)
} else {
setSelectedConstMthd(null)
}
}, [constMthdList])
useEffect(() => {
if (constructionList.length > 0) {
setSelectedConstruction(constructionList.find((construction) => construction.constTp === trestleState?.construction?.constTp) ?? null)
if (constructionList.filter((construction) => construction.constPossYn === 'Y').length === 0) {
Swal.fire({
title: getMessage('modal.module.basic.settting.module.error4', [selectedRoof?.nameJp]), // .
icon: 'warning',
})
}
} else {
setSelectedConstruction(null)
}
}, [constructionList])
const getConstructionState = (index) => {
if (constructionList && constructionList.length > 0) {
if (constructionList[index].constPossYn === 'Y') {
if (trestleState && trestleState.constTp === constructionList[index].constTp) {
return 'blue'
}
return 'white'
}
return 'no-click'
}
return 'no-click'
}
const onChangeLength = (e) => {
setLengthBase(e)
dispatch({
type: 'SET_LENGTH',
roof: {
length: e,
moduleTpCd: selectedModules.itemTp ?? '',
roofMatlCd: selectedRoof?.roofMatlCd ?? '',
raft: selectedRaftBase?.clCode,
},
})
}
const onChangeRaftBase = (e) => {
setSelectedRaftBase(e)
dispatch({
type: 'SET_RAFT_BASE',
roof: {
moduleTpCd: selectedModules.itemTp ?? '',
roofMatlCd: selectedRoof?.roofMatlCd ?? '',
raft: e.clCode,
},
})
}
const onChangeTrestleMaker = (e) => {
setSelectedTrestle(e)
dispatch({
type: 'SET_TRESTLE_MAKER',
roof: {
moduleTpCd: selectedModules.itemTp ?? '',
roofMatlCd: selectedRoof?.roofMatlCd ?? '',
raft: selectedRaftBase?.clCode,
trestleMkrCd: e.trestleMkrCd,
},
})
}
const onChangeConstMthd = (e) => {
setSelectedConstMthd(e)
dispatch({
type: 'SET_CONST_MTHD',
roof: {
moduleTpCd: selectedModules.itemTp ?? '',
roofMatlCd: selectedRoof?.roofMatlCd ?? '',
raft: selectedRaftBase?.clCode,
trestleMkrCd: selectedTrestle.trestleMkrCd,
constMthdCd: e.constMthdCd,
},
})
}
const onChangeRoofBase = (e) => {
setSelectedRoofBase(e)
dispatch({
type: 'SET_ROOF_BASE',
roof: {
moduleTpCd: selectedModules.itemTp ?? '',
roofMatlCd: selectedRoof?.roofMatlCd ?? '',
raft: selectedRaftBase?.clCode,
trestleMkrCd: selectedTrestle.trestleMkrCd,
constMthdCd: selectedConstMthd.constMthdCd,
roofBaseCd: e.roofBaseCd,
illuminationTp: managementState?.surfaceTypeValue ?? '',
instHt: managementState?.installHeight ?? '',
stdWindSpeed: managementState?.standardWindSpeedId ?? '',
stdSnowLd: managementState?.verticalSnowCover ?? '',
inclCd: selectedRoof?.pitch ?? 0,
roofPitch: Math.round(hajebichi ?? 0),
},
})
}
const handleConstruction = (index) => {
if (constructionList[index]?.constPossYn === 'Y') {
dispatch({
type: 'SET_CONSTRUCTION',
roof: {
moduleTpCd: selectedModules.itemTp ?? '',
roofMatlCd: selectedRoof?.roofMatlCd ?? '',
raft: selectedRaftBase?.clCode,
trestleMkrCd: selectedTrestle.trestleMkrCd,
constMthdCd: selectedConstMthd.constMthdCd,
roofBaseCd: selectedRoofBase.roofBaseCd,
illuminationTp: managementState?.surfaceTypeValue ?? '',
instHt: managementState?.installHeight ?? '',
stdWindSpeed: managementState?.standardWindSpeedId ?? '',
stdSnowLd: managementState?.verticalSnowCover ?? '',
inclCd: selectedRoof?.pitch ?? 0,
roofPitch: Math.round(hajebichi ?? 0),
constTp: constructionList[index].constTp,
snowGdPossYn: constructionList[index].snowGdPossYn,
cvrYn: constructionList[index].cvrYn,
mixMatlNo: selectedModules.mixMatlNo,
// workingWidth: selectedRoof?.length?.toString() ?? '',
workingWidth: lengthBase,
},
})
setCvrYn(constructionList[index].cvrYn)
setSnowGdPossYn(constructionList[index].snowGdPossYn)
setCvrChecked(false)
setSnowGdChecked(false)
}
}
const handleChangeRoofMaterial = (index) => {
const newAddedRoofs = roofs.map((roof, i) => {
if (i === selectedRoof.index) {
return {
...selectedRoof,
hajebichi,
length: lengthBase,
eavesMargin,
ridgeMargin,
kerabaMargin,
roofIndex: selectedRoof.index,
raft: selectedRaftBase?.clCode,
trestle: {
hajebichi: hajebichi,
length: lengthBase,
...selectedRaftBase,
...selectedTrestle,
...selectedConstMthd,
...selectedRoofBase,
},
construction: {
...constructionList.find((data) => data.constTp === trestleState.constTp),
cvrYn: cvrYn,
snowGdPossYn: snowGdPossYn,
cvrChecked: cvrChecked,
snowGdChecked: snowGdChecked,
setupCover: cvrChecked ?? false,
setupSnowCover: snowGdChecked ?? false,
},
trestleDetail: trestleDetail,
}
}
return roof
})
setRoofs(newAddedRoofs)
setSelectedRoof(newAddedRoofs[index])
}
const isComplete = async () => {
const newAddedRoofs = roofs.map((roof, i) => {
if (i === selectedRoof?.index) {
return {
...selectedRoof,
length: lengthBase,
eavesMargin,
ridgeMargin,
kerabaMargin,
roofIndex: roof.index,
raft: selectedRaftBase?.clCode,
hajebichi: hajebichi,
trestle: {
length: lengthBase,
hajebichi: hajebichi,
...selectedRaftBase,
...selectedTrestle,
...selectedConstMthd,
...selectedRoofBase,
},
construction: {
...constructionList.find((data) => data.constTp === trestleState.constTp),
cvrYn,
snowGdPossYn,
cvrChecked,
snowGdChecked,
setupCover: cvrChecked ?? false,
setupSnowCover: snowGdChecked ?? false,
},
trestleDetail: trestleDetail,
}
}
return roof
})
let result = true
for (let i = 0; i < newAddedRoofs.length; i++) {
const roof = newAddedRoofs[i]
if (!roof.trestle?.trestleMkrCd) {
Swal.fire({
title: getMessage('modal.module.basic.settting.module.error1', [roof.nameJp]), // .
icon: 'warning',
})
result = false
return false
}
if (!roof.trestle?.constMthdCd) {
Swal.fire({
title: getMessage('modal.module.basic.settting.module.error2', [roof.nameJp]), // .
icon: 'warning',
})
result = false
return false
}
if (!roof.trestle?.roofBaseCd) {
Swal.fire({
title: getMessage('modal.module.basic.settting.module.error3', [roof.nameJp]), // .
icon: 'warning',
})
result = false
return false
}
if (!roof.construction?.constTp) {
Swal.fire({
title: getMessage('modal.module.basic.settting.module.error12', [roof.nameJp]), // .
icon: 'warning',
})
result = false
return false
}
if (roof.lenAuth === 'C') {
if (!roof.trestle?.length) {
Swal.fire({
title: getMessage('modal.module.basic.settting.module.error5', [roof.nameJp]), // L .
icon: 'warning',
})
result = false
return false
}
}
if (['C', 'R'].includes(roof.raftAuth)) {
if (!roof?.raft) {
Swal.fire({
title: getMessage('modal.module.basic.settting.module.error6', [roof.nameJp]), // .
icon: 'warning',
})
result = false
return false
}
}
if (['C', 'R'].includes(roof.roofPchAuth)) {
if (!roof?.roofPchBase) {
Swal.fire({
title: getMessage('modal.module.basic.settting.module.error7', [roof.nameJp]), // .
icon: 'warning',
})
result = false
return false
}
}
if (!roof?.eavesMargin || !roof?.ridgeMargin || !roof?.kerabaMargin) {
Swal.fire({
title: getMessage('modal.module.basic.settting.module.error8', [roof.nameJp]), // .
icon: 'warning',
})
result = false
return false
}
if (roof.trestle.trestleMkrCd !== 'NO_DATA') {
//
if (roof.trestleDetail?.eaveIntvl > roof.eavesMargin) {
Swal.fire({
title: getMessage('modal.module.basic.settting.module.error9', [roof.trestleDetail?.eaveIntvl, roof.nameJp]), // {0}mm .
icon: 'warning',
})
result = false
return false
}
if (roof.trestleDetail?.ridgeIntvl > roof.ridgeMargin) {
Swal.fire({
title: getMessage('modal.module.basic.settting.module.error10', [roof.trestleDetail?.ridgeIntvl, roof.nameJp]), // {0}mm .
icon: 'warning',
})
result = false
return false
}
if (roof.trestleDetail?.kerabaIntvl > roof.kerabaMargin) {
Swal.fire({
title: getMessage('modal.module.basic.settting.module.error11', [roof.trestleDetail?.kerabaIntvl, roof.nameJp]), // {0}mm .
icon: 'warning',
})
result = false
return false
}
}
}
if (result) {
const newRoofs = newAddedRoofs.map((roof) => {
const { addRoof, construction, trestle, trestleDetail, roofConstructions, ...rest } = roof
return rest
})
setModuleSelectionData({
...moduleSelectionData,
roofConstructions: newAddedRoofs.map((roof, index) => ({
roofIndex: newRoofs[index].index,
trestle: roof.trestle,
addRoof: newRoofs[index],
construction: roof.construction,
trestleDetail: roof.trestleDetail,
})),
})
setFlag(true)
tempModuleSelectionData.current = {
...moduleSelectionData,
roofConstructions: newAddedRoofs.map((roof, index) => ({
roofIndex: newRoofs[index].index,
trestle: roof.trestle,
addRoof: newRoofs[index],
construction: roof.construction,
trestleDetail: roof.trestleDetail,
})),
}
const updatePromises = [
// new Promise((resolve) => {
// resolve()
// }),
new Promise((resolve) => {
setRoofs(newRoofs)
resolve()
}),
new Promise((resolve) => {
const roofConstructions = newAddedRoofs.map((roof, index) => ({
roofIndex: newRoofs[index].index,
addRoof: newRoofs[index],
trestle: {
...roof.trestle,
raft: roof.raftBaseCd,
},
construction: {
// ...constructionList.find((construction) => newAddedRoofs[index].construction.constTp === construction.constTp),
...roof.construction,
roofIndex: roof.index,
selectedIndex: roof.index,
},
trestleDetail: roof.trestleDetail,
}))
trestleTrigger({
roofConstructions,
})
setRoofsStore(roofConstructions)
resolve()
}),
]
await Promise.all(updatePromises)
return true
}
return false
}
useImperativeHandle(ref, () => ({
isComplete,
}))
return (
<div className="roof-module-tab2-overflow">
<div className="module-table-box mb10">
<div className="module-box-tab">
{roofs &&
roofs.map((roof, index) => (
<button
key={index}
className={`module-btn ${selectedRoof?.index === index ? 'act' : ''}`}
onClick={() => (roof ? handleChangeRoofMaterial(index) : null)}
>
{roof !== undefined ? `${roof.nameJp} (${currentAngleType === 'slope' ? roof.pitch : roof.angle}${pitchText})` : '-'}
</button>
))}
</div>
<div className="module-table-inner">
<div className="module-table-flex-wrap tab2">
<div className="module-flex-item">
<div className="eaves-keraba-table">
{selectedRoof && selectedRoof.lenAuth === 'C' && (
<>
<div className="eaves-keraba-item">
<div className="eaves-keraba-th">L</div>
<div className="eaves-keraba-td">
<div className="grid-select">
<input
type="text"
className="input-origin block"
value={lengthBase}
onChange={(e) => onChangeLength(e.target.value)}
disabled={selectedRoof.lenAuth === 'R'}
/>
</div>
</div>
</div>
</>
)}
{selectedRoof && ['C', 'R'].includes(selectedRoof.raftAuth) && (
<>
<div className="eaves-keraba-item">
<div className="eaves-keraba-th">{getMessage('modal.module.basic.setting.module.rafter.margin')}</div>
<div className="eaves-keraba-td">
<div className="grid-select">
{raftBaseList.length > 0 && (
<QSelectBox
options={raftBaseList}
value={selectedRaftBase}
sourceKey={'clCode'}
targetKey={'clCode'}
showKey={'clCodeNm'}
disabled={selectedRoof.raftAuth === 'R'}
onChange={(e) => onChangeRaftBase(e)}
/>
)}
</div>
</div>
</div>
</>
)}
{selectedRoof && ['C', 'R'].includes(selectedRoof.roofPchAuth) && (
<>
<div className="eaves-keraba-item">
<div className="eaves-keraba-th">{getMessage('modal.module.basic.setting.module.hajebichi')}</div>
<div className="eaves-keraba-td">
<div className="grid-select">
<input
type="text"
className="input-origin block"
disabled={selectedRoof.roofPchAuth === 'R'}
onChange={(e) => setHajebichi(e.target.value)}
value={hajebichi}
/>
</div>
</div>
</div>
</>
)}
<div className="eaves-keraba-item">
<div className="eaves-keraba-th">{getMessage('modal.module.basic.setting.module.trestle.maker')}</div>
<div className="eaves-keraba-td">
<div className="grid-select">
{trestleList && (
<QSelectBox
title={getMessage('selectbox.title')}
options={trestleList}
value={selectedTrestle}
sourceKey={'trestleMkrCd'}
targetKey={'trestleMkrCd'}
showKey={'trestleMkrCdJp'}
onChange={(e) => onChangeTrestleMaker(e)}
/>
)}
</div>
</div>
</div>
<div className="eaves-keraba-item">
<div className="eaves-keraba-th">{getMessage('modal.module.basic.setting.module.construction.method')}</div>
<div className="eaves-keraba-td">
<div className="grid-select">
{constMthdList && (
<QSelectBox
title={getMessage('selectbox.title')}
options={constMthdList}
value={selectedConstMthd}
sourceKey={'constMthdCd'}
targetKey={'constMthdCd'}
showKey={'constMthdCdJp'}
onChange={(e) => onChangeConstMthd(e)}
/>
)}
</div>
</div>
</div>
<div className="eaves-keraba-item">
<div className="eaves-keraba-th">{getMessage('modal.module.basic.setting.module.under.roof')}</div>
<div className="eaves-keraba-td">
<div className="grid-select">
{roofBaseList && (
<QSelectBox
title={getMessage('selectbox.title')}
options={roofBaseList}
sourceKey={'roofBaseCd'}
targetKey={'roofBaseCd'}
showKey={'roofBaseCdJp'}
value={selectedRoofBase}
onChange={(e) => onChangeRoofBase(e)}
/>
)}
</div>
</div>
</div>
</div>
</div>
<div className="module-flex-item non-flex">
<div className="flex-item-btn-wrap">
<button className={`btn-frame roof ${getConstructionState(0)}`} onClick={() => handleConstruction(0)}>
{getMessage('modal.module.basic.setting.module.standard.construction')}(I)
</button>
<button className={`btn-frame roof ${getConstructionState(3)}`} onClick={() => handleConstruction(3)}>
{getMessage('modal.module.basic.setting.module.multiple.construction')}
</button>
<button className={`btn-frame roof ${getConstructionState(1)}`} onClick={() => handleConstruction(1)}>
{getMessage('modal.module.basic.setting.module.standard.construction')}
</button>
<button className={`btn-frame roof ${getConstructionState(4)}`} onClick={() => handleConstruction(4)}>
{getMessage('modal.module.basic.setting.module.multiple.construction')}(II)
</button>
<button className={`btn-frame roof ${getConstructionState(2)}`} onClick={() => handleConstruction(2)}>
{getMessage('modal.module.basic.setting.module.enforce.construction')}
</button>
</div>
<div className="grid-check-form-flex">
<div className="d-check-box pop">
<input
type="checkbox"
id={`ch01`}
disabled={!cvrYn || cvrYn === 'N'}
checked={cvrChecked || false}
// onChange={() => dispatch({ type: 'SET_TRESTLE_DETAIL', roof: { ...trestleState, cvrChecked: !trestleState.cvrChecked } })}
onChange={() => setCvrChecked(!cvrChecked)}
/>
<label htmlFor={`ch01`}>{getMessage('modal.module.basic.setting.module.eaves.bar.fitting')}</label>
</div>
<div className="d-check-box pop">
<input
type="checkbox"
id={`ch02`}
disabled={!snowGdPossYn || snowGdPossYn === 'N'}
checked={snowGdChecked || false}
// onChange={() => dispatch({ type: 'SET_TRESTLE_DETAIL', roof: { ...trestleState, snowGdChecked: !trestleState.snowGdChecked } })}
onChange={() => setSnowGdChecked(!snowGdChecked)}
/>
<label htmlFor={`ch02`}>{getMessage('modal.module.basic.setting.module.blind.metal.fitting')}</label>
</div>
</div>
</div>
</div>
<div className="module-input-area">
<div className="module-area-title">{getMessage('modal.module.basic.setting.module.placement.area')}</div>
<div className="module-input-wrap">
<div className="outline-form mr15">
<span>{getMessage('modal.module.basic.setting.module.placement.area.eaves')}</span>
<div className="input-grid mr10">
<input
type="number"
className="input-origin block"
value={eavesMargin ?? 0}
// onChange={(e) => dispatch({ type: 'SET_TRESTLE_DETAIL', roof: { ...trestleState, eavesMargin: e.target.value } })}
onChange={(e) => setEavesMargin(+e.target.value)}
/>
</div>
<span className="thin">mm</span>
</div>
<div className="outline-form mr15">
<span>{getMessage('modal.module.basic.setting.module.placement.area.ridge')}</span>
<div className="input-grid mr10">
<input
type="number"
className="input-origin block"
value={ridgeMargin ?? 0}
// onChange={(e) => dispatch({ type: 'SET_TRESTLE_DETAIL', roof: { ...trestleState, ridgeMargin: e.target.value } })}
onChange={(e) => setRidgeMargin(+e.target.value)}
/>
</div>
<span className="thin">mm</span>
</div>
<div className="outline-form ">
<span>{getMessage('modal.module.basic.setting.module.placement.area.keraba')}</span>
<div className="input-grid mr10">
<input
type="number"
className="input-origin block"
value={kerabaMargin ?? 0}
// onChange={(e) => dispatch({ type: 'SET_TRESTLE_DETAIL', roof: { ...trestleState, kerabaMargin: e.target.value } })}
onChange={(e) => setKerabaMargin(+e.target.value)}
/>
</div>
<span className="thin">mm</span>
</div>
</div>
</div>
<div className="module-input-area">
<div className="module-area-title">{getMessage('modal.module.basic.setting.module.placement.margin')}</div>
<div className="module-input-wrap">
<div className="outline-form">
<span>{getMessage('modal.module.basic.setting.module.placement.margin.horizontal')}</span>
<div className="input-grid mr10">
<input type="text" className="input-origin block" defaultValue={trestleDetail?.moduleIntvlHor} readOnly />
</div>
<span className="thin">mm</span>
</div>
<div className="outline-form">
<span>{getMessage('modal.module.basic.setting.module.placement.margin.vertical')}</span>
<div className="input-grid mr10">
<input type="text" className="input-origin block" defaultValue={trestleDetail?.moduleIntvlVer} readOnly />
</div>
<span className="thin">mm</span>
</div>
</div>
</div>
</div>
</div>
<div className="module-bottom">
<div className="module-table-box ">
<div className="warning-guide">
<div className="warning">
{getMessage('modal.module.basic.setting.module.setting.info1')}
<br />
{getMessage('modal.module.basic.setting.module.setting.info2')}
</div>
</div>
</div>
</div>
</div>
)
})
export default Trestle

View File

@ -33,14 +33,6 @@ const PitchPlacement = forwardRef((props, refs) => {
setSelectedItems({ ...selectedItems, [e.target.name]: e.target.checked })
}
const moduleData = {
header: [
{ type: 'check', name: '', prop: 'check', width: 70 },
{ type: 'color-box', name: getMessage('module'), prop: 'module' },
{ type: 'text', name: `${getMessage('output')} (W)`, prop: 'output', width: 70 },
],
}
//
useEffect(() => {
const checkedModuleIds = Object.keys(selectedItems).filter((key) => selectedItems[key])
@ -105,24 +97,20 @@ const PitchPlacement = forwardRef((props, refs) => {
return (
<>
<div className="module-table-box mb10">
<div className="hexagonal-flex-wrap">
<div className="module-table-box ">
<div className="module-table-inner">
<div className="roof-module-table">
<table>
<thead>
<tr>
{moduleData.header.map((data) => (
<th key={data.prop} style={{ width: data.width ? data.width : '' }}>
{data.type === 'check' ? (
<th style={{ width: '70px' }}>
<div className="d-check-box no-text pop">
<input type="checkbox" id="ch01" disabled />
<label htmlFor="ch01"></label>
</div>
) : (
data.name
)}
</th>
))}
<th>{getMessage('module')}</th>
</tr>
</thead>
<tbody>
@ -147,7 +135,6 @@ const PitchPlacement = forwardRef((props, refs) => {
<span className="name">{item.itemNm}</span>
</div>
</td>
<td className="al-r">{item.wpOut}</td>
</tr>
))}
</tbody>
@ -155,15 +142,20 @@ const PitchPlacement = forwardRef((props, refs) => {
</div>
</div>
</div>
<div className="module-table-box mb10">
<div className="module-table-box non-flex">
<div className="module-table-inner">
<div className="hexagonal-wrap">
<div className="hexagonal-item">
<div className="bold-font">{getMessage('modal.module.basic.setting.pitch.module.placement.standard.setting')}</div>
</div>
<div className="hexagonal-item">
<div className="pop-form-radio">
<div className="d-check-radio pop">
<div className="roof-module-table">
<table>
<thead>
<tr>
<th>{getMessage('modal.module.basic.setting.pitch.module.placement.standard.setting')}</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<div className="hexagonal-radio-wrap">
<div className="d-check-radio pop mb10">
<input
type="radio"
name="radio01"
@ -186,6 +178,10 @@ const PitchPlacement = forwardRef((props, refs) => {
<label htmlFor="ra02">{getMessage('modal.module.basic.setting.pitch.module.placement.standard.setting.select')}</label>
</div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>

View File

@ -13,7 +13,7 @@ import { useRecoilState } from 'recoil'
import { makersState, modelsState, modelState, pcsCheckState, selectedMakerState, selectedModelsState, seriesState } from '@/store/circuitTrestleAtom'
import { POLYGON_TYPE } from '@/common/common'
import { useSwal } from '@/hooks/useSwal'
import { canvasState } from '@/store/canvasAtom'
import { canvasState, canvasZoomState } from '@/store/canvasAtom'
import { useTrestle } from '@/hooks/module/useTrestle'
import { selectedModuleState } from '@/store/selectedModuleOptions'
@ -25,6 +25,8 @@ import { useCanvasPopupStatusController } from '@/hooks/common/useCanvasPopupSta
import { useImgLoader } from '@/hooks/floorPlan/useImgLoader'
import { usePlan } from '@/hooks/usePlan'
import { QcastContext } from '@/app/QcastProvider'
import { fabric } from 'fabric'
import { fontSelector } from '@/store/fontAtom'
const ALLOCATION_TYPE = {
AUTO: 'auto',
@ -37,13 +39,16 @@ export default function CircuitTrestleSetting({ id }) {
const { swalFire } = useSwal()
const { saveEstimate } = useEstimate()
const canvas = useRecoilValue(canvasState)
const [canvasZoom, setCanvasZoom] = useRecoilState(canvasZoomState)
const [tabNum, setTabNum] = useState(1)
const [allocationType, setAllocationType] = useState(ALLOCATION_TYPE.AUTO)
const [circuitAllocationType, setCircuitAllocationType] = useState(1)
const { managementState, setManagementState, managementStateLoaded } = useContext(GlobalDataContext)
const { managementState, setManagementState } = useContext(GlobalDataContext)
const selectedModules = useRecoilValue(selectedModuleState)
const { getPcsAutoRecommendList, getPcsVoltageChk, getPcsVoltageStepUpList, getPcsManualConfChk } = useMasterController()
const flowText = useRecoilValue(fontSelector('flowText'))
const lengthText = useRecoilValue(fontSelector('lengthText'))
const circuitNumberText = useRecoilValue(fontSelector('circuitNumberText'))
// ()
const [selectedStepUpValues, setSelectedStepUpValues] = useState({})
@ -84,7 +89,6 @@ export default function CircuitTrestleSetting({ id }) {
// const { trigger: moduleSelectedDataTrigger } = useCanvasPopupStatusController(2)
useEffect(() => {
if (!managementState) {
setManagementState(managementStateLoaded)
}
// setCircuitData({
// makers,
@ -103,6 +107,29 @@ export default function CircuitTrestleSetting({ id }) {
}
}, [])
//
const beforeCapture = () => {
// setCanvasZoom(100)
const x = canvas.width / 2
const y = canvas.height / 2
canvas.zoomToPoint(new fabric.Point(x, y), 0.5)
changeFontSize('lengthText', '28')
changeFontSize('circuitNumber', '28')
changeFontSize('flowText', '28')
canvas.renderAll()
}
//
const afterCapture = () => {
setCanvasZoom(100)
canvas.set({ zoom: 1 })
canvas.viewportTransform = [1, 0, 0, 1, 0, 0]
changeFontSize('lengthText', lengthText.fontSize.value)
changeFontSize('circuitNumber', circuitNumberText.fontSize.value)
changeFontSize('flowText', flowText.fontSize.value)
canvas.renderAll()
}
//
// PCS
@ -155,6 +182,7 @@ export default function CircuitTrestleSetting({ id }) {
getPcsVoltageChk(pcsVoltageChkParams).then((res) => {
if (res.resultCode === 'S') {
setTabNum(2)
setAllModuleSurfaceIsComplete(false)
} else {
swalFire({
title: res.resultMsg,
@ -188,6 +216,7 @@ export default function CircuitTrestleSetting({ id }) {
}).then((res) => {
if (res?.result.resultCode === 'S' && res?.data) {
setTabNum(2)
setAllModuleSurfaceIsComplete(false)
} else {
swalFire({ text: getMessage('common.message.send.error') })
}
@ -287,6 +316,8 @@ export default function CircuitTrestleSetting({ id }) {
setSelectedModels(pcsItemList)
getPcsVoltageChk(pcsVoltageChkParams).then((res) => {
setAllocationType(ALLOCATION_TYPE.PASSIVITY)
setAllModuleSurfaceIsComplete(false)
clearTrestle()
})
} else {
swalFire({
@ -319,6 +350,7 @@ export default function CircuitTrestleSetting({ id }) {
}
setAllocationType(ALLOCATION_TYPE.PASSIVITY)
clearTrestle()
}
}
@ -339,6 +371,7 @@ export default function CircuitTrestleSetting({ id }) {
// ()
const onApply = async () => {
beforeCapture()
setAllModuleSurfaceIsComplete(false)
setIsGlobalLoading(true)
@ -349,12 +382,9 @@ export default function CircuitTrestleSetting({ id }) {
obj.pcses = getStepUpListData()
})
setViewCircuitNumberTexts(false)
handleCanvasToPng(1)
// result=null
setViewCircuitNumberTexts(true)
//
//
@ -370,6 +400,7 @@ export default function CircuitTrestleSetting({ id }) {
if (result) {
handleCanvasToPng(2)
afterCapture()
//
await saveEstimate(result)
} else {
@ -379,6 +410,16 @@ export default function CircuitTrestleSetting({ id }) {
// removeNotAllocationModules()
}
const changeFontSize = (name, size) => {
const textObjs = canvas?.getObjects().filter((obj) => obj.name === name)
textObjs.forEach((obj) => {
obj.set({
fontSize: size,
})
})
canvas.renderAll()
}
//
const onClickPrev = () => {
// setAllocationType(ALLOCATION_TYPE.AUTO)
@ -479,7 +520,7 @@ export default function CircuitTrestleSetting({ id }) {
console.log(stepUpListData)
stepUpListData[0].pcsItemList.map((item, index) => {
return item.serQtyList
.filter((serQty) => serQty.selected)
.filter((serQty) => serQty.selected && serQty.paralQty > 0)
.forEach((serQty) => {
pcs.push({
pcsMkrCd: item.pcsMkrCd,
@ -655,6 +696,7 @@ export default function CircuitTrestleSetting({ id }) {
return
} else {
setTabNum(2)
setAllModuleSurfaceIsComplete(false)
}
})
}

View File

@ -44,7 +44,7 @@ export default function PowerConditionalSelect(props) {
const { getPcsMakerList, getPcsModelList } = useMasterController()
const selectedModules = useRecoilValue(selectedModuleState)
const { swalFire } = useSwal()
const { trigger: moduleSelectedDataTrigger } = useCanvasPopupStatusController(2)
// const { trigger: moduleSelectedDataTrigger } = useCanvasPopupStatusController(2)
const [moduleSelectionData, setModuleSelectionData] = useRecoilState(moduleSelectionDataState)
const modelHeader = [
{ name: getMessage('modal.circuit.trestle.setting.circuit.allocation.passivity.series'), width: '15%', prop: 'pcsSerNm', type: 'color-box' },
@ -110,6 +110,7 @@ export default function PowerConditionalSelect(props) {
selected: s.pcsSerCd === data.pcsSerCd ? !s.selected : false,
}
})
setSelectedModels([])
}
setSeries(copySeries)
handleSetmodels(copySeries.filter((s) => s.selected))

View File

@ -42,7 +42,6 @@ export default function StepUp(props) {
const [arrayLength, setArrayLength] = useState(3) //module-table-inner
const [pcsCheck, setPcsCheck] = useRecoilState(pcsCheckState)
const { getPcsVoltageStepUpList, getPcsAutoRecommendList, getPcsVoltageChk, getPcsConnOptionItemList } = useMasterController()
const { managementState, setManagementState, managementStateLoaded } = useContext(GlobalDataContext)
const canvas = useRecoilValue(canvasState)
const selectedModules = useRecoilValue(selectedModuleState)
const [optCodes, setOptCodes] = useState([])
@ -573,7 +572,7 @@ export default function StepUp(props) {
value={seletedMainOption}
sourceKey="code"
targetKey="code"
showKey="name"
showKey={`${globalLocale === 'ja' ? 'nameJp' : 'name'}`}
onChange={(e) => setSeletedMainOption(e)}
/>
)}
@ -586,7 +585,7 @@ export default function StepUp(props) {
value={seletedSubOption}
sourceKey="code"
targetKey="code"
showKey="name"
showKey={`${globalLocale === 'ja' ? 'nameJp' : 'name'}`}
onChange={(e) => setSeletedSubOption(e)}
/>
)}

View File

@ -25,7 +25,7 @@ export default function PassivityCircuitAllocation(props) {
const { swalFire } = useSwal()
const { getMessage } = useMessage()
const canvas = useRecoilValue(canvasState)
const { managementState, setManagementState, managementStateLoaded } = useContext(GlobalDataContext)
const { managementState } = useContext(GlobalDataContext)
const selectedModules = useRecoilValue(selectedModuleState)
const [selectedPcs, setSelectedPcs] = useState(selectedModels[0])
const { header, rows, footer } = useRecoilValue(moduleStatisticsState)
@ -38,7 +38,6 @@ export default function PassivityCircuitAllocation(props) {
useEffect(() => {
setModuleStatisticsData()
if (!managementState) {
setManagementState(managementStateLoaded)
}
canvas
.getObjects()
@ -87,6 +86,26 @@ export default function PassivityCircuitAllocation(props) {
.map((obj) => obj.circuitNumber),
),
]
const surfaceList = targetModules.map((module) => {
return canvas.getObjects().filter((obj) => obj.id === canvas.getObjects().filter((obj) => obj.id === module)[0].surfaceId)[0]
})
if (surfaceList.length > 1) {
let surfaceType = {}
surfaceList.forEach((surface) => {
surfaceType[`${surface.direction}-${surface.roofMaterial.pitch}`] = surface
})
if (Object.keys(surfaceType).length > 1) {
swalFire({
text: getMessage('module.circuit.fix.not.same.roof.error'),
type: 'alert',
icon: 'warning',
})
return
}
}
if (!circuitNumber || circuitNumber === 0) {
swalFire({
text: getMessage('module.circuit.minimun.error'),

View File

@ -7,6 +7,7 @@ import { useMessage } from '@/hooks/useMessage'
import { usePopup } from '@/hooks/usePopup'
import { canvasState } from '@/store/canvasAtom'
import { usePolygon } from '@/hooks/usePolygon'
import { useSurfaceShapeBatch } from '@/hooks/surface/useSurfaceShapeBatch'
const FLOW_DIRECTION_TYPE = {
EIGHT_AZIMUTH: 'eightAzimuth',
@ -19,6 +20,8 @@ export default function FlowDirectionSetting(props) {
const canvas = useRecoilValue(canvasState)
const { getMessage } = useMessage()
const { changeSurfaceLineType } = useSurfaceShapeBatch({})
useEffect(() => {
return () => {
canvas?.discardActiveObject()
@ -53,6 +56,7 @@ export default function FlowDirectionSetting(props) {
})
drawDirectionArrow(roof)
canvas?.renderAll()
changeSurfaceLineType(roof)
closePopup(id)
}

View File

@ -4,9 +4,11 @@ import { usePopup } from '@/hooks/usePopup'
import { useRecoilValue } from 'recoil'
import { contextPopupPositionState } from '@/store/popupAtom'
import { useState } from 'react'
import { currentObjectState } from '@/store/canvasAtom'
import { canvasState, currentObjectState } from '@/store/canvasAtom'
import { useGrid } from '@/hooks/common/useGrid'
import { gridColorState } from '@/store/gridAtom'
import { gridDisplaySelector } from '@/store/settingAtom'
const GRID_PADDING = 5
export default function GridCopy(props) {
const contextPopupPosition = useRecoilValue(contextPopupPositionState)
const { id, pos = contextPopupPosition } = props
@ -15,9 +17,39 @@ export default function GridCopy(props) {
const [length, setLength] = useState('0')
const [arrow, setArrow] = useState(null)
const currentObject = useRecoilValue(currentObjectState)
const { copy } = useGrid()
const canvas = useRecoilValue(canvasState)
const gridColor = useRecoilValue(gridColorState)
const isGridDisplay = useRecoilValue(gridDisplaySelector)
const handleApply = () => {
copy(currentObject, ['↑', '←'].includes(arrow) ? +length * -1 : +length)
copy(currentObject, ['↑', '←'].includes(arrow) ? (+length * -1) / 10 : +length / 10)
}
const copy = (object, length) => {
const lineStartX = object.direction === 'vertical' ? object.x1 + length : 0
const lineEndX = object.direction === 'vertical' ? object.x2 + length : canvas.width
const lineStartY = object.direction === 'vertical' ? 0 : object.y1 + length
const lineEndY = object.direction === 'vertical' ? canvas.width : object.y1 + length
const line = new fabric.Line([lineStartX, lineStartY, lineEndX, lineEndY], {
stroke: gridColor,
strokeWidth: 1,
selectable: true,
lockMovementX: true,
lockMovementY: true,
lockRotation: true,
lockScalingX: true,
lockScalingY: true,
strokeDashArray: [5, 2],
opacity: 0.3,
padding: GRID_PADDING,
direction: object.direction,
visible: isGridDisplay,
name: object.name,
})
canvas.add(line)
canvas.setActiveObject(line)
canvas.renderAll()
}
return (
<WithDraggable isShow={true} pos={pos} className="xm">

View File

@ -6,7 +6,6 @@ import { contextPopupPositionState } from '@/store/popupAtom'
import { useCanvas } from '@/hooks/useCanvas'
import { canvasState, currentObjectState } from '@/store/canvasAtom'
import { useEffect, useState } from 'react'
import { useGrid } from '@/hooks/common/useGrid'
import { useSwal } from '@/hooks/useSwal'
import { set } from 'react-hook-form'
@ -17,7 +16,6 @@ export default function GridMove(props) {
const { getMessage } = useMessage()
const { closePopup } = usePopup()
const { swalFire } = useSwal()
const { move } = useGrid()
const [currentObject, setCurrentObject] = useRecoilState(currentObjectState)
const [isAll, setIsAll] = useState(false)
const [verticalSize, setVerticalSize] = useState('0')
@ -54,21 +52,31 @@ export default function GridMove(props) {
.forEach((grid) => {
move(
grid,
arrow2 === '←' ? Number(horizonSize) * -1 : Number(horizonSize),
arrow1 === '↑' ? Number(verticalSize) * -1 : Number(verticalSize),
arrow2 === '←' ? (Number(horizonSize) * -1) / 10 : Number(horizonSize) / 10,
arrow1 === '↑' ? (Number(verticalSize) * -1) / 10 : Number(verticalSize) / 10,
)
})
} else {
move(
currentObject,
arrow2 === '←' ? Number(horizonSize) * -1 : Number(horizonSize),
arrow1 === '↑' ? Number(verticalSize) * -1 : Number(verticalSize),
arrow2 === '←' ? (Number(horizonSize) * -1) / 10 : Number(horizonSize) / 10,
arrow1 === '↑' ? (Number(verticalSize) * -1) / 10 : Number(verticalSize) / 10,
)
}
canvas.renderAll()
handleClose()
}
const move = (object, x, y) => {
object.set({
...object,
x1: object.direction === 'vertical' ? object.x1 + x : 0,
x2: object.direction === 'vertical' ? object.x1 + x : canvas.width,
y1: object.direction === 'vertical' ? 0 : object.y1 + y,
y2: object.direction === 'vertical' ? canvas.height : object.y1 + y,
})
}
const handleClose = () => {
closePopup(id)
}

View File

@ -38,6 +38,11 @@ export default function PanelEdit(props) {
const isSetupModules = canvas.getObjects().filter((obj) => obj.name === 'module') // selectedObj
isSetupModules.forEach((obj) => obj.set({ lockMovementX: false, lockMovementY: false }))
}
//
return () => {
canvas?.discardActiveObject() //
}
}, [])
//
@ -87,7 +92,7 @@ export default function PanelEdit(props) {
moduleMultiCopy('row', length, direction)
break
}
closePopup(id)
// closePopup(id)
}
return (

View File

@ -15,27 +15,36 @@ export default function FlowLine({ FLOW_LINE_REF }) {
const currentObject = useRecoilValue(currentObjectState)
const handleFocus = () => {
if (currentObject === null) {
FLOW_LINE_REF.POINTER_INPUT_REF.current.value = ''
FLOW_LINE_REF.FILLED_INPUT_REF.current.value = ''
FLOW_LINE_REF.FILLED_INPUT_REF.current.blur()
}
}
const handleInput = (e) => {
const value = e.target.value.replace(/^0+/, '')
setFilledInput(value.replace(/[^0-9]/g, ''))
const regex = /^-?\d*$/
let value = e.target.value
if (!regex.test(value) && value !== '') return
if (value.startsWith('0') || value === '-0') return
setFilledInput(value.replace(/[^0-9-]/g, ''))
}
return (
<>
<div className="outline-wrap">
<div className="guide">{getMessage('modal.movement.flow.line.info')}</div>
<div className="eaves-keraba-table">
<div className="eaves-keraba-item">
<div className="eaves-keraba-th">
<div className="outline-form">
<span>{getMessage('modal.movement.flow.line.position')}</span>
<div className="input-grid mr5">
<input type="text" className="input-origin block" defaultValue={100} readOnly={true} ref={FLOW_LINE_REF.POINTER_INPUT_REF} />
</div>
</div>
<div className="moving-tab-content">
<div className="moving-tab-radio-wrap">
<div className="d-check-radio pop">
<input
type="radio"
name="radio01"
id="ra01"
defaultChecked={true}
ref={FLOW_LINE_REF.DOWN_LEFT_RADIO_REF}
onChange={() => {
setType(FLOW_LINE_TYPE.DOWN_LEFT)
@ -43,17 +52,6 @@ export default function FlowLine({ FLOW_LINE_REF }) {
/>
<label htmlFor="ra01">{getMessage('modal.movement.flow.line.bottom.left')}</label>
</div>
</div>
<div className="eaves-keraba-td">
<div className="outline-form">
<div className="input-grid mr5" style={{ width: '100px' }}>
{/*<input type="text" className="input-origin block" readOnly={true} ref={FLOW_LINE_REF.POINTER_INPUT_REF} />*/}
</div>
</div>
</div>
</div>
<div className="eaves-keraba-item">
<div className="eaves-keraba-th">
<div className="d-check-radio pop">
<input
type="radio"
@ -67,12 +65,13 @@ export default function FlowLine({ FLOW_LINE_REF }) {
<label htmlFor="ra02">{getMessage('modal.movement.flow.line.top.right')}</label>
</div>
</div>
<div className="eaves-keraba-td">
<div className="outline-form">
<div className="input-grid mr5" style={{ width: '100px' }}>
<span>{getMessage('modal.movement.flow.line.movement')}</span>
<div className="input-grid mr5">
<input
type="text"
className="input-origin block"
defaultValue={100}
ref={FLOW_LINE_REF.FILLED_INPUT_REF}
value={filledInput}
onFocus={handleFocus}
@ -83,8 +82,6 @@ export default function FlowLine({ FLOW_LINE_REF }) {
</div>
</div>
</div>
</div>
</div>
</>
)
}

View File

@ -15,6 +15,7 @@ export default function Updown({ UP_DOWN_REF }) {
const currentObject = useRecoilValue(currentObjectState)
const handleFocus = () => {
if (currentObject === null) {
UP_DOWN_REF.POINTER_INPUT_REF.current.value = ''
UP_DOWN_REF.FILLED_INPUT_REF.current.value = ''
UP_DOWN_REF.FILLED_INPUT_REF.current.blur()
}
@ -28,9 +29,14 @@ export default function Updown({ UP_DOWN_REF }) {
<>
<div className="outline-wrap">
<div className="guide">{getMessage('modal.movement.flow.line.updown.info')}</div>
<div className="eaves-keraba-table">
<div className="eaves-keraba-item">
<div className="eaves-keraba-th">
<div className="outline-form">
<span>{getMessage('modal.movement.flow.line.position')}</span>
<div className="input-grid mr5">
<input type="text" className="input-origin block" defaultValue={100} readOnly={true} ref={UP_DOWN_REF.POINTER_INPUT_REF} />
</div>
</div>
<div className="moving-tab-content">
<div className="moving-tab-radio-wrap">
<div className="d-check-radio pop">
<input
type="radio"
@ -44,17 +50,6 @@ export default function Updown({ UP_DOWN_REF }) {
/>
<label htmlFor="ra01">{getMessage('modal.movement.flow.line.updown.up')}</label>
</div>
</div>
<div className="eaves-keraba-td">
<div className="outline-form">
<div className="input-grid mr5" style={{ width: '100px' }}>
{/*<input type="text" className="input-origin block" readOnly={true} ref={UP_DOWN_REF.UP_INPUT_REF} />*/}
</div>
</div>
</div>
</div>
<div className="eaves-keraba-item">
<div className="eaves-keraba-th">
<div className="d-check-radio pop">
<input
type="radio"
@ -68,12 +63,13 @@ export default function Updown({ UP_DOWN_REF }) {
<label htmlFor="ra02">{getMessage('modal.movement.flow.line.updown.down')}</label>
</div>
</div>
<div className="eaves-keraba-td">
<div className="outline-form">
<div className="input-grid mr5" style={{ width: '100px' }}>
<span>{getMessage('modal.movement.flow.line.movement')}</span>
<div className="input-grid mr5">
<input
type="text"
className="input-origin block"
defaultValue={100}
ref={UP_DOWN_REF.FILLED_INPUT_REF}
value={filledInput}
onFocus={handleFocus}
@ -84,8 +80,6 @@ export default function Updown({ UP_DOWN_REF }) {
</div>
</div>
</div>
</div>
</div>
</>
)
}

View File

@ -100,7 +100,7 @@ export default function ObjectSetting({ id, pos = { x: 50, y: 230 } }) {
]
return (
<WithDraggable isShow={true} pos={pos} className="lrr" style={{ visibility: isHidden ? 'hidden' : 'visible' }}>
<WithDraggable isShow={true} pos={pos} className="lrr" isHidden={isHidden}>
<WithDraggable.Header title={getMessage('plan.menu.placement.surface.object')} onClose={() => closePopup(id)} />
<WithDraggable.Body>
<div className="modal-btn-wrap">

View File

@ -162,6 +162,7 @@ export default function WallLineSetting(props) {
<></>
)}
</div>
<div className="normal-font mt10">{getMessage('outerLine.property.info')}</div>
<div className="grid-btn-wrap">
<button className="btn-frame modal mr5" onClick={handleRollback}>
{getMessage('modal.cover.outline.rollback')}

View File

@ -19,6 +19,7 @@ import { getChonByDegree, getDegreeByChon } from '@/util/canvas-util'
import { usePolygon } from '@/hooks/usePolygon'
import { canvasState } from '@/store/canvasAtom'
import { useRoofFn } from '@/hooks/common/useRoofFn'
import { usePlan } from '@/hooks/usePlan'
/**
* 지붕 레이아웃
@ -53,7 +54,7 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, pla
rafter: useRef(null),
hajebichi: useRef(null),
}
const { saveCanvas } = usePlan()
/**
* 치수 입력방법(복시도입력/실측값입력/육지붕)
*/
@ -205,7 +206,7 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, pla
/**
* 배치면초기설정 저장 버튼 클릭
*/
const handleSaveBtn = () => {
const handleSaveBtn = async () => {
const roofInfo = {
...currentRoof,
planNo: basicSetting.planNo,
@ -226,7 +227,7 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, pla
console.log('save Info', {
...basicSetting,
selectedRoofMaterial: {
roofInfo,
...newAddedRoofs[0],
},
})
@ -239,7 +240,7 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, pla
* 선택된 지붕재 정보
*/
selectedRoofMaterial: {
roofInfo,
...newAddedRoofs[0],
},
})
@ -254,14 +255,13 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, pla
/* 저장 후 화면 닫기 */
closePopup(id)
await saveCanvas(false)
}
return (
<WithDraggable isShow={true} pos={pos} className="ll">
<WithDraggable.Header title={getMessage('plan.menu.placement.surface.initial.setting')} />
<WithDraggable.Body>
<div className="left-bar modal-handle"></div>
<div className="right-bar modal-handle"></div>
<div className="placement-table">
<table>
<colgroup>
@ -271,7 +271,11 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, pla
<tbody>
<tr>
<th>{getMessage('modal.placement.initial.setting.plan.drawing')}</th>
<td>{getMessage('modal.placement.initial.setting.plan.drawing.size.stuff')}</td>
<td>
{getMessage('modal.placement.initial.setting.plan.drawing.size.stuff')}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
{getMessage('modal.placement.initial.setting.plan.drawing.only.number')}
</td>
</tr>
<tr>
<th>

View File

@ -105,6 +105,16 @@ export default function GridOption(props) {
initEvent()
}, [gridOptions])
useEffect(() => {
return () => {
setAdsorptionPointAddMode(false)
setTempGridMode(false)
setTimeout(() => {
initEvent()
}, 100)
}
}, [])
const dotLineGridProps = {
id: dotLineId,
setIsShow: setShowDotLineGridModal,

View File

@ -185,7 +185,7 @@ export default function SecondOption(props) {
const onClickOption = async (item) => {
let option4Data = settingModalSecondOptions?.option4
let adsorpPointData = adsorptionPointMode.adsorptionPoint
let adsorpPointData = adsorptionPointMode
// ( )
if (
@ -203,11 +203,9 @@ export default function SecondOption(props) {
//
setAdsorptionRange(item.range)
setAdsorptionPointMode({ ...adsorptionPointMode, adsorptionPoint: adsorpPointData })
setAdsorptionPointMode(adsorpPointData)
} else if (item === 'adsorpPoint') {
setAdsorptionPointMode({ ...adsorptionPointMode, adsorptionPoint: !adsorpPointData })
adsorpPointData = !adsorpPointData
setAdsorptionPointMode(!adsorpPointData)
}
setSettingsData({ ...settingsData, option4: [...option4Data], adsorptionPoint: adsorpPointData })
@ -257,7 +255,7 @@ export default function SecondOption(props) {
}}
>
<span>{getMessage('modal.canvas.setting.font.plan.absorption.point')}</span>
<i>{adsorptionPointMode.adsorptionPoint ? 'ON' : 'OFF'}</i>
<i>{adsorptionPointMode ? 'ON' : 'OFF'}</i>
</button>
</div>
</div>

View File

@ -6,16 +6,22 @@ import WithDraggable from '@/components/common/draggable/WithDraggable'
import SecondOption from '@/components/floor-plan/modal/setting01/SecondOption'
import { useMessage } from '@/hooks/useMessage'
import GridOption from '@/components/floor-plan/modal/setting01/GridOption'
import { canGridOptionSeletor } from '@/store/canvasAtom'
import { useRecoilValue } from 'recoil'
import { adsorptionPointAddModeState, canGridOptionSeletor, tempGridModeState } from '@/store/canvasAtom'
import { useRecoilState, useRecoilValue } from 'recoil'
import { usePopup } from '@/hooks/usePopup'
import { useCanvasSetting } from '@/hooks/option/useCanvasSetting'
import { useTempGrid } from '@/hooks/useTempGrid'
import { settingModalGridOptionsState } from '@/store/settingAtom'
import { useEvent } from '@/hooks/useEvent'
export default function SettingModal01(props) {
const { id } = props
const [buttonAct, setButtonAct] = useState(1)
const { getMessage } = useMessage()
const canGridOptionSeletorValue = useRecoilValue(canGridOptionSeletor)
const [gridOptions, setGridOptions] = useRecoilState(settingModalGridOptionsState)
const [tempGridMode, setTempGridMode] = useRecoilState(tempGridModeState)
const [adsorptionPointAddMode, setAdsorptionPointAddMode] = useRecoilState(adsorptionPointAddModeState)
const { closePopup } = usePopup()
const {
@ -71,9 +77,22 @@ export default function SettingModal01(props) {
setButtonAct(num)
}
const onClose = () => {
setTempGridMode(false)
setAdsorptionPointAddMode(false)
setGridOptions((prev) => {
const newSettingOptions = [...prev]
newSettingOptions[0].selected = false
newSettingOptions[2].selected = false
return [...newSettingOptions]
})
closePopup(id, true)
}
return (
<WithDraggable isShow={true} pos={{ x: 1275, y: 180 }} className="sm">
<WithDraggable.Header title={getMessage('modal.canvas.setting')} onClose={() => closePopup(id, true)} />
<WithDraggable.Header title={getMessage('modal.canvas.setting')} onClose={onClose} />
<WithDraggable.Body>
<div className="modal-btn-wrap">
<button className={`btn-frame modal ${buttonAct === 1 ? 'act' : ''}`} onClick={() => handleBtnClick(1)}>

View File

@ -74,7 +74,7 @@ export default function Offset({ length1Ref, arrow1Ref, currentWallLineRef }) {
<div className="eaves-keraba-td">
<div className="outline-form">
<div className="input-grid mr5" style={{ width: '100px' }}>
<input type="text" className="input-origin block" defaultValue={0} ref={length1Ref} />
<input type="text" className="input-origin block" placeholder={0} ref={length1Ref} />
</div>
<span className="thin">mm</span>
</div>

View File

@ -46,7 +46,7 @@ export default forwardRef(function WallLine({ length1Ref, length2Ref, arrow1Ref,
<div className="eaves-keraba-td">
<div className="outline-form">
<div className="input-grid mr5" style={{ width: '100px' }}>
<input type="text" className="input-origin block" defaultValue={0} readOnly={type !== 1} ref={length1Ref} />
<input type="text" className="input-origin block" placeholder={0} readOnly={type !== 1} ref={length1Ref} />
</div>
<span className="thin">mm</span>
</div>
@ -80,7 +80,7 @@ export default forwardRef(function WallLine({ length1Ref, length2Ref, arrow1Ref,
<div className="eaves-keraba-td">
<div className="outline-form">
<div className="input-grid mr5" style={{ width: '100px' }}>
<input type="text" className="input-origin block" defaultValue={0} readOnly={type !== 2} ref={length2Ref} />
<input type="text" className="input-origin block" placeholder={0} readOnly={type !== 2} ref={length2Ref} />
</div>
<span className="thin">mm</span>
</div>

View File

@ -20,6 +20,11 @@ import { stuffSearchState } from '@/store/stuffAtom'
import { QcastContext } from '@/app/QcastProvider'
import { usePopup } from '@/hooks/usePopup'
import { commonCodeState } from '@/store/commonCodeAtom'
import { isObjectNotEmpty } from '@/util/common-utils'
import { roofMaterialsAtom } from '@/store/settingAtom'
import { useMasterController } from '@/hooks/common/useMasterController'
import { ROOF_MATERIAL_LAYOUT } from '@/components/floor-plan/modal/placementShape/PlacementShapeSetting'
export const ToggleonMouse = (e, act, target) => {
const listWrap = e.target.closest(target)
@ -67,6 +72,53 @@ export default function Header(props) {
const [SelectOptions, setSelectOptions] = useState([])
const [commonCode, setCommonCode] = useRecoilState(commonCodeState)
const { promiseGet } = useAxios()
/**
* 지붕재 목록 Header에서 조회
*/
const [roofMaterials, setRoofMaterials] = useRecoilState(roofMaterialsAtom)
const { getRoofMaterialList } = useMasterController()
useEffect(() => {
async function initRoofMaterial() {
if (roofMaterials.length !== 0) {
return
}
const { data } = await getRoofMaterialList()
const roofLists = data.map((item, idx) => ({
...item,
id: item.roofMatlCd,
name: item.roofMatlNm,
selected: idx === 0,
index: idx,
nameJp: item.roofMatlNmJp,
length: item.lenBase && parseInt(item.lenBase),
width: item.widBase && parseInt(item.widBase),
raft: item.raftBase && parseInt(item.raftBase),
layout: ['ROOF_ID_SLATE', 'ROOF_ID_SINGLE'].includes(item.roofMatlCd) ? ROOF_MATERIAL_LAYOUT.STAIRS : ROOF_MATERIAL_LAYOUT.PARALLEL,
hajebichi: item.roofPchBase && parseInt(item.roofPchBase),
pitch: item.pitch ? parseInt(item.pitch) : 4,
angle: item.angle ? parseInt(item.angle) : 21.8,
}))
setRoofMaterials(roofLists)
}
initRoofMaterial()
}, [])
useEffect(() => {
const getCommonCode = async () => {
await promiseGet({ url: '/api/commcode/qc-comm-code' }).then((res) => {
setCommonCode(Object.groupBy(res.data, ({ clHeadCd }) => clHeadCd))
})
}
if (!isObjectNotEmpty(commonCode)) {
getCommonCode()
}
}, [])
const getAutoLoginParam = async () => {
await promisePost({ url: '/api/login/v1.0/user/login/autoLoginEncryptData', data: { loginId: userSession.userId } })
.then((res) => {
@ -75,19 +127,19 @@ export default function Header(props) {
userSession.storeId === 'T01'
? [
{ id: 0, name: getMessage('site.header.link1'), target: '_blank' },
{ id: 1, name: 'Q.ORDER', link: `${qOrderUrl}?autoLoginParam1=${encodeURIComponent(res.data)}`, target: '_blank' },
{ id: 2, name: 'Q.Musubi', link: `${qMusubiUrl}?autoLoginParam1=${encodeURIComponent(res.data)}`, target: '_blank' },
{ id: 1, name: 'HANASYS ORDER', link: `${qOrderUrl}?autoLoginParam1=${encodeURIComponent(res.data)}`, target: '_blank' },
{ id: 2, name: 'HANASYS Musubi', link: `${qMusubiUrl}?autoLoginParam1=${encodeURIComponent(res.data)}`, target: '_blank' },
{ id: 3, name: getMessage('site.header.link2'), link: `https://q-warranty.q-cells.jp/seller_login`, target: '_blank' },
]
: userSession.groupId === '60000'
? [
{ id: 0, name: getMessage('site.header.link1'), target: '_blank' },
{ id: 1, name: 'Q.ORDER', link: `${qOrderUrl}?autoLoginParam1=${encodeURIComponent(res.data)}`, target: '_blank' },
{ id: 1, name: 'HANASYS ORDER', link: `${qOrderUrl}?autoLoginParam1=${encodeURIComponent(res.data)}`, target: '_blank' },
{ id: 2, name: getMessage('site.header.link2'), link: `https://q-warranty.q-cells.jp/seller_login`, target: '_blank' },
]
: [
{ id: 0, name: getMessage('site.header.link1'), target: '_blank' },
{ id: 1, name: 'Q.Musubi', link: `${qMusubiUrl}?autoLoginParam1=${encodeURIComponent(res.data)}`, target: '_blank' },
{ id: 1, name: 'HANASYS Musubi', link: `${qMusubiUrl}?autoLoginParam1=${encodeURIComponent(res.data)}`, target: '_blank' },
{ id: 2, name: getMessage('site.header.link2'), link: `https://q-warranty.q-cells.jp/seller_login`, target: '_blank' },
],
)
@ -125,6 +177,7 @@ export default function Header(props) {
{ id: 7, name: 'header.menus.community.archive', url: '/community/archive', children: [] },
],
},
{ id: 8, name: 'qna.title', url: '/community/qna', children: [] },
]
const syncSession = useCallback(() => {
@ -182,7 +235,7 @@ export default function Header(props) {
onClick={() => {
// moveHome()
removeStuffRecoil(menu)
if (pathName === '/') {
if (pathName === '/' && menu.id !== 8) {
window.location.reload()
}
}}

View File

@ -203,7 +203,7 @@ export default function Stuff() {
if (event.column.colId === 'objectNo') {
return
} else {
//T R
//T S
if (event.data.objectNo) {
setIsGlobalLoading(true)
if (event.data.tempFlg === '0') {
@ -238,6 +238,8 @@ export default function Stuff() {
schSortType: stuffSearchParams.schSortType,
pageNo: stuffSearchParams?.pageNo ? stuffSearchParams.pageNo : 1,
pageSize: stuffSearchParams?.pageSize ? stuffSearchParams.pageSize : 100,
builderNo: session.builderNo,
userId: session.userId
}
if (!params.saleStoreId) {

View File

@ -54,7 +54,7 @@ export default function StuffDetail() {
const { get, promiseGet, del, promisePost, promisePut } = useAxios(globalLocaleState)
//form
const formInitValue = {
// T...() R...()
// T...() S...()
planReqNo: '', //No
receiveUser: session?.userNm, //
objectStatusId: '0', //(:0 : 1)
@ -1033,8 +1033,7 @@ export default function StuffDetail() {
const _saleStoreId = watch('saleStoreId')
// 2
const _otherSaleStoreId = watch('otherSaleStoreId')
// zipNo: '', //
const _zipNo = watch('zipNo')
// zipNo: '', // #947
// prefId: '', //
const _prefId = watch('prefId')
// address: '', //
@ -1071,10 +1070,6 @@ export default function StuffDetail() {
}
}
if (!formData.zipNo) {
errors.zipNo = true
}
if (!formData.prefId) {
errors.prefId = true
}
@ -1115,10 +1110,6 @@ export default function StuffDetail() {
}
}
if (!formData.zipNo) {
errors.zipNo = true
}
if (!formData.prefId || formData.prefId === '0') {
errors.prefId = true
}
@ -1144,7 +1135,6 @@ export default function StuffDetail() {
_objectName,
_saleStoreId,
_otherSaleStoreId,
_zipNo,
_prefId,
_address,
_areaId,
@ -1189,6 +1179,14 @@ export default function StuffDetail() {
}
}, [prefValue])
// / disabled
const onChangePrefCode = (e) => {
setPrefValue(e.prefId)
form.setValue('prefId', e.prefId)
form.setValue('prefName', e.prefName)
}
//
const handleAreaIdOnChange = (e) => {
form.setValue('areaId', e.areaId)
@ -1243,12 +1241,6 @@ export default function StuffDetail() {
errors = fieldNm
}
//
if (!formData.zipNo) {
fieldNm = getMessage('stuff.detail.zipNo')
errors = fieldNm
}
//1
if (!formData.saleStoreId) {
fieldNm = getMessage('stuff.detail.saleStoreId')
@ -1370,10 +1362,11 @@ export default function StuffDetail() {
}
if (params?.receiveUser !== '') {
if (checkLength(params?.receiveUser.trim()) > 10) {
if (checkLength(params?.receiveUser.trim()) > 40) {
return swalFire({ text: getMessage('stuff.detail.tempSave.message2'), type: 'alert', icon: 'warning' })
}
}
// 2 otherSaleStoreId
if (session.storeLvl !== '1') {
if (params.saleStoreLevel === '1') {
@ -1443,20 +1436,15 @@ export default function StuffDetail() {
}
}
/**
* 전각20자 (반각40자)
*/
const checkLength = (value) => {
let str = new String(value)
let _byte = 0
if (str.length !== 0) {
for (let i = 0; i < str.length; i++) {
let str2 = str.charAt(i)
if (encodeURIComponent(str2).length > 4) {
_byte += 2
} else {
_byte++
}
}
}
return _byte
let fullWidthLength = value.replace(/[^\u3000-\u9FFF\uFF01-\uFF5E]/g, '').length
let halfWidthLength = value.replace(/[\u3000-\u9FFF\uFF01-\uFF5E]/g, '').length
let totalLength = fullWidthLength * 2 + halfWidthLength
return totalLength
}
//
const onTempSave = async () => {
@ -1506,7 +1494,7 @@ export default function StuffDetail() {
//
if (params?.receiveUser !== '') {
if (checkLength(params?.receiveUser.trim()) > 10) {
if (checkLength(params?.receiveUser.trim()) > 40) {
return swalFire({ text: getMessage('stuff.detail.tempSave.message2'), type: 'alert', icon: 'warning' })
}
}
@ -1558,7 +1546,7 @@ export default function StuffDetail() {
type: 'alert',
icon: 'error',
})
console.log('error::::::', error)
console.error('error::::::', error)
})
}
}
@ -2004,9 +1992,7 @@ export default function StuffDetail() {
</td>
</tr>
<tr>
<th>
{getMessage('stuff.detail.zipNo')} <span className="important">*</span>
</th>
<th>{getMessage('stuff.detail.zipNo')}</th>
<td>
<div className="flx-box">
<div className="input-wrap mr5" style={{ width: '200px' }}>
@ -2038,10 +2024,10 @@ export default function StuffDetail() {
getOptionLabel={(x) => x.prefName}
getOptionValue={(x) => x.prefId}
isSearchable={false}
onChange={onChangePrefCode}
value={prefCodeList.filter(function (option) {
return option.prefId === prefValue
})}
isDisabled={true}
/>
)}
</div>
@ -2571,9 +2557,7 @@ export default function StuffDetail() {
</td>
</tr>
<tr>
<th>
{getMessage('stuff.detail.zipNo')} <span className="important">*</span>
</th>
<th>{getMessage('stuff.detail.zipNo')}</th>
<td>
<div className="flx-box">
<div className="input-wrap mr5" style={{ width: '200px' }}>
@ -2606,10 +2590,10 @@ export default function StuffDetail() {
getOptionLabel={(x) => x.prefName}
getOptionValue={(x) => x.prefId}
isSearchable={false}
onChange={onChangePrefCode}
value={prefCodeList.filter(function (option) {
return option.prefId === prefValue
})}
isDisabled={true}
/>
)}
</div>

View File

@ -56,19 +56,20 @@ export default function StuffSubHeader({ type }) {
*/
const moveFloorPlan = () => {
setFloorPlanObjectNo({ floorPlanObjectNo: objectNo })
const param = {
pid: managementState?.planList?.length === 0 ? '1' : managementState?.planList[0].planNo,
pid: managementState?.planList?.length > 0 ? managementState?.planList[0].planNo : '1',
objectNo: objectNo,
}
if (managementState?.planList?.length === 0) {
setSelectedMenu('surface')
} else {
if (managementState?.planList?.length > 0) {
if (managementState?.planList[0].estimateDate) {
setSelectedMenu('module')
} else {
setSelectedMenu('surface')
}
} else {
setSelectedMenu('surface')
}
const url = `/floor-plan?${queryStringFormatter(param)}`

View File

@ -66,6 +66,11 @@ export default function FindAddressPop(props) {
zipcode: watch('zipNo'),
}
if (params.zipcode.length < 7) {
swalFire({ text: getMessage('stuff.addressPopup.error.message1'), type: 'alert', icon: 'warning' })
return
}
setIsGlobalLoading(true)
get({ url: `https://zipcloud.ibsnet.co.jp/api/search?${queryStringFormatter(params)}` }).then((res) => {
if (res.status === 200) {
@ -85,15 +90,20 @@ export default function FindAddressPop(props) {
}
//
const applyAddress = () => {
if (prefId == null) {
if (!isNotEmptyArray(gridProps.gridData)) {
swalFire({ text: getMessage('stuff.addressPopup.error.message2'), type: 'alert', icon: 'warning' })
return
}
if (gridProps.gridData[0].zipcode == '') {
swalFire({ text: getMessage('stuff.addressPopup.error.message2'), type: 'alert', icon: 'warning' })
} else {
// 1:1
props.zipInfo({
zipNo: zipNo,
address1: address1,
address2: address2,
address3: address3,
prefId: prefId,
zipNo: gridProps.gridData[0].zipcode,
address1: gridProps.gridData[0].address1,
address2: gridProps.gridData[0].address2,
address3: gridProps.gridData[0].address3,
prefId: gridProps.gridData[0].prefcode,
})
//

View File

@ -114,7 +114,7 @@ export default function Simulator() {
setHatsudenryouPeakcutAllSnow([])
if (objectNo && pid && selectedPlan) {
fetchObjectDetail(objectNo, selectedPlan.planNo)
fetchObjectDetail(objectNo, selectedPlan?.planNo??pid)
fetchSimulatorNotice()
setPwrGnrSimType('D')
setPwrRecoil({ ...pwrRecoil, type: 'D' })

View File

@ -0,0 +1,11 @@
// local, development, production 과 관계없이 동일한 값으로 반환되는 부분은 해당 함수의 return 되는 부분만 수정하면 됩니다. (달라져야 하는 값이 아닌, 같은 값에 대해서는 local, development, production 파일을 모두 수정할 필요가 없어지게 됩니다.)
export default function getConfigs(params) {
// local, development, production 마다 달라지는 값
const { baseUrl, mode } = params
// 공통으로 반환되는 구조
return {
baseUrl,
mode,
}
}

View File

@ -0,0 +1,13 @@
import getConfigs from './config.common'
// 환경마다 달라져야 할 변수, 값들을 정의합니다. (여기는 development 환경에 맞는 값을 지정합니다.)
const baseUrl = 'https://dev.hanssys.jp'
const mode = 'development'
// 환경마다 달라져야 할 값들을 getConfig 함수에 전달합니다.
const configDevelopment = getConfigs({
baseUrl,
mode,
})
export default configDevelopment

View File

@ -0,0 +1,23 @@
import configDevelopment from './config.development'
import configLocal from './config.local'
import configLocalDev from './config.local.dev'
import configProduction from './config.production'
// 클라이언트에서는 이 함수를 사용하여 config 값을 참조합니다.
const Config = () => {
console.log('🚀 ~ Config ~ process.env.NEXT_PUBLIC_RUN_MODE:', process.env.NEXT_PUBLIC_RUN_MODE)
switch (process.env.NEXT_PUBLIC_RUN_MODE) {
case 'local':
return configLocal
case 'local.dev':
return configLocalDev
case 'development':
return configDevelopment
case 'production':
return configProduction
default:
return configLocal
}
}
export default Config

View File

@ -0,0 +1,13 @@
import getConfigs from './config.common'
// 환경마다 달라져야 할 변수, 값들을 정의합니다. (여기는 local 환경에 맞는 값을 지정합니다.)
const baseUrl = 'http://1.248.227.176:5000'
const mode = 'local.dev'
// 환경마다 달라져야 할 값들을 getConfig 함수에 전달합니다.
const configLocalDev = getConfigs({
baseUrl,
mode,
})
export default configLocalDev

View File

@ -0,0 +1,13 @@
import getConfigs from './config.common'
// 환경마다 달라져야 할 변수, 값들을 정의합니다. (여기는 local 환경에 맞는 값을 지정합니다.)
const baseUrl = 'http://localhost:3000'
const mode = 'local'
// 환경마다 달라져야 할 값들을 getConfig 함수에 전달합니다.
const configLocal = getConfigs({
baseUrl,
mode,
})
export default configLocal

View File

@ -0,0 +1,13 @@
import getConfigs from './config.common'
// 환경마다 달라져야 할 변수, 값들을 정의합니다. (여기는 production 환경에 맞는 값을 지정합니다.)
const baseUrl = 'https://www.hanasys.jp'
const mode = 'production'
// 환경마다 달라져야 할 값들을 getConfig 함수에 전달합니다.
const configProduction = getConfigs({
baseUrl,
mode,
})
export default configProduction

View File

@ -9,7 +9,7 @@ import { globalFontAtom } from '@/store/fontAtom'
import { useRoof } from '@/hooks/common/useRoof'
import { usePolygon } from '@/hooks/usePolygon'
import { useRoofFn } from '@/hooks/common/useRoofFn'
import { POLYGON_TYPE } from '@/common/common'
import { LINE_TYPE, POLYGON_TYPE } from '@/common/common'
export function useCanvasConfigInitialize() {
const canvas = useRecoilValue(canvasState)
@ -64,6 +64,7 @@ export function useCanvasConfigInitialize() {
groupDimensionInit()
reGroupInit() //그룹 객체 재그룹
moduleInit()
innerLinesInit() // innerLines 세팅 추가
}
const gridInit = () => {
@ -228,5 +229,23 @@ export function useCanvasConfigInitialize() {
})
}
const innerLinesInit = () => {
const roofs = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
// innerLine이 세팅이 안되어있는경우 찾아서 세팅한다.
let innerLineTypes = Object.keys(LINE_TYPE.SUBLINE).map((key, value) => LINE_TYPE.SUBLINE[key])
roofs.forEach((roof) => {
roof.innerLines = canvas
.getObjects()
.filter(
(obj) =>
obj.type === 'QLine' &&
obj.attributes?.type !== 'pitchSizeLine' &&
obj.attributes?.roofId === roof.id &&
innerLineTypes.includes(obj.name),
)
})
}
return { canvasLoadInit, gridInit }
}

View File

@ -7,8 +7,6 @@ import { POLYGON_TYPE } from '@/common/common'
export const useCanvasMenu = () => {
const [selectedMenu, setSelectedMenu] = useRecoilState(selectedMenuState)
const canvas = useRecoilValue(canvasState)
const { drawDirectionArrow } = usePolygon()
return {
selectedMenu,

View File

@ -1,6 +1,6 @@
'use client'
import { useRecoilState, useRecoilValue } from 'recoil'
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'
import useSWRMutation from 'swr/mutation'
import { useAxios } from '../useAxios'
import { unescapeString } from '@/util/common-utils'
@ -9,7 +9,10 @@ import { compasDegAtom } from '@/store/orientationAtom'
import { canvasState, currentCanvasPlanState } from '@/store/canvasAtom'
import { POLYGON_TYPE } from '@/common/common'
import { useCircuitTrestle } from '../useCirCuitTrestle'
import { useEffect } from 'react'
import { useContext } from 'react'
import { addedRoofsState } from '@/store/settingAtom'
import { roofsState } from '@/store/roofAtom'
import { GlobalDataContext } from '@/app/GlobalDataProvider'
/**
* 캔버스 팝업 상태 관리
@ -19,19 +22,22 @@ import { useEffect } from 'react'
export function useCanvasPopupStatusController(param = 1) {
const popupType = parseInt(param)
const [compasDeg, setCompasDeg] = useRecoilState(compasDegAtom)
const setCompasDeg = useSetRecoilState(compasDegAtom)
const [moduleSelectionDataStore, setModuleSelectionDataStore] = useRecoilState(moduleSelectionDataState)
const [selectedModules, setSelectedModules] = useRecoilState(selectedModuleState)
const setSelectedModules = useSetRecoilState(selectedModuleState)
const { get, promiseGet, getFetcher, postFetcher } = useAxios()
const canvas = useRecoilValue(canvasState)
const currentCanvasPlan = useRecoilValue(currentCanvasPlanState)
const [addedRoofs, setAddedRoofs] = useRecoilState(addedRoofsState)
const [roofs, setRoofs] = useRecoilState(roofsState)
const { managementState, setManagementState } = useContext(GlobalDataContext)
/**
* 팝업 상태 조회
* @param {number} popupTypeParam
* @returns
*/
const getModuleSelection = async (popupTypeParam) => {
if (!currentCanvasPlan.objectNo || !currentCanvasPlan.planNo) return
const result = await promiseGet({
url: `/api/v1/canvas-popup-status?objectNo=${currentCanvasPlan.objectNo}&planNo=${currentCanvasPlan.planNo}&popupType=${popupTypeParam}`,
})
@ -43,7 +49,7 @@ export function useCanvasPopupStatusController(param = 1) {
return null
})
return result.data
return result?.data
}
/**
@ -51,23 +57,36 @@ export function useCanvasPopupStatusController(param = 1) {
* 조회 전체 데이터 recoil에 저장
*/
const handleModuleSelectionTotal = async () => {
let resultData = {}
for (let i = 1; i < 3; i++) {
const result = await getModuleSelection(i)
console.log('🚀 ~ handleModuleSelectionTotal ~ result:', result)
if (!result.objectNo) return
if (!result?.objectNo) return
if (i === 1) {
setCompasDeg(result.popupStatus)
if (result.popupStatus && unescapeString(result.popupStatus)) {
const data = JSON.parse(unescapeString(result.popupStatus))
if (data?.compasDeg) setCompasDeg(data.compasDeg)
if (data?.module) setSelectedModules(data.module)
// setModuleSelectionDataStore(data)
resultData = { ...data }
}
} else if (i === 2) {
const data = JSON.parse(unescapeString(result.popupStatus))
setModuleSelectionDataStore(data)
const roofSurfaceList = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE)
const modules = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE)
roofSurfaceList.forEach((surface) => {
surface.modules = modules.filter((module) => module.surfaceId === surface.id)
})
if (data.module) setSelectedModules(data.module)
if (data.roofConstructions) {
setRoofs(data.roofConstructions)
// setManagementState({ ...managementState, roofs: data.roofConstructions.map((roof) => roof.construction.managementState) })
// setModuleSelectionDataStore({ ...moduleSelectionDataStore, roofConstructions: data.roofConstruction })
resultData = { ...resultData, roofConstructions: data.roofConstructions }
}
// if (data?.module) setManagementState(data.common.managementState)
}
}
setModuleSelectionDataStore(resultData)
}
/**
@ -80,7 +99,8 @@ export function useCanvasPopupStatusController(param = 1) {
objectNo: currentCanvasPlan.objectNo,
planNo: parseInt(currentCanvasPlan.planNo),
popupType: popupType.toString(),
popupStatus: popupType === 1 ? arg : JSON.stringify(arg).replace(/"/g, '\"'),
// popupStatus: popupType === 1 ? arg : JSON.stringify(arg).replace(/"/g, '\"'),
popupStatus: JSON.stringify(arg).replace(/"/g, '\"'),
}
postFetcher(`/api/v1/canvas-popup-status`, params)
},

View File

@ -22,9 +22,8 @@ import { useAxios } from '../useAxios'
* const honorificCodes = findCommonCode(200800);
*/
export const useCommonCode = () => {
const [commonCode, setCommonCode] = useRecoilState(commonCodeState)
const globalLocale = useRecoilValue(globalLocaleStore)
const { promiseGet } = useAxios()
const [commonCode, setCommonCode] = useRecoilState(commonCodeState)
const findCommonCode = (key) => {
const resultCodes = commonCode[key]?.map((code) => {
@ -41,18 +40,6 @@ export const useCommonCode = () => {
return resultCodes
}
useEffect(() => {
const getCommonCode = async () => {
await promiseGet({ url: '/api/commcode/qc-comm-code' }).then((res) => {
setCommonCode(Object.groupBy(res.data, ({ clHeadCd }) => clHeadCd))
})
}
if (!isObjectNotEmpty(commonCode)) {
getCommonCode()
}
}, [])
return {
commonCode,
findCommonCode,

View File

@ -1,11 +1,11 @@
import { useEffect } from 'react'
import { useRecoilState, useRecoilValue } from 'recoil'
import { useRecoilState, useRecoilValue, useResetRecoilState } from 'recoil'
import { wordDisplaySelector } from '@/store/settingAtom'
import { useEvent } from '@/hooks/useEvent'
import { checkLineOrientation, getDistance } from '@/util/canvas-util'
import { commonUtilsState, dimensionLineSettingsState } from '@/store/commonUtilsAtom'
import { fontSelector } from '@/store/fontAtom'
import { canvasState } from '@/store/canvasAtom'
import { canvasState, currentMenuState } from '@/store/canvasAtom'
import { v4 as uuidv4 } from 'uuid'
import { usePopup } from '@/hooks/usePopup'
import Distance from '@/components/floor-plan/modal/distance/Distance'
@ -16,29 +16,34 @@ import { BATCH_TYPE } from '@/common/common'
export function useCommonUtils() {
const canvas = useRecoilValue(canvasState)
const wordDisplay = useRecoilValue(wordDisplaySelector)
const { addCanvasMouseEventListener, addDocumentEventListener, initEvent } = useEvent()
const { addCanvasMouseEventListener, addDocumentEventListener, initEvent, removeMouseEvent } = useEvent()
const dimensionSettings = useRecoilValue(dimensionLineSettingsState)
const dimensionLineTextFont = useRecoilValue(fontSelector('dimensionLineText'))
const lengthTextFont = useRecoilValue(fontSelector('lengthText'))
const commonTextFont = useRecoilValue(fontSelector('commonText'))
const [commonUtils, setCommonUtilsState] = useRecoilState(commonUtilsState)
const { addPopup } = usePopup()
const { addPopup, closeAll, targetClose } = usePopup()
const { drawDirectionArrow, addLengthText } = usePolygon()
const { applyDormers } = useObjectBatch({})
useEffect(() => {
if (commonUtils.text) {
commonTextMode()
} else if (commonUtils.dimension) {
if (commonUtils.dimension) {
commonDimensionMode()
} else if (commonUtils.distance) {
return
}
if (commonUtils.distance) {
commonDistanceMode()
return
}
}, [commonUtils, dimensionSettings, commonTextFont, dimensionLineTextFont])
const commonTextMode = () => {
let textbox
if (commonUtils.text) {
targetClose('other')
setTimeout(() => {
commonTextKeyEvent()
addCanvasMouseEventListener('mouse:down', (event) => {
const pointer = canvas?.getPointer(event.e)
@ -67,6 +72,35 @@ export function useCommonUtils() {
textbox.enterEditing()
textbox.selectAll()
})
}, 100)
} else {
removeMouseEvent('mouse:down')
const activeObject = canvas?.getActiveObject()
const commonTexts = canvas?.getObjects().filter((obj) => obj.name === 'commonText')
if (commonTexts) {
commonTexts.forEach((text) => {
if (text.text === '') {
canvas?.remove(text)
}
})
}
/*if (activeObject && activeObject.name === 'commonText') {
if (activeObject && activeObject.isEditing) {
if (activeObject.text === '') {
canvas?.remove(activeObject)
} else {
activeObject.exitEditing()
}
//정책 협의
const texts = canvas.getObjects().filter((obj) => obj.name === 'commonText')
texts.forEach((text) => {
text.set({ editable: false })
})
canvas.renderAll()
}
}*/
initEvent()
}
}

View File

@ -153,18 +153,18 @@ export function useGrid() {
const move = (object, x, y) => {
object.set({
...object,
x1: object.direction === 'vertical' ? object.x1 + x : 0,
x2: object.direction === 'vertical' ? object.x1 + x : canvas.width,
y1: object.direction === 'vertical' ? 0 : object.y1 + y,
y2: object.direction === 'vertical' ? canvas.height : object.y1 + y,
x1: object.direction === 'vertical' ? object.x1 + x : -1500,
x2: object.direction === 'vertical' ? object.x1 + x : 2500,
y1: object.direction === 'vertical' ? -1500 : object.y1 + y,
y2: object.direction === 'vertical' ? 2500 : object.y1 + y,
})
}
const copy = (object, length) => {
const lineStartX = object.direction === 'vertical' ? object.x1 + length : 0
const lineEndX = object.direction === 'vertical' ? object.x2 + length : canvas.width
const lineStartY = object.direction === 'vertical' ? 0 : object.y1 + length
const lineEndY = object.direction === 'vertical' ? canvas.width : object.y1 + length
const lineStartX = object.direction === 'vertical' ? object.x1 + length : -1500
const lineEndX = object.direction === 'vertical' ? object.x2 + length : 2500
const lineStartY = object.direction === 'vertical' ? -1500 : object.y1 + length
const lineEndY = object.direction === 'vertical' ? 2500 : object.y1 + length
const line = new fabric.Line([lineStartX, lineStartY, lineEndX, lineEndY], {
stroke: gridColor,

View File

@ -18,7 +18,7 @@ export function useMasterController() {
*/
const getRoofMaterialList = async () => {
return await get({ url: '/api/v1/master/getRoofMaterialList' }).then((res) => {
console.log('🚀🚀 ~ getRoofMaterialList ~ res:', res)
// console.log('🚀🚀 ~ getRoofMaterialList ~ res:', res)
return res
})
}

View File

@ -16,12 +16,13 @@ import { usePopup } from '@/hooks/usePopup'
import { useState } from 'react'
import { v4 as uuidv4 } from 'uuid'
import { useSurfaceShapeBatch } from '@/hooks/surface/useSurfaceShapeBatch'
import { useRecoilValue } from 'recoil'
import { useRecoilState, useRecoilValue } from 'recoil'
import { canvasState, currentMenuState } from '@/store/canvasAtom'
import { MENU } from '@/common/common'
import { useTrestle } from '@/hooks/module/useTrestle'
import { usePolygon } from '@/hooks/usePolygon'
import { useOrientation } from '@/hooks/module/useOrientation'
import { corridorDimensionSelector, settingModalFirstOptionsState } from '@/store/settingAtom'
/**
* 메뉴 처리
@ -36,8 +37,11 @@ export default function useMenu() {
const { deleteAllSurfacesAndObjects } = useSurfaceShapeBatch({})
const { clear: trestleClear, setAllModuleSurfaceIsComplete } = useTrestle()
const { nextStep } = useOrientation()
const [corridorDimension, setCorridorDimension] = useRecoilState(corridorDimensionSelector)
const handleMenu = (type) => {
if (type === 'outline') {
// 지붕 덮개 메뉴의 경우는 복도치수로 적용한다.
setCorridorDimension(0)
switch (currentMenu) {
case MENU.ROOF_COVERING.EXTERIOR_WALL_LINE:
addPopup(popupId, 1, <WallLineSetting id={popupId} />)
@ -67,6 +71,8 @@ export default function useMenu() {
}
if (type === 'surface') {
// 배치면 메뉴의 경우는 실치수로 적용한다.
setCorridorDimension(1)
switch (currentMenu) {
// case MENU.BATCH_CANVAS.SLOPE_SETTING:
// addPopup(popupId, 1, <Slope id={popupId} />)
@ -87,6 +93,8 @@ export default function useMenu() {
}
if (type === 'module') {
// 모듈,회로 구성 메뉴의 경우는 실치수로 적용한다.
setCorridorDimension(1)
switch (currentMenu) {
case MENU.MODULE_CIRCUIT_SETTING.BASIC_SETTING:
trestleClear()

View File

@ -1,5 +1,5 @@
import { useEffect, useRef, useState } from 'react'
import { useRecoilState } from 'recoil'
import { useRecoilState, useSetRecoilState } from 'recoil'
import { useSwal } from '@/hooks/useSwal'
import { useAxios } from '../useAxios'
@ -7,6 +7,8 @@ import { currentCanvasPlanState } from '@/store/canvasAtom'
import { useCanvas } from '@/hooks/useCanvas'
import { deleteBackGroundImage, setBackGroundImage } from '@/lib/imageActions'
import { settingModalFirstOptionsState } from '@/store/settingAtom'
import { popSpinnerState } from '@/store/popupAtom'
import Config from '@/config/config.export'
/**
* 배경 이미지 관리
@ -15,6 +17,9 @@ import { settingModalFirstOptionsState } from '@/store/settingAtom'
* 이미지 -> 캔버스 배경에 이미지 로드
* 주소 -> 구글 맵에서 주소 검색 이미지로 다운로드 받아서 캔버스 배경에 이미지 로드
* .dwg -> api를 통해서 .png로 변환 캔버스 배경에 이미지 로드
*
* setCurrentBgImage 이미지를 세팅하면 도면에 배경으로 로딩된다.
* 다만 S3 Response에 aws 고유 주소가 나오는데 여기서는 cloudfront 사용을 위해서 NEXT_PUBLIC_AWS_S3_BASE_URL 도메인을 사용한다.
* @returns {object}
*/
export function useRefFiles() {
@ -28,7 +33,8 @@ export function useRefFiles() {
const [settingModalFirstOptions, setSettingModalFirstOptions] = useRecoilState(settingModalFirstOptionsState)
const { handleBackImageLoadToCanvas } = useCanvas()
const { swalFire } = useSwal()
const { get, post } = useAxios()
const { get, post, del } = useAxios()
const setPopSpinnerStore = useSetRecoilState(popSpinnerState)
useEffect(() => {
if (refFileMethod === '1') {
@ -39,6 +45,7 @@ export function useRefFiles() {
}, [refFileMethod])
/**
* 최초 input type="file" 대한 이벤트
* 파일 불러오기 버튼 컨트롤
* @param {*} file
*/
@ -59,6 +66,10 @@ export function useRefFiles() {
}
}
/**
* 허용하는 파일인지 체크한다
* @param {File} file
*/
const refFileSetting = (file) => {
console.log('🚀 ~ refFileSetting ~ file:', file)
if (file.name.split('.').pop() === 'dwg') {
@ -77,34 +88,40 @@ export function useRefFiles() {
}
/**
* 파일 삭제
* 이미지 파일 삭제
*/
const handleFileDelete = async () => {
swalFire({
text: '삭제하시겠습니까?',
type: 'confirm',
confirmFn: async () => {
setRefImage(null)
setCurrentCanvasPlan((prev) => ({ ...prev, bgImageName: null }))
setPopSpinnerStore(true)
console.log('🚀 ~ handleFileDelete ~ handleFileDelete:', refImage)
console.log('🚀 ~ handleFileDelete ~ currentCanvasPlan.bgImageName:', currentCanvasPlan.bgImageName)
await del({ url: `${Config().baseUrl}/api/image/upload?fileName=${currentCanvasPlan.bgImageName}` })
setCurrentBgImage(null)
await deleteBackGroundImage({
objectId: currentCanvasPlan.id,
planNo: currentCanvasPlan.planNo,
})
setPopSpinnerStore(false)
},
})
}
/**
* 주소 삭제
* 주소 이미지 삭제
*/
const handleAddressDelete = async () => {
swalFire({
text: '삭제하시겠습니까?',
type: 'confirm',
confirmFn: async () => {
console.log('🚀 ~ handleAddressDelete ~ handleAddressDelete:', refImage)
console.log('🚀 ~ handleAddressDelete ~ currentCanvasPlan.bgImageName:', currentCanvasPlan.bgImageName)
await del({ url: `${Config().baseUrl}/api/image/map?fileName=${currentCanvasPlan.bgImageName}` })
setMapPositionAddress('')
setCurrentBgImage(null)
setCurrentCanvasPlan((prev) => ({ ...prev, mapPositionAddress: null }))
await deleteBackGroundImage({
objectId: currentCanvasPlan.id,
planNo: currentCanvasPlan.planNo,
@ -114,7 +131,7 @@ export function useRefFiles() {
}
/**
* 주소로 구글 이미지 다운로드
* 주소로 구글 이미지 다운로드하여 캔버스 배경으로 로드
*/
const handleMapImageDown = async () => {
console.log('🚀 ~ handleMapImageDown ~ handleMapImageDown:')
@ -133,15 +150,16 @@ export function useRefFiles() {
}))
const res = await get({
url: `${process.env.NEXT_PUBLIC_HOST_URL}/map/convert?q=${queryRef.current.value}&fileNm=${currentCanvasPlan.id}&zoom=20`,
url: `${Config().baseUrl}/api/image/map?q=${queryRef.current.value}&fileNm=${currentCanvasPlan.id}&zoom=20`,
})
console.log('🚀 ~ handleMapImageDown ~ res:', res)
setCurrentBgImage(`${process.env.NEXT_PUBLIC_HOST_URL}${res.filePath}`)
setCurrentBgImage(`${process.env.NEXT_PUBLIC_AWS_S3_BASE_URL}/${res.fileName}`)
await setBackGroundImage({
objectId: currentCanvasPlan.id,
planNo: currentCanvasPlan.planNo,
imagePath: `${process.env.NEXT_PUBLIC_HOST_URL}${res.filePath}`,
// imagePath: `${process.env.NEXT_PUBLIC_HOST_URL}${res.filePath}`,
imagePath: `${res.filePath}`,
})
}
@ -149,16 +167,26 @@ export function useRefFiles() {
* 배경 이미지 로드를 위한 세팅
*/
useEffect(() => {
if (!currentBgImage) {
return
}
// if (!currentBgImage) {
// return
// }
console.log('🚀 ~ useEffect ~ currentBgImage:', currentBgImage)
if (currentBgImage) {
handleBackImageLoadToCanvas(currentBgImage)
setCurrentCanvasPlan((prev) => ({
...prev,
bgImageName: refImage?.name ?? null,
// bgImageName: refImage?.name ?? null,
bgImageName: currentBgImage.split('/').pop(),
mapPositionAddress: queryRef.current.value,
}))
} else {
setRefImage(null)
setCurrentCanvasPlan((prev) => ({
...prev,
bgImageName: null,
mapPositionAddress: null,
}))
}
}, [currentBgImage])
/**
@ -166,6 +194,7 @@ export function useRefFiles() {
* @param {*} file
*/
const handleUploadImageRefFile = async (file) => {
setPopSpinnerStore(true)
const newOption1 = settingModalFirstOptions.option1.map((option) => ({
...option,
selected: option.column === 'imageDisplay' ? true : option.selected,
@ -180,20 +209,22 @@ export function useRefFiles() {
formData.append('file', file)
const res = await post({
url: `${process.env.NEXT_PUBLIC_HOST_URL}/image/upload`,
url: `${Config().baseUrl}/api/image/upload`,
data: formData,
})
console.log('🚀 ~ handleUploadImageRefFile ~ res:', res)
setCurrentBgImage(`${process.env.NEXT_PUBLIC_HOST_URL}${res.filePath}`)
setCurrentBgImage(`${process.env.NEXT_PUBLIC_AWS_S3_BASE_URL}/${res.fileName}`)
setRefImage(file)
const params = {
objectId: currentCanvasPlan.id,
planNo: currentCanvasPlan.planNo,
imagePath: `${process.env.NEXT_PUBLIC_HOST_URL}${res.filePath}`,
// imagePath: `${process.env.NEXT_PUBLIC_HOST_URL}${res.filePath}`,
imagePath: `${res.filePath}`,
}
console.log('🚀 ~ handleUploadImageRefFile ~ params:', params)
await setBackGroundImage(params)
setPopSpinnerStore(false)
}
/**
@ -204,15 +235,54 @@ export function useRefFiles() {
const formData = new FormData()
formData.append('file', file)
/** 캐드 도면 파일 변환 */
const res = await post({ url: converterUrl, data: formData })
console.log('🚀 ~ handleUploadConvertRefFile ~ res:', res)
// Convert Base64 to Blob
const base64Data = res.Files[0].FileData
const byteCharacters = atob(base64Data)
const byteArrays = []
for (let offset = 0; offset < byteCharacters.length; offset += 512) {
const slice = byteCharacters.slice(offset, offset + 512)
const byteNumbers = new Array(slice.length)
for (let i = 0; i < slice.length; i++) {
byteNumbers[i] = slice.charCodeAt(i)
}
const byteArray = new Uint8Array(byteNumbers)
byteArrays.push(byteArray)
}
const blob = new Blob(byteArrays, { type: 'image/png' })
// Create File object from Blob
const convertImg = new File([blob], res.Files[0].FileName, { type: 'image/png' })
const newFormData = new FormData()
newFormData.append('file', convertImg)
/** 캐드 도면 파일 업로드 */
const result = await post({
url: `${process.env.NEXT_PUBLIC_HOST_URL}/cad/convert`,
data: res,
url: `${Config().baseUrl}/api/image/cad`,
data: newFormData,
})
console.log('🚀 ~ handleUploadConvertRefFile ~ result:', result)
setCurrentBgImage(`${process.env.NEXT_PUBLIC_HOST_URL}${result.filePath}`)
setRefImage(res.Files[0].FileData)
setCurrentBgImage(`${process.env.NEXT_PUBLIC_AWS_S3_BASE_URL}/${result.fileName}`)
// setCurrentBgImage(result.filePath)
setRefImage(file)
const params = {
objectId: currentCanvasPlan.id,
planNo: currentCanvasPlan.planNo,
// imagePath: `${process.env.NEXT_PUBLIC_HOST_URL}${res.filePath}`,
imagePath: `${result.filePath}`,
}
console.log('🚀 ~ handleUploadImageRefFile ~ params:', params)
await setBackGroundImage(params)
}
/**

View File

@ -23,6 +23,7 @@ export function useRoofFn() {
//면형상 선택 클릭시 지붕 패턴 입히기
function setSurfaceShapePattern(polygon, mode = 'onlyBorder', trestleMode = false, roofMaterial, isForceChange = false, isDisplay = false) {
try {
if (!polygon) {
return
}
@ -169,6 +170,9 @@ export function useRoofFn() {
polygon.set('fill', pattern)
polygon.roofMaterial = roofMaterial
polygon.canvas?.renderAll()
} catch (e) {
console.log(e)
}
}
function removeRoofMaterial(roof = currentObject) {

View File

@ -13,6 +13,8 @@ import { useSwal } from '@/hooks/useSwal'
// Constants
const ESTIMATE_API_ENDPOINT = '/api/estimate' // API 엔드포인트 정의
import Config from '@/config/config.export'
// Helper functions
const updateItemInList = (itemList, dispOrder, updates) => {
return itemList.map((item) => (item.dispOrder === dispOrder ? { ...item, ...updates } : item))
@ -130,13 +132,14 @@ export const useEstimateController = (planNo, flag) => {
addFlg: true,
paDispOrder: null,
dispCableFlg: '0',
itemTpCd: '',
},
],
})
}
useEffect(() => {
setEstimateData({ ...estimateContextState, userId: session.userId, sapSalesStoreCd: session.custCd })
setEstimateData({ ...estimateContextState, userId: session.userId })
}, [estimateContextState])
// 첨부파일 다운로드
@ -309,6 +312,7 @@ export const useEstimateController = (planNo, flag) => {
//봄 컴포넌트 제품은 0으로
item.openFlg = '0'
item.unitOpenFlg = '0'
}
}
})
@ -321,6 +325,7 @@ export const useEstimateController = (planNo, flag) => {
}
})
if (delCnt === estimateData.itemList.length) {
itemFlg = false
setIsGlobalLoading(false)
return swalFire({ text: getMessage('estimate.detail.save.requiredItem'), type: 'alert', icon: 'warning' })
}
@ -446,14 +451,12 @@ export const useEstimateController = (planNo, flag) => {
icon: 'warning',
})
} else {
if (checkLength(copyReceiveUser.trim()) > 10) {
if (checkLength(copyReceiveUser.trim()) > 40) {
return swalFire({ text: getMessage('stuff.detail.tempSave.message2'), type: 'alert', icon: 'warning' })
}
}
const params = {
saleStoreId: session.storeId,
sapSalesStoreCd: session.custCd,
objectNo: estimateData.objectNo,
planNo: sendPlanNo,
copySaleStoreId: otherSaleStoreId ? otherSaleStoreId : saleStoreId,
@ -462,11 +465,14 @@ export const useEstimateController = (planNo, flag) => {
}
setIsGlobalLoading(true)
await promisePost({ url: '/api/estimate/save-estimate-copy', data: params }).then((res) => {
await promisePost({ url: '/api/estimate/save-estimate-copy', data: params })
.then(async (res) => {
setIsGlobalLoading(false)
if (res.status === 201) {
if (isObjectNotEmpty(res.data)) {
let newObjectNo = res.data.objectNo
const copyImage = await handleEstimateImageCopy(params.objectNo, params.planNo, newObjectNo, '1')
swalFire({
text: getMessage('estimate.detail.estimateCopyPopup.copy.alertMessage'),
type: 'alert',
@ -480,22 +486,43 @@ export const useEstimateController = (planNo, flag) => {
setIsGlobalLoading(false)
}
})
.catch((e) => {
console.error('캔버스 복사 중 오류 발생')
swalFire({ text: getMessage('estimate.detail.estimateCopyPopup.copy.alertMessageError'), type: 'alert', icon: 'error' })
setIsGlobalLoading(false)
})
}
const handleEstimateImageCopy = async (objectNo, planNo, newObjectNo, newPlanNo) => {
await promisePost({ url: `${Config().baseUrl}/api/image/estimate-image-copy`, data: { objectNo, planNo, newObjectNo, newPlanNo } }).then(
(res) => {
return res
},
)
}
const handleDeleteEstimate = async (canvasStatus) => {
try {
setIsGlobalLoading(true)
await promisePost({ url: `${ESTIMATE_API_ENDPOINT}/delete-estimate`, data: canvasStatus }).then((res) => {
if (res.status === 201) {
}
})
} catch (e) {
console.error('error::::::::::::', e.response.data.message)
}
setIsGlobalLoading(false)
}
/**
* 전각20자 (반각40자)
*/
const checkLength = (value) => {
let str = new String(value)
let _byte = 0
if (str.length !== 0) {
for (let i = 0; i < str.length; i++) {
let str2 = str.charAt(i)
if (encodeURIComponent(str2).length > 4) {
_byte += 2
} else {
_byte++
}
}
}
return _byte
let fullWidthLength = value.replace(/[^\u3000-\u9FFF\uFF01-\uFF5E]/g, '').length
let halfWidthLength = value.replace(/[\u3000-\u9FFF\uFF01-\uFF5E]/g, '').length
let totalLength = fullWidthLength * 2 + halfWidthLength
return totalLength
}
return {
@ -507,5 +534,7 @@ export const useEstimateController = (planNo, flag) => {
fetchSetting,
handleEstimateFileDownload,
handleEstimateCopy,
handleDeleteEstimate,
handleEstimateImageCopy,
}
}

View File

@ -5,6 +5,7 @@ import { usePlan } from '../usePlan'
import { POLYGON_TYPE } from '@/common/common'
import { QcastContext } from '@/app/QcastProvider'
import { useContext } from 'react'
import Config from '@/config/config.export'
/**
* 이미지 로더 hook
@ -79,7 +80,8 @@ export function useImgLoader() {
/** 이미지 크롭 요청 */
const result = await post({
url: `${process.env.NEXT_PUBLIC_HOST_URL}/image/canvas`,
// url: `${process.env.NEXT_PUBLIC_API_HOST_URL}/image/canvas`,
url: `${Config().baseUrl}/api/image/canvas`,
data: formData,
})
console.log('🚀 ~ handleCanvasToPng ~ result:', result)

View File

@ -27,7 +27,7 @@ export const useMainContentsController = () => {
*/
const fetchObjectList = async () => {
try {
const apiUrl = `/api/main-page/object/${session?.storeId}/list`
const apiUrl = `/api/main-page/object/${session?.storeId}/${session?.userId}/${session?.builderNo}/list`
await promiseGet({
url: apiUrl,
}).then((res) => {

View File

@ -54,7 +54,7 @@ export function useModule() {
})
return
}
canvas.discardActiveObject() //선택해제
// canvas.discardActiveObject() //선택해제
const isSetupModules = getOtherModules(selectedObj)
const selectedModules = canvas.getObjects().filter((obj) => selectedIds.includes(obj.id) && obj.name === 'module') //선택했던 객체들만 가져옴
@ -991,14 +991,14 @@ export function useModule() {
const getRowModules = (target) => {
return canvas
.getObjects()
.filter((obj) => target.surfaceId === obj.surfaceId && obj.name === POLYGON_TYPE.MODULE && obj.top === target.top)
.filter((obj) => target.surfaceId === obj.surfaceId && obj.name === POLYGON_TYPE.MODULE && Math.abs(obj.top - target.top) < 1)
.sort((a, b) => a.left - b.left)
}
const getColumnModules = (target) => {
return canvas
.getObjects()
.filter((obj) => target.surfaceId === obj.surfaceId && obj.name === POLYGON_TYPE.MODULE && obj.left === target.left)
.filter((obj) => target.surfaceId === obj.surfaceId && obj.name === POLYGON_TYPE.MODULE && Math.abs(obj.left - target.left) < 1)
.sort((a, b) => a.top - b.top)
}

File diff suppressed because it is too large Load Diff

View File

@ -1,35 +0,0 @@
import { useEffect, useState } from 'react'
import { useRecoilValue, useSetRecoilState } from 'recoil'
import { moduleSelectionDataState, selectedModuleState } from '@/store/selectedModuleOptions'
import { useMasterController } from '@/hooks/common/useMasterController'
import { canvasSettingState, canvasState, currentCanvasPlanState, moduleSetupSurfaceState } from '@/store/canvasAtom'
import { POLYGON_TYPE, BATCH_TYPE } from '@/common/common'
import { useRoofFn } from '@/hooks/common/useRoofFn'
import { roofDisplaySelector } from '@/store/settingAtom'
import offsetPolygon from '@/util/qpolygon-utils'
import { v4 as uuidv4 } from 'uuid'
import { QPolygon } from '@/components/fabric/QPolygon'
import { useEvent } from '@/hooks/useEvent'
import { useSwal } from '@/hooks/useSwal'
import { useMessage } from '@/hooks/useMessage'
export function useModulePlace() {
const canvas = useRecoilValue(canvasState)
const moduleSelectionData = useRecoilValue(moduleSelectionDataState)
const [trestleDetailParams, setTrestleDetailParams] = useState([])
const [trestleDetailList, setTrestleDetailList] = useState([])
const selectedModules = useRecoilValue(selectedModuleState)
const { getTrestleDetailList } = useMasterController()
const canvasSetting = useRecoilValue(canvasSettingState)
const { setSurfaceShapePattern } = useRoofFn()
const roofDisplay = useRecoilValue(roofDisplaySelector)
const { addTargetMouseEventListener } = useEvent()
const setModuleSetupSurface = useSetRecoilState(moduleSetupSurfaceState)
const [saleStoreNorthFlg, setSaleStoreNorthFlg] = useState(false)
const { swalFire } = useSwal()
const { getMessage } = useMessage()
return {
selectedModules,
}
}

View File

@ -3,7 +3,7 @@ import { useContext, useEffect, useState } from 'react'
import { GlobalDataContext } from '@/app/GlobalDataProvider'
import { useMasterController } from '@/hooks/common/useMasterController'
import { useCommonCode } from '@/hooks/common/useCommonCode'
import { selectedModuleState, moduleSelectionInitParamsState, moduleSelectionDataState } from '@/store/selectedModuleOptions'
import { selectedModuleState, moduleSelectionDataState } from '@/store/selectedModuleOptions'
import { isObjectNotEmpty } from '@/util/common-utils'
import { canvasState } from '@/store/canvasAtom'
import { POLYGON_TYPE } from '@/common/common'
@ -12,23 +12,20 @@ import { useModuleBasicSetting } from '@/hooks/module/useModuleBasicSetting'
export function useModuleSelection(props) {
const canvas = useRecoilValue(canvasState)
const { managementState, setManagementState, managementStateLoaded } = useContext(GlobalDataContext)
const { managementState, setManagementState } = useContext(GlobalDataContext)
const [roughnessCodes, setRoughnessCodes] = useState([]) //면조도 목록
const [windSpeedCodes, setWindSpeedCodes] = useState([]) //기준풍속 목록
const [moduleList, setModuleList] = useState([{}]) //모듈 목록
const [selectedSurfaceType, setSelectedSurfaceType] = useState({}) //선택된 면조도
const [installHeight, setInstallHeight] = useState() //설치 높이
const [standardWindSpeed, setStandardWindSpeed] = useState({}) //기준풍속
const [verticalSnowCover, setVerticalSnowCover] = useState() //수직적설량
const [installHeight, setInstallHeight] = useState(managementState?.installHeight) //설치 높이
const [standardWindSpeed, setStandardWindSpeed] = useState() //기준풍속
const [verticalSnowCover, setVerticalSnowCover] = useState(managementState?.verticalSnowCover) //수직적설량
const [selectedModules, setSelectedModules] = useRecoilState(selectedModuleState) //선택된 모듈
const [moduleSelectionInitParams, setModuleSelectionInitParams] = useRecoilState(moduleSelectionInitParamsState) //모듈 기본 데이터 ex) 면조도, 높이등등
// const [moduleSelectionInitParams, setModuleSelectionInitParams] = useRecoilState(moduleSelectionInitParamsState) //모듈 기본 데이터 ex) 면조도, 높이등등
const { getModuleTypeItemList } = useMasterController()
const { findCommonCode } = useCommonCode()
const resetStatisticsData = useResetRecoilState(moduleStatisticsState)
const { restoreModuleInstArea } = useModuleBasicSetting()
const bindInitData = () => {
@ -43,21 +40,21 @@ export function useModuleSelection(props) {
//탭별 파라메터 초기화
useEffect(() => {
bindInitData()
const initParams = {
illuminationTp: managementState?.surfaceTypeValue, //면조도
illuminationTpNm: managementState?.surfaceType, //면조도명
instHt: managementState?.installHeight, //설치높이
stdWindSpeed: managementState?.standardWindSpeedId, //기준풍속
stdSnowLd: managementState?.verticalSnowCover, //기준적설량
saleStoreNorthFlg: managementState?.saleStoreNorthFlg, //북쪽 설치 여부
}
// const initParams = {
// illuminationTp: managementState?.surfaceTypeValue, //면조도
// illuminationTpNm: managementState?.surfaceType, //면조도명
// instHt: managementState?.installHeight, //설치높이
// stdWindSpeed: managementState?.standardWindSpeedId, //기준풍속
// stdSnowLd: managementState?.verticalSnowCover, //기준적설량
// saleStoreNorthFlg: managementState?.saleStoreNorthFlg, //북쪽 설치 여부
// }
if (selectedModules) {
initParams.moduleTpCd = selectedModules.itemTp
initParams.moduleItemId = selectedModules.itemId
}
// if (selectedModules) {
// initParams.moduleTpCd = selectedModules.itemTp
// initParams.moduleItemId = selectedModules.itemId
// }
setModuleSelectionInitParams(initParams)
// setModuleSelectionInitParams(initParams)
}, [managementState])
useEffect(() => {
@ -87,7 +84,7 @@ export function useModuleSelection(props) {
//새로고침시 데이터 날아가는거 방지
if (managementState === null) {
setManagementState(managementStateLoaded)
setManagementState(managementState)
} else {
bindInitData()
}
@ -95,7 +92,7 @@ export function useModuleSelection(props) {
getModuleData(roofsIds)
//모듈설치면 초기화
restoreModuleInstArea()
// restoreModuleInstArea()
resetStatisticsData()
}, [])
@ -125,19 +122,19 @@ export function useModuleSelection(props) {
setSelectedModules(option) //선택값 저장
//init 데이터에 선택된 모듈 추가
setModuleSelectionInitParams({
...moduleSelectionInitParams,
moduleTpCd: option.itemTp,
moduleItemId: option.itemId,
})
// setModuleSelectionInitParams({
// ...moduleSelectionInitParams,
// moduleTpCd: option.itemTp,
// moduleItemId: option.itemId,
// })
}
const handleChangeSurfaceType = (option) => {
setModuleSelectionInitParams({
...moduleSelectionInitParams,
illuminationTp: option.clCode,
illuminationTpNm: option.clCodeNm,
})
// setModuleSelectionInitParams({
// ...moduleSelectionInitParams,
// illuminationTp: option.clCode,
// illuminationTpNm: option.clCodeNm,
// })
setManagementState({
...managementState,
@ -147,10 +144,10 @@ export function useModuleSelection(props) {
}
const handleChangeWindSpeed = (option) => {
setModuleSelectionInitParams({
...moduleSelectionInitParams,
stdWindSpeed: option.clCode,
})
// setModuleSelectionInitParams({
// ...moduleSelectionInitParams,
// stdWindSpeed: option.clCode,
// })
setManagementState({
...managementState,
@ -160,10 +157,10 @@ export function useModuleSelection(props) {
const handleChangeInstallHeight = (option) => {
setInstallHeight(option)
setModuleSelectionInitParams({
...moduleSelectionInitParams,
instHt: option,
})
// setModuleSelectionInitParams({
// ...moduleSelectionInitParams,
// instHt: option,
// })
setManagementState({
...managementState,
@ -173,10 +170,10 @@ export function useModuleSelection(props) {
const handleChangeVerticalSnowCover = (option) => {
setVerticalSnowCover(option)
setModuleSelectionInitParams({
...moduleSelectionInitParams,
stdSnowLd: option,
})
// setModuleSelectionInitParams({
// ...moduleSelectionInitParams,
// stdSnowLd: option,
// })
setManagementState({
...managementState,
@ -184,53 +181,23 @@ export function useModuleSelection(props) {
})
}
useEffect(() => {
// console.log('installHeight', installHeight)
}, [installHeight])
useEffect(() => {
// console.log('verticalSnowCover', verticalSnowCover)
}, [verticalSnowCover])
//TODO: 설치높이, 기준적설량 debounce 적용해서 추가해야됨
// useEffect(() => {
// getConstructionListData(constructionListParams)
// }, [constructionListParams])
// const getConstructionListData = async (params) => {
// if (params.trestleMkrCd && params.constMthdCd && params.roofBaseCd) {
// const optionsList = await getConstructionList(params)
// console.log('optionsList', optionsList)
// setConstructionList(optionsList.data)
// }
// }
//state 배열에 데이터 추가 함수
// const addRoofTabParams = (key, value, excludeArray = []) => {
// const index = roofTabParams.findIndex((obj) => obj.roofTab === roofTab)
// if (index !== -1) {
// roofTabParams[index][key] = value
// if (excludeArray.length > 0) {
// excludeArray.forEach((exclude) => {
// roofTabParams[index][exclude] = ''
// })
// }
// setRoofTabParams((prev) => [...prev.slice(0, index), roofTabParams[index], ...prev.slice(index + 1)])
// }
// }
return {
moduleSelectionInitParams,
// moduleSelectionInitParams,
selectedModules,
roughnessCodes,
windSpeedCodes,
managementState,
setManagementState,
moduleList,
setSelectedModules,
selectedSurfaceType,
setSelectedSurfaceType,
installHeight,
setInstallHeight,
standardWindSpeed,
setStandardWindSpeed,
verticalSnowCover,
setVerticalSnowCover,
handleChangeModule,
handleChangeSurfaceType,
handleChangeWindSpeed,

View File

@ -0,0 +1,302 @@
import { use, useContext, useEffect, useReducer, useState } from 'react'
import { useCommonCode } from '../common/useCommonCode'
import { useMasterController } from '../common/useMasterController'
import { selectedModuleState } from '@/store/selectedModuleOptions'
import { useRecoilState, useRecoilValue } from 'recoil'
import { GlobalDataContext } from '@/app/GlobalDataProvider'
import { popSpinnerState } from '@/store/popupAtom'
const RAFT_BASE_CODE = '203800'
const trestleReducer = (state, action) => {
switch (action.type) {
case 'SET_LENGTH':
case 'SET_RAFT_BASE':
case 'SET_TRESTLE_MAKER':
case 'SET_CONST_MTHD':
case 'SET_ROOF_BASE':
case 'SET_CONSTRUCTION':
case 'SET_TRESTLE_DETAIL':
return {
...action.roof,
}
case 'SET_INITIALIZE':
return {
moduleTpCd: action.roof.module?.itemTp ?? '',
roofMatlCd: action.roof?.roofMatlCd ?? '',
hajebichi: action.roof?.hajebichi ?? 0,
raft: action.roof?.raft ?? null,
trestleMkrCd: action.roof.trestle?.trestleMkrCd ?? null,
constMthdCd: action.roof.trestle?.constMthdCd ?? null,
constTp: action.roof.construction?.constTp ?? null,
roofBaseCd: action.roof.trestle?.roofBaseCd ?? null,
workingWidth: action.roof.workingWidth ?? 0,
lengthBase: action.roof?.length ?? 0,
illuminationTp: action.roof.common?.illuminationTp ?? null,
instHt: action.roof.common?.instHt ?? null,
stdWindSpeed: action.roof.common?.stdWindSpeed ?? null,
stdSnowLd: action.roof.common?.stdSnowLd ?? null,
inclCd: action.roof?.pitch ?? null,
roofPitch: action.roof?.roofPchBase ?? 0,
eavesMargin: action.roof?.eavesMargin ?? null,
ridgeMargin: action.roof?.ridgeMargin ?? null,
kerabaMargin: action.roof?.kerabaMargin ?? null,
}
default:
return state
}
}
export function useModuleTrestle(props) {
const { selectedRoof } = props
const { findCommonCode } = useCommonCode()
const [raftBaseList, setRaftBaseList] = useState([])
const [trestleList, setTrestleList] = useState([])
const [constMthdList, setConstMthdList] = useState([])
const [roofBaseList, setRoofBaseList] = useState([])
const [constructionList, setConstructionList] = useState([])
const { getTrestleList, getConstructionList, getTrestleDetailList } = useMasterController()
const [lengthBase, setLengthBase] = useState(0)
const [hajebichi, setHajebichi] = useState(0)
const [cvrYn, setCvrYn] = useState('N')
const [cvrChecked, setCvrChecked] = useState(false)
const [snowGdPossYn, setSnowGdPossYn] = useState('N')
const [snowGdChecked, setSnowGdChecked] = useState(false)
const [eavesMargin, setEavesMargin] = useState(0)
const [ridgeMargin, setRidgeMargin] = useState(0)
const [kerabaMargin, setKerabaMargin] = useState(0)
const [trestleState, dispatch] = useReducer(trestleReducer, null)
const [trestleDetail, setTrestleDetail] = useState(null)
const [popSpinnerStore, setPopSpinnerStore] = useRecoilState(popSpinnerState)
useEffect(() => {
const raftCodeList = findCommonCode(RAFT_BASE_CODE)
setRaftBaseList(raftCodeList)
setTrestleList([])
setConstMthdList([])
setRoofBaseList([])
setConstructionList([])
// setEavesMargin(selectedRoof?.addRoof?.eavesMargin ?? 0)
// setRidgeMargin(selectedRoof?.addRoof?.ridgeMargin ?? 0)
// setKerabaMargin(selectedRoof?.addRoof?.kerabaMargin ?? 0)
setHajebichi(selectedRoof?.hajebichi ?? 0)
setEavesMargin(selectedRoof?.eavesMargin ?? 0)
setRidgeMargin(selectedRoof?.ridgeMargin ?? 0)
setKerabaMargin(selectedRoof?.kerabaMargin ?? 0)
setLengthBase(Math.round(selectedRoof?.length ?? 0))
setCvrYn(selectedRoof?.construction?.cvrYn ?? 'N')
setCvrChecked(selectedRoof?.construction?.cvrChecked ?? false)
setSnowGdPossYn(selectedRoof?.construction?.snowGdPossYn ?? 'N')
setSnowGdChecked(selectedRoof?.construction?.snowGdChecked ?? false)
setTrestleDetail(selectedRoof?.trestleDetail)
}, [selectedRoof])
useEffect(() => {
if (trestleState) {
handleSetTrestleList()
if (!trestleState?.trestleMkrCd) {
setConstMthdList([])
setRoofBaseList([])
setConstructionList([])
setTrestleDetail(null)
setEavesMargin(0)
setRidgeMargin(0)
setKerabaMargin(0)
return
}
handleSetConstMthdList()
if (!trestleState?.constMthdCd) {
setRoofBaseList([])
setConstructionList([])
setTrestleDetail(null)
setEavesMargin(0)
setRidgeMargin(0)
setKerabaMargin(0)
return
}
handleSetRoofBaseList()
if (!trestleState?.roofBaseCd) {
setConstructionList([])
setTrestleDetail(null)
setEavesMargin(0)
setRidgeMargin(0)
setKerabaMargin(0)
return
}
handleSetConstructionList()
if (!trestleState?.constTp) {
setTrestleDetail(null)
setEavesMargin(0)
setRidgeMargin(0)
setKerabaMargin(0)
return
}
if (!trestleState?.eavesMargin) {
handleSetTrestleDetailData()
}
}
}, [trestleState])
const handleSetTrestleList = () => {
setPopSpinnerStore(true)
getTrestleList({
moduleTpCd: trestleState?.moduleTpCd ?? '',
roofMatlCd: trestleState?.roofMatlCd ?? '',
raftBaseCd: trestleState?.raft ?? '',
})
.then((res) => {
if (res?.data) setTrestleList(res.data)
setPopSpinnerStore(false)
})
.catch((e) => {
setPopSpinnerStore(false)
})
}
const handleSetConstMthdList = () => {
setPopSpinnerStore(true)
getTrestleList({
moduleTpCd: trestleState?.moduleTpCd ?? '',
roofMatlCd: trestleState?.roofMatlCd ?? '',
raftBaseCd: trestleState?.raft ?? '',
trestleMkrCd: trestleState?.trestleMkrCd ?? '',
})
.then((res) => {
if (res?.data) setConstMthdList(res.data)
setPopSpinnerStore(false)
})
.catch((e) => {
setPopSpinnerStore(false)
})
}
const handleSetRoofBaseList = () => {
setPopSpinnerStore(true)
getTrestleList({
moduleTpCd: trestleState?.moduleTpCd ?? '',
roofMatlCd: trestleState?.roofMatlCd ?? '',
raftBaseCd: trestleState?.raft ?? '',
trestleMkrCd: trestleState?.trestleMkrCd ?? '',
constMthdCd: trestleState?.constMthdCd ?? '',
})
.then((res) => {
if (res?.data) setRoofBaseList(res.data)
setPopSpinnerStore(false)
})
.catch((e) => {
setPopSpinnerStore(false)
})
}
const handleSetConstructionList = () => {
setPopSpinnerStore(true)
getConstructionList({
moduleTpCd: trestleState?.moduleTpCd ?? '',
roofMatlCd: trestleState?.roofMatlCd ?? '',
trestleMkrCd: trestleState?.trestleMkrCd ?? '',
constMthdCd: trestleState?.constMthdCd ?? '',
roofBaseCd: trestleState?.roofBaseCd ?? '',
illuminationTp: trestleState.illuminationTp ?? '',
instHt: trestleState.instHt ?? '',
stdWindSpeed: trestleState.stdWindSpeed ?? '',
stdSnowLd: trestleState.stdSnowLd ?? '',
inclCd: trestleState.inclCd ?? '',
raftBaseCd: trestleState.raft ?? '',
roofPitch: trestleState.hajebichi ? trestleState.hajebichi : (trestleState.roofPitch ?? ''),
})
.then((res) => {
if (res?.data) setConstructionList(res.data)
setPopSpinnerStore(false)
})
.catch((e) => {
setPopSpinnerStore(false)
})
}
const handleSetTrestleDetailData = () => {
setPopSpinnerStore(true)
getTrestleDetailList([
{
moduleTpCd: trestleState.moduleTpCd ?? '',
roofMatlCd: trestleState.roofMatlCd ?? '',
trestleMkrCd: trestleState.trestleMkrCd ?? '',
constMthdCd: trestleState.constMthdCd ?? '',
roofBaseCd: trestleState.roofBaseCd ?? '',
illuminationTp: trestleState.illuminationTp ?? '',
instHt: trestleState.instHt ?? '',
stdWindSpeed: trestleState.stdWindSpeed ?? '',
stdSnowLd: trestleState.stdSnowLd ?? '',
inclCd: trestleState.inclCd ?? '',
constTp: trestleState.constTp ?? '',
mixMatlNo: trestleState.mixMatlNo ?? '',
roofPitch: trestleState.hajebichi ? trestleState.hajebichi : (trestleState.roofPitch ?? ''),
// workingWidth: trestleState.length ?? '',
workingWidth: lengthBase ?? '',
},
])
.then((res) => {
if (res.length > 0) {
if (!res[0].data) return
setEavesMargin(res[0].data.eaveIntvl)
setRidgeMargin(res[0].data.ridgeIntvl)
setKerabaMargin(res[0].data.kerabaIntvl)
setTrestleDetail(res[0].data)
// dispatch({
// type: 'SET_TRESTLE_DETAIL',
// roof: {
// ...trestleState,
// eavesMargin: res[0].data.eaveIntvl,
// ridgeMargin: res[0].data.ridgeIntvl,
// kerabaMargin: res[0].data.kerabaIntvl,
// },
// })
}
setPopSpinnerStore(false)
})
.catch((e) => {
setPopSpinnerStore(false)
})
}
return {
trestleState,
trestleDetail,
dispatch,
raftBaseList,
trestleList,
constMthdList,
roofBaseList,
constructionList,
handleSetTrestleList,
handleSetConstMthdList,
handleSetRoofBaseList,
handleSetConstructionList,
handleSetTrestleDetailData,
lengthBase,
setLengthBase,
hajebichi,
setHajebichi,
cvrYn,
cvrChecked,
snowGdPossYn,
snowGdChecked,
eavesMargin,
ridgeMargin,
kerabaMargin,
setEavesMargin,
setRidgeMargin,
setKerabaMargin,
setCvrYn,
setCvrChecked,
setSnowGdPossYn,
setSnowGdChecked,
}
}

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