Compare commits

...
520 Commits
Author SHA1 Message Date
jcchoi 74934d4559 수정
Deploy Frontend / deploy (push) Successful in 4m0s
2026-10-22 14:28:31 +09:00
jcchoi 027f88da6c 대시보드 ㅁ이상치리포트 명칭 변경 2026-10-22 14:24:21 +09:00
changwng 6dc2965209 [as fr] 필수첨부 파일에 필수파일검토내용에 CRUD 처리 및 AI 요청시 같이 전송처리
Deploy Frontend / deploy (push) Successful in 4m5s
2026-10-21 16:14:39 +09:00
jcchoi c69c1aa855 Merge branch 'iipbs/st/dev'
Deploy Frontend / deploy (push) Successful in 9m0s
2026-10-21 13:15:44 +09:00
jcchoi 9f0e03c151 이상치 선별보고서 2026-10-21 11:31:50 +09:00
jcchoi 471a431af5 대시보드 진행상태 변경 2026-10-21 10:37:32 +09:00
jcchoi 26d8410649 Merge remote-tracking branch 'origin/iipbs/st/dev'
Deploy Frontend / deploy (push) Successful in 5m0s
2026-10-20 21:52:31 +09:00
jcchoi 8403ad556c 설치인가 버튼 이벤트 수정 2026-10-20 21:50:41 +09:00
rsone001 d26c15f857 Merge branch 'iipbs/st/dev' of https://www.onasys.co.kr:3000/ONAANDALL/frontend into iipbs/st/dev 2026-10-20 17:49:42 +09:00
rsone001 86ed664e3e [input] - 오류 로직 수정 2026-10-20 17:49:09 +09:00
jcchoi a105f395dd 설치인가 프로세스 변경 2026-10-20 15:50:05 +09:00
jcchoi a5ebc12580 검토의견 수정 오류 처리 2026-10-20 13:48:39 +09:00
jcchoi 270f6e366f Merge branch 'iipbs/st/dev' 2026-10-20 10:53:41 +09:00
jcchoi acf215c1bf 대시보드 이상치선별보고서 수정 2026-10-20 10:45:29 +09:00
jcchoi 8a98cd7d19 행정지원 검토의견 수정 2026-10-20 10:04:42 +09:00
jcchoi 3a5b48b608 통계 도움말 오류 수정 2026-10-20 10:04:26 +09:00
jcchoi d735ad7dd7 통계 파라미터 변경
Deploy Frontend / deploy (push) Successful in 1m57s
2026-10-19 21:28:01 +09:00
jcchoi 25dd0f1504 getVerifyResultList 파라미터 변경
Deploy Frontend / deploy (push) Successful in 2m2s
2026-10-19 19:17:01 +09:00
jcchoi 8e14c6cef5 Merge remote-tracking branch 'origin/iipbs/st/dev' 2026-10-19 19:07:14 +09:00
jcchoi 9f7abe4413 필수첨부파일 위치 업데이트 2026-10-19 18:41:58 +09:00
rsone001 de730205af [ST] - 일반 검증 등록 및 수정 다이얼로그 css 수정 2026-10-19 17:50:49 +09:00
rsone001 a873f0e0dc [ST] - api 표준화 누락 수정 2026-10-19 15:14:58 +09:00
jcchoi 32e3765b52 초안 평가 단계 수정
Deploy Frontend / deploy (push) Successful in 1m53s
2026-10-15 19:56:54 +09:00
rsone001 4f62095e8e [ST] - fix: input 2-3 검증 cell 스타일 로직 수정 2026-10-15 16:03:40 +09:00
jcchoi 5234448ae5 기본계획 조회 전체 적용
Deploy Frontend / deploy (push) Successful in 1m40s
2026-10-15 15:53:38 +09:00
jcchoi f284c67988 hwp 서버 도메인 연결 2026-10-15 10:43:15 +09:00
jcchoi 2d77813dab Merge remote-tracking branch 'origin/iipbs/st/dev'
Deploy Frontend / deploy (push) Successful in 4m31s
2026-10-14 17:36:08 +09:00
rsone001 d22a255143 [ST] - fix: type 오류 수정 2026-10-14 17:35:32 +09:00
jcchoi 6703bbeeec Merge remote-tracking branch 'origin/iipbs/st/dev' 2026-10-14 17:25:16 +09:00
rsone001 d444c591ec [ST] - flx: input 표준화에 맞춰 소스 수정 2026-10-14 17:05:45 +09:00
rsone001 f7ddb6cae0 Merge remote-tracking branch 'origin/feature/sein' into iipbs/st/dev 2026-10-14 15:39:21 +09:00
yangsein 7ffc02e887 refactor: 대시보드 통계처리현황 표준화 수정 2026-10-14 15:17:25 +09:00
yangsein cbacb27bb1 refactor: 행정동 갱신 표준화 수정 2026-10-14 15:16:40 +09:00
100JM 03a08a320a [ST] - fix: DB 표준화에 따른 소스 수정 2026-10-14 14:24:03 +09:00
jcchoi 45223be64a 포탈 수정
Deploy Frontend / deploy (push) Successful in 3m45s
2026-10-13 11:44:05 +09:00
jcchoi b22dfdfa4c 웹기안기 도메인 적용 2026-10-13 11:22:08 +09:00
jcchoi ce219f0e40 https 적용
Deploy Frontend / deploy (push) Successful in 4m5s
2026-10-13 10:53:39 +09:00
jcchoi a0f26d38b2 행정지원 리스트 디자인 cs 와 통일
Deploy Frontend / deploy (push) Successful in 3m42s
2026-10-12 19:19:13 +09:00
jcchoi 4efdcfe0fc 공통 테마 적용
Deploy Frontend / deploy (push) Successful in 7m55s
2026-10-12 18:47:26 +09:00
jcchoi 806ccf950f 행정지원 대시보드 테마 적용 2026-10-12 18:38:00 +09:00
jcchoi b96b78fa0b skin 및 레이아웃 작업 2026-10-12 17:22:37 +09:00
jcchoi f816bf005e 파일보안 간격 조절
Deploy Frontend / deploy (push) Successful in 9m15s
2026-10-12 11:54:55 +09:00
jcchoi 791ad6ce37 파일보안 줄바꿈 2026-10-12 11:50:50 +09:00
jcchoi 4bc802cee2 파일보완 내용 자동 줄빠굼 2026-10-12 10:55:19 +09:00
jcchoi 4662c8c896 선형 구조 수정 2026-10-12 10:50:09 +09:00
jcchoi 7058560a03 메뉴 배치 수정 2026-10-12 10:09:52 +09:00
jcchoi 79a5999ad5 콘텐츠의 선형 구조 2026-10-12 10:09:39 +09:00
jcchoi 1aeded5da2 페이지 타이틀 2026-10-12 10:09:16 +09:00
jcchoi e8d1d882c2 페이지 타이틀 수정 2026-10-12 10:01:22 +09:00
jcchoi 39dca810a5 css 오류 수정
Deploy Frontend / deploy (push) Successful in 7m50s
2026-10-11 17:58:09 +09:00
jcchoi 97f94688f1 Html 문법 오류
Deploy Frontend / deploy (push) Successful in 7m57s
2026-10-11 17:36:43 +09:00
jcchoi 0b502cbc99 접근성 체크 박스 (적절한 링크 텍스트)
Deploy Frontend / deploy (push) Successful in 7m28s
2026-10-11 17:09:37 +09:00
jcchoi a8eb8f9ab1 로고 alt 작업
Deploy Frontend / deploy (push) Successful in 7m43s
2026-10-11 16:16:14 +09:00
jcchoi 6471f4365a 접근성 2026-10-11 16:14:08 +09:00
jcchoi 48999f5699 웹 접근성 조치
Deploy Frontend / deploy (push) Successful in 9m6s
2026-10-11 15:32:56 +09:00
jcchoi 5ad64a596b 담당자 정보 caption 수정 2026-10-11 14:27:59 +09:00
jcchoi df60bd0b40 Merge branch 'main' of https://www.onasys.co.kr:3000/ONAANDALL/frontend
Deploy Frontend / deploy (push) Successful in 7m13s
2026-10-11 13:27:16 +09:00
jcchoi 41e6f1cf48 Table Cpation 처리 2026-10-11 13:16:56 +09:00
jcchoi 0802ba191a DataTable Caption 추가 2026-10-11 12:58:04 +09:00
mind-one005 ad57ed241d Merge branch 'iipbs/sn/dev' 2026-10-08 13:29:37 +09:00
jcchoi f17cb420e3 LNB GNB 탭 포커스 2026-10-07 21:42:43 +09:00
jcchoi c2396f093b 필수입력 안내 메세지
Deploy Frontend / deploy (push) Successful in 8m31s
2026-10-07 19:43:06 +09:00
jcchoi bdf65dcfd1 설치인가 사업구분 팝업 사이즈 조정 2026-10-07 19:42:42 +09:00
jcchoi 2aefbe17ee 웹접근성 2차 수정 2026-10-07 19:42:20 +09:00
mind-one005 5e43d69d8e sn: null 인경우, 공백으로 보이도록 2026-10-07 15:05:40 +09:00
jcchoi 4482b92ccd 메뉴간 탭이동
Deploy Frontend / deploy (push) Successful in 7m33s
2026-10-06 17:41:39 +09:00
jcchoi d3c39caefb 웹 접근성 조치 2026-10-06 14:32:26 +09:00
jcchoi 24c5dce24d 대체 텍스트 적용 2026-10-06 13:18:58 +09:00
mind-one005 9971e512c8 Merge branch 'iipbs/sn/dev' of https://www.onasys.co.kr:3000/ONAANDALL/frontend into iipbs/sn/dev 2026-10-06 13:15:48 +09:00
mind-one005 0c77ed9bba sn: 도시침수예방사업 사업명 필수 2026-10-06 13:15:32 +09:00
kiminchan e759c96ade Merge branch 'iipbs/sn/dev' of https://www.onasys.co.kr:3000/ONAANDALL/frontend into iipbs/sn/dev 2026-10-06 13:06:20 +09:00
kiminchan f602b75268 페이지별 도움말 조회를 위한 메뉴 코드 전달 누락 수정 2026-10-06 13:06:01 +09:00
jcchoi d4d9eaf773 CM 로그인
Deploy Frontend / deploy (push) Successful in 5m32s
하단 메뉴 링크
2026-10-06 11:13:46 +09:00
jcchoi 68a800cdfc 메인페이지 탭이동
Deploy Frontend / deploy (push) Successful in 2m54s
2026-10-02 21:52:10 +09:00
jcchoi ff45d1a875 스킵 네비게이션 2026-10-02 21:51:56 +09:00
mind-one005 11d0378060 Merge branch 'iipbs/sn/dev' of https://www.onasys.co.kr:3000/ONAANDALL/frontend into iipbs/sn/dev 2026-10-02 21:26:43 +09:00
jcchoi 49000c6140 FAQ 웹접근성(툴팁) 2026-10-02 21:25:44 +09:00
jcchoi 24bba484ca Merge branch 'main' into deploy
Deploy Frontend / deploy (push) Successful in 3m49s
2026-10-02 19:43:52 +09:00
jcchoi c8d0d85937 Merge branch 'deploy' of https://www.onasys.co.kr:3000/ONAANDALL/frontend into deploy 2026-10-02 19:42:49 +09:00
jcchoi c692f7c647 Merge branch 'main' of https://www.onasys.co.kr:3000/ONAANDALL/frontend 2026-10-02 19:40:06 +09:00
jcchoi 080463458b 도움말 표시 2026-10-02 19:38:00 +09:00
jcchoi 08aaf4dd62 누락 리소스 추가 2026-10-02 19:29:13 +09:00
mind-one005 8ed4f959cb sn: 확률강우량 모달 추가 2026-10-02 18:12:03 +09:00
mind-one005 e5f7778861 sn: 확률강우량 모달 추가 2026-10-02 18:07:00 +09:00
chanii8 ad1e517472 Merge branch 'main' into deploy
Deploy Frontend / deploy (push) Successful in 3m56s
2026-10-02 17:55:30 +09:00
chanii8 17ed5eb46b Merge branch 'iipbs/as/dev' 2026-10-02 17:54:53 +09:00
chanii8 1a2faf02da [as] feat: 알림톡발송 페이지 구현 2026-10-02 17:54:25 +09:00
kiminchan 2d086a05b2 조사사업·준설 등록 필수 표시 및 숫자 입력 제한 보완 2026-10-02 17:11:04 +09:00
kiminchan 6ef4603a7c 지도 대상 선택 이동 시 지역·시설 검색조건 초기화 2026-10-02 16:51:47 +09:00
chanii8 d6522338aa [as] chore: 행정지원 숫자 폼 최대자릿수 제한 2026-10-02 16:37:26 +09:00
chanii8 df154a45b6 Merge branch 'main' into deploy
Deploy Frontend / deploy (push) Successful in 4m3s
2026-10-02 15:16:46 +09:00
chanii8 c5dbea334d Merge branch 'iipbs/as/dev' 2026-10-02 15:15:36 +09:00
chanii8 85f535eb5d [as] chore: 행정지원 숫자 폼 음수 입력 제한 2026-10-02 15:14:22 +09:00
chanii8 9334514c71 [as] chore: 행정지원 폼 사업기간 스키마 수정 2026-10-02 14:55:42 +09:00
chanii8 d438755616 Merge branch 'main' into deploy
Deploy Frontend / deploy (push) Successful in 7m37s
2026-10-02 13:12:44 +09:00
chanii8 54595fb7b6 Merge remote-tracking branch 'origin/iipbs/st/dev' 2026-10-02 13:07:41 +09:00
chanii8 4ee8d5a099 Merge branch 'deploy' of https://www.onasys.co.kr:3000/ONAANDALL/frontend into deploy 2026-10-02 13:05:57 +09:00
chanii8 abe5fae4c4 Merge branch 'iipbs/as/dev' 2026-10-02 13:03:56 +09:00
chanii8 aeb48661b1 [as] chore: 보완 요청 사항 적용
- 사업기간 폼 포매팅
2026-10-02 13:03:24 +09:00
jcchoi 4bdf68c2b6 좌측메뉴 탭 이동 처리 2026-10-02 11:39:49 +09:00
chanii8 c5a0e378fd [as] feat: 보완 요청 사항 적용
- 실무협의서 내 보완요청 버튼 추가
- 대시보드 수정
2026-10-02 11:37:45 +09:00
jcchoi 538f656640 Merge branch 'main' into deploy
Deploy Frontend / deploy (push) Canceled after 6m27s
2026-10-01 22:25:22 +09:00
jcchoi a323c42acf 테이블에 scope="col" 및 캡션 추가
Deploy Frontend / deploy (push) Successful in 4m16s
2026-10-01 22:15:11 +09:00
jcchoi 095101ad2f 필수항목 tooltip 2026-10-01 22:14:28 +09:00
jcchoi 7ee5ebd506 글자 확대 축소 2026-10-01 20:26:45 +09:00
jcchoi f90ef6297d 상단메뉴 클릭 표준 2026-10-01 18:03:03 +09:00
chanii8 4167107a91 [as] feat: 파일 업로드 에러 로직 수정 2026-10-01 16:41:10 +09:00
kiminchan 90c1cb1f52 Merge branch 'iipbs/sn/dev' 2026-10-01 16:40:10 +09:00
mind-one001 8c614b84a7 merge(sn): preserve modular map entry with remote import cleanup 2026-10-01 16:22:02 +09:00
kiminchan 7d1a501ad8 관망도 페이지 import 병합 충돌 해소 2026-10-01 16:03:12 +09:00
mind-one001 48589274c8 merge(sn): integrate latest survey layers and upload guidance 2026-10-01 15:59:43 +09:00
kiminchan 83f7676db4 조사 레이어 표출 및 조사결과 업로드 안내 개선 2026-10-01 15:55:29 +09:00
mind-one001 c8b30810f6 style(sn): format integrated map modules 2026-10-01 15:54:43 +09:00
mind-one001 f71fa7d97a merge(sn): retain remote layer and flood updates after refactoring 2026-10-01 15:53:30 +09:00
mind-one001 4c7c4c00ab refactor(sn): modularize map and features and standardize file naming 2026-10-01 15:47:57 +09:00
chanii8 d2645a9c08 [as] feat: 파일 업로드 확장자 제한 정책 적용 2026-10-01 15:36:02 +09:00
mind-one005 ae341d66ba sn: 침수예측 오버레이 수정 2026-10-01 15:02:53 +09:00
chanii8 fce4def951 [as] feat: 행정지원 숫자 폼 소숫점 자리 제한 2026-10-01 14:26:22 +09:00
chanii8 ba0964f772 [as] feat: useTus > handleError 수정 2026-10-01 13:44:46 +09:00
kiminchan e346417c29 Merge branch 'iipbs/sn/dev' 2026-10-01 10:15:58 +09:00
kiminchan 5fcc184a88 노후관로 레이어를 DB 설정 기반으로 전환 2026-10-01 09:46:16 +09:00
mind-one005 f6d3f89b5b Merge branch 'iipbs/sn/dev' of https://www.onasys.co.kr:3000/ONAANDALL/frontend into iipbs/sn/dev 2026-10-01 00:26:53 +09:00
mind-one005 106e09a7a2 sn: 도시침수예방사업 지정일 수정 2026-10-01 00:26:36 +09:00
mind-one005 d1b4600392 sn: 도시침수예방사업 수정화면 사업순번 단계 수정 불가 2026-10-01 00:16:07 +09:00
kiminchan 4204dfa624 Merge branch 'iipbs/sn/dev' of https://www.onasys.co.kr:3000/ONAANDALL/frontend into iipbs/sn/dev 2026-09-30 18:15:15 +09:00
kiminchan d7fd5be068 관로분석 및 통수위험 기본 조회조건 개선 2026-09-30 18:13:31 +09:00
mind-one005 735bb397ec sn: 침수예측현황 분석시각 조건 초기화 2026-09-30 18:09:20 +09:00
mind-one005 8b5a2b713c sn: 도시침수지도 자동선택 2026-09-30 17:53:50 +09:00
rsone001 f2ec9327fd feat: [ST] - eval 제거 2026-09-30 17:17:01 +09:00
yangsein 5331e10c96 [refactor] 대시보드 - 통계처리현황 반려되어 제출중인 상태 구분 처리 포함 2026-09-30 16:59:05 +09:00
chanii8 7090f02a52 [as] feat: 보완 요청 사항 반영 (260930_1128) 2026-09-30 11:28:28 +09:00
chanii8 12152a6810 [as] chore: 파일보완 다이얼로그 컨텐츠 최대 높이 지정 2026-09-29 17:41:37 +09:00
chanii8 c0c2d3d1c9 Merge remote-tracking branch 'origin/iipbs/as/dev' 2026-09-29 17:32:18 +09:00
chanii8 858844e81a [as] chore: 설계자문 진행상태 모델 수정 2026-09-29 16:58:01 +09:00
chanii8 1ff2731121 Merge branch 'iipbs/cs/dev' 2026-09-29 15:37:15 +09:00
chanii8 70db7662bb [cs] chore: 모듈API관리 폼 수정 2026-09-29 15:36:37 +09:00
chanii8 bb57ff72e7 [cs] feat: 모듈API관리 "API URL" 검색 조건 추가 2026-09-29 15:19:42 +09:00
chanii8 8df79fd624 Merge branch 'main' into deploy
Deploy Frontend / deploy (push) Successful in 3m47s
2026-09-29 14:40:09 +09:00
chanii8 7946c08c72 Merge branch 'iipbs/as/dev' 2026-09-29 14:29:32 +09:00
chanii8 6f20ef7273 [as] feat: 보완 요청 사항 반영 (260929_1429) 2026-09-29 14:29:13 +09:00
mind-one005 657ae77dde Merge branch 'iipbs/sn/dev' 2026-09-29 14:03:36 +09:00
mind-one005 4ff80dd257 cm: 유역별 정보 수정 2026-09-29 11:38:47 +09:00
mind-one005 9bb1069d26 cm: 노후 하수관로 현황 수정 2026-09-29 11:17:52 +09:00
chanii8 730be25948 Merge remote-tracking branch 'origin/main' into deploy
Deploy Frontend / deploy (push) Successful in 5m4s
2026-09-28 17:17:08 +09:00
chanii8 78d8fea7f5 Merge branch 'iipbs/as/dev' 2026-09-28 16:44:05 +09:00
chanii8 22ed78c87b [as] feat: 행정지원 페이지 useSearchParamsState 적용 2026-09-28 16:37:20 +09:00
mind-one005 f5331d4c50 cm: 유역별 정보 div 수정 2026-09-28 16:32:16 +09:00
mind-one005 f32fe0ec5b cm: 유역별 정보 2026-09-28 16:07:52 +09:00
chanii8 3b914fbc2e [as] feat: 보완 요청 사항 반영 (20260928_1521) 2026-09-28 15:21:19 +09:00
kiminchan 533df0eb16 Merge branch 'iipbs/sn/dev' 2026-09-28 13:23:36 +09:00
chanii8 9c38165afb Merge branch 'main' into deploy
Deploy Frontend / deploy (push) Successful in 7m25s
2026-09-28 11:24:12 +09:00
mind-one001 7b506320b4 서버페이징 변경 2026-09-28 11:22:22 +09:00
chanii8 2b7c497c3e Merge branch 'iipbs/as/dev' 2026-09-28 11:20:57 +09:00
chanii8 4dee1309eb [as] feat: 보완 요청 사항 반영 (260928_1120) 2026-09-28 11:20:31 +09:00
kiminchan f3f8909e45 Merge branch 'iipbs/sn/dev' 2026-09-28 10:00:24 +09:00
mind-one001 d45cd79422 테스트코드 삭제 2026-09-23 16:00:28 +09:00
mind-one001 7cbb58ff32 노후관로 조회 기능 보완 2026-09-23 14:58:14 +09:00
chanii8 9242e1fb47 Merge branch 'main' into deploy
Deploy Frontend / deploy (push) Successful in 12m36s
2026-09-23 14:44:23 +09:00
chanii8 f5ce74d0ee Merge remote-tracking branch 'origin/iipbs/as/dev' 2026-09-23 14:39:57 +09:00
chanii8 3114280e83 [as] chore: 알림이력 배지 크기 조정 2026-09-23 14:39:24 +09:00
chanii8 c7b8b36f4a Merge branch 'iipbs/common/dev' 2026-09-23 14:17:15 +09:00
chanii8 60ee194cc9 [common/components] design: NoticePopover 스타일 수정 2026-09-23 13:55:16 +09:00
mind-one001 4f50593eac Merge branch 'iipbs/sn/dev' of https://www.onasys.co.kr:3000/ONAANDALL/frontend into iipbs/sn/dev 2026-09-23 13:50:59 +09:00
mind-one001 8818dd21c6 관망도 레이어 전체켜기 기능 추가 2026-09-23 13:50:50 +09:00
chanii8 db8ee298d9 Merge branch 'iipbs/cs/dev' 2026-09-23 11:40:04 +09:00
chanii8 9eef99a435 [cs] chore: 게시판 댓글 사용 여부 적용 2026-09-23 11:39:32 +09:00
chanii8 3d027ed374 [common/features] chore: 게시판 댓글 사용 여부 적용 2026-09-23 11:37:16 +09:00
chanii8 271b5d445d [as] chore: 재원협의 버튼 수정
Deploy Frontend / deploy (push) Successful in 3m57s
2026-09-23 11:12:26 +09:00
chanii8 2457d14ae9 Merge branch 'main' into deploy
Deploy Frontend / deploy (push) Successful in 6m32s
2026-09-23 09:51:39 +09:00
chanii8 d70eb17a72 Merge branch 'iipbs/as/dev' 2026-09-23 09:50:30 +09:00
chanii8 ddd619babc [as] feat: 행정지원 문서 체크리스트 레이블 변경 2026-09-23 09:48:08 +09:00
chanii8 5d27ee830e [as] feat: 행정지원 문서 체크리스트 탭 노출 알고리즘 수정 2026-09-23 09:35:35 +09:00
mind-one005 b73b83a49f Merge branch 'iipbs/sn/dev' of https://www.onasys.co.kr:3000/ONAANDALL/frontend into iipbs/sn/dev 2026-09-23 09:08:05 +09:00
mind-one005 549debf8bf sn: 노후하수관로정비사업 검색조건 추가 2026-09-23 09:07:56 +09:00
mind-one005 212b9a43a9 Merge branch 'iipbs/wr/dev' 2026-09-22 20:49:40 +09:00
mind-one005 f092a23ee4 wr: font 크기 조정 2026-09-22 20:45:22 +09:00
chanii8 25c357a423 [as] chore: 보완요청 섹션 첨부파일 관련 코드 수정 2026-09-22 16:21:13 +09:00
chanii8 a77246a1db [as] fix: 타입 에러 2026-09-22 15:39:06 +09:00
chanii8 f00ce31648 [as] refactor: zod 스키마 .optional()-> .nullish() 변경 2026-09-22 15:12:28 +09:00
chanii8 dc57a491dd [as] chore: 재원협의 코드 수정 2026-09-22 15:02:36 +09:00
mind-one005 c9b7266959 wr: 재이용실적 수정 2026-09-22 11:24:32 +09:00
chanii8 0dd2111cb9 [as] refactor: 재원협의 폼 useWatch + Controller 방식 제거 2026-09-22 10:57:41 +09:00
mind-one005 c39b048398 Merge branch 'iipbs/wr/dev' 2026-09-22 10:25:49 +09:00
chanii8 87fbf15e2b [as] feat: 재원협의 수정 2026-09-22 10:23:10 +09:00
mind-one005 db1cfaa1a6 wr: label 위치 조정 2026-09-22 10:05:49 +09:00
mind-one005 905b750877 Merge branch 'iipbs/sn/dev' 2026-09-22 09:28:50 +09:00
chanii8 9739609de4 Merge branch 'main' into deploy
Deploy Frontend / deploy (push) Successful in 4m15s
2026-09-21 17:51:47 +09:00
chanii8 20f7d595a1 Merge branch 'iipbs/as/dev' 2026-09-21 17:50:59 +09:00
chanii8 e909dc81ad [as] chore: 재원협의 버튼 노출 조건 수정 2026-09-21 17:50:23 +09:00
chanii8 a510779ea1 [as] feat: AI 만족도 평가 기능 구현 2026-09-21 17:24:01 +09:00
chanii8 c799c199c5 Merge branch 'main' into deploy
Deploy Frontend / deploy (push) Successful in 8m28s
2026-09-21 16:58:49 +09:00
chanii8 0818e9ade2 Merge branch 'iipbs/as/dev' 2026-09-21 16:57:46 +09:00
chanii8 e60a8bed8b fix: 빌드 에러 2026-09-21 16:57:16 +09:00
chanii8 17fdcd7d1f fix: 빌드 에러 2026-09-21 16:56:46 +09:00
chanii8 6412e1a997 Merge branch 'iipbs/as/dev' 2026-09-21 16:36:58 +09:00
chanii8 1674598480 [as] design: RHFDevtool 스타일 수정 2026-09-21 16:36:28 +09:00
chanii8 23ebf36244 [as] feat: "@hookform/devtools" 추가 2026-09-21 16:04:19 +09:00
chanii8 342db06685 [as] refactor: 행정지원 문서 저장 payload 스키마 수정 2026-09-21 15:18:42 +09:00
100JM 2c355525df [il] - DB 표준화로 인한 소스 수정 2026-09-21 13:55:37 +09:00
chanii8 bd20a499d3 [as] feat: AI 수동 검증 버튼 추가 2026-09-21 13:34:26 +09:00
chanii8 5fec5d5239 [as] feat: RAG 인덱싱 상태일 때 검토자 지정 버튼 분기 처리 2026-09-21 11:33:59 +09:00
chanii8 5b3aaa463d Merge branch 'main' into deploy
Deploy Frontend / deploy (push) Successful in 3m27s
2026-09-21 11:03:48 +09:00
chanii8 a14b22b1ef Merge branch 'iipbs/as/dev' 2026-09-21 11:02:55 +09:00
chanii8 50aa549311 [as] feat: 공단 실무협의서 관리 버튼 노출 조건 수정 2026-09-21 11:02:22 +09:00
chanii8 947dba5934 Merge branch 'main' into deploy
Deploy Frontend / deploy (push) Successful in 3m43s
2026-09-18 17:38:37 +09:00
chanii8 572ef04bb4 Merge branch 'iipbs/as/dev' 2026-09-18 17:37:31 +09:00
chanii8 110d6c689c [as] feat: UploadedFileChip 컴포넌트 props 추가(onDownload) 2026-09-18 17:33:45 +09:00
chanii8 f40d6fd5e8 [as] feat: AttachmentDownloadLink 컴포넌트 구현 및 적용 2026-09-18 16:51:00 +09:00
chanii8 cc3bc548c9 [as] feat: 재원협의 코드 수정 2026-09-18 16:27:51 +09:00
chanii8 4816a512bf Merge branch 'main' into deploy
Deploy Frontend / deploy (push) Successful in 3m40s
2026-09-17 17:08:09 +09:00
chanii8 cc3d7ccd7b Merge remote-tracking branch 'origin/iipbs/as/dev' 2026-09-17 17:05:08 +09:00
chanii8 43bc7db7d5 [as] chore: useAplnRows 수정 2026-09-17 17:04:31 +09:00
chanii8 b0672aead5 Merge branch 'iipbs/as/dev' 2026-09-17 16:48:50 +09:00
chanii8 086d5a18e8 [as] chore: 재원협의 코드 수정 2026-09-17 16:48:13 +09:00
chanii8 41643563f8 [as] chore: 수정사항 반영 2026-09-17 10:55:51 +09:00
chanii8 b6b58eaeba feat: 재원협의&실무협의서 로직 사이클 2026-09-17 10:00:00 +09:00
chanii8 e8a5701e73 [as] feat: 웹기안기 다이얼로그 내 금액검증결과 섹션 구현 2026-09-16 17:37:44 +09:00
100JM 241222dcd1 [il] - 연계 db 표준화로 인한 소스 수정 2026-09-16 14:52:38 +09:00
chanii8 380575dae0 [as] feat: 실무협의서 검토 로직(RR, RK) 작업 2026-09-16 14:19:39 +09:00
chanii8 5e54039028 [as] feat: 실무협의서 검토자 지정(EV) 로직 작업 2026-09-15 16:42:21 +09:00
chanii8 4fad163b14 Merge branch 'main' into deploy
Deploy Frontend / deploy (push) Successful in 4m4s
2026-09-14 17:56:23 +09:00
chanii8 4d20a68f93 Merge branch 'iipbs/as/dev' 2026-09-14 17:52:41 +09:00
chanii8 90a1d6fdff [as] feat: 웹기안기 '열기' 기능 추가(개발용) 2026-09-14 17:52:16 +09:00
chanii8 00e1cb707e [as] feat: 재원협의 환경청 조치결과 검토 의뢰(RA) 로직 작업 2026-09-14 17:35:00 +09:00
chanii8 6eeb774a21 [as] feat: 재원협의 지자체 조치완료(RB) 로직 작업 2026-09-14 13:36:23 +09:00
chanii8 6c4b8a52dc [as] chore: 보완요청 사항 적용 2026-09-14 10:16:16 +09:00
chanii8 a7e0d8f826 [as] feat: 첨부파일 컴포넌트 생성 및 적용 2026-09-11 17:23:45 +09:00
chanii8 d1ccddc1d3 [as] feat: 재원협의 기술검토 로직 작업 2026-09-11 17:02:09 +09:00
chanii8 da2191f365 Merge branch 'main' into deploy
Deploy Frontend / deploy (push) Successful in 3m43s
2026-09-11 10:38:12 +09:00
chanii8 056643bd96 Merge branch 'iipbs/as/dev' 2026-09-11 10:36:03 +09:00
chanii8 01d5ed297e [as] chore: 재원협의 상세화면 게이트 수정 2026-09-11 10:35:33 +09:00
chanii8 55ab2b8562 Merge remote-tracking branch 'origin/main' into deploy
Deploy Frontend / deploy (push) Successful in 3m33s
2026-09-10 17:53:02 +09:00
chanii8 dae5d0a9f5 Merge remote-tracking branch 'origin/iipbs/as/dev' 2026-09-10 17:52:46 +09:00
chanii8 41ad33310a [as] feat: 실무협의서 관리 다이얼로그 내 첨부파일 섹션 추가 2026-09-10 17:50:56 +09:00
chanii8 526392c370 Merge branch 'main' into deploy
Deploy Frontend / deploy (push) Successful in 3m39s
2026-09-10 15:44:08 +09:00
chanii8 b38ee9c7c7 Merge branch 'iipbs/as/dev' 2026-09-10 15:43:30 +09:00
chanii8 9f7f1db9b8 [as] feat: 기안기 저장 코드 수정 2026-09-10 14:50:09 +09:00
chanii8 6020c80a7a Merge branch 'main' into deploy
Deploy Frontend / deploy (push) Successful in 10m6s
2026-09-09 18:09:33 +09:00
chanii8 a2a165a42b Merge branch 'iipbs/as/dev' 2026-09-09 18:08:35 +09:00
chanii8 0f27c1e5ce [as] chore: 웹기안기 다이얼로그 수정 2026-09-09 18:08:08 +09:00
chanii8 e944ad4369 [as] feat: 실무협의서 웹기안기에 렌더링 2026-09-09 17:38:40 +09:00
chanii8 768ad001d2 [as] feat: 재원협의 쿼리 무효화 로직 수정 2026-09-09 10:30:29 +09:00
mind-one005 866d11988d Merge branch 'iipbs/sn/dev' 2026-09-09 09:27:53 +09:00
mind-one005 eb4f6f1b09 Merge branch 'iipbs/sn/dev' 2026-09-08 22:29:39 +09:00
chanii8 32b778f961 Merge branch 'main' into deploy
Deploy Frontend / deploy (push) Successful in 3m47s
2026-09-08 17:14:51 +09:00
chanii8 17b1c12286 Merge branch 'iipbs/st/dev' 2026-09-08 17:01:32 +09:00
chanii8 c79824e358 [as] feat: 보완요청 파일 첨부 로직 수정 2026-09-08 17:01:02 +09:00
rsone001 de8971bea6 [ST] fix: 대시보드 통계 처리 현황 panel css 수정 2026-09-08 13:43:26 +09:00
chanii8 c47c130fc3 [as] feat: 파일보완 요청 기능 구현 2026-09-08 10:29:27 +09:00
chanii8 3946fda013 Merge branch 'main' into deploy
Deploy Frontend / deploy (push) Successful in 4m42s
2026-09-07 17:27:45 +09:00
chanii8 db30b45267 Merge branch 'deploy' of https://www.onasys.co.kr:3000/ONAANDALL/frontend into deploy 2026-09-07 17:27:38 +09:00
chanii8 01377b6013 Merge remote-tracking branch 'origin/iipbs/st/dev' 2026-09-07 17:26:39 +09:00
chanii8 b38be38169 [as] feat: AI 자동 진행 단계 대기 상태 텍스트 추가 2026-09-07 17:25:40 +09:00
chanii8 b9ac4a1c18 [as] feat: 재원협의 서비스(fnrsCnsltnService) 수정 2026-09-07 17:05:59 +09:00
chanii8 dbea497cc5 [as] feat: 웹한글 기안기 데모 페이지 생성 2026-09-07 15:24:06 +09:00
chanii8 94af73a810 [as] docs: 실무협의 로직 개발 가이드 추가 2026-09-07 15:22:21 +09:00
rsone001 a7fd6a8e16 [ST] - fix: 취약점 점검 관련 조치 2026-09-07 14:26:00 +09:00
rsone001 8039ab8281 [ST] - refactor: 2-5 맨홀 지표 변경된 테이블에 맞게 수정, 일반검증 항목 입력기간일 시 등록/수정 불가 2026-09-04 16:05:26 +09:00
chanii8 2bfe653ac0 [as] feat: 'dcrag-to' API 구현 2026-09-04 15:18:32 +09:00
kiminchan b4982e07b8 Merge branch 'iipbs/sn/dev' 2026-09-04 10:22:18 +09:00
jcchoi 6cc2a02df4 Merge branch 'main' into deploy
Deploy Frontend / deploy (push) Successful in 1m9s
2026-09-03 18:31:48 +09:00
jcchoi 351e1091ce Merge remote-tracking branch 'origin/iipbs/st/dev'
Deploy Frontend / deploy (push) Successful in 5m9s
2026-09-03 18:23:16 +09:00
rsone001 794691f05e [ST] - fix: 제출결과(검증)화면에 시설명 컬럼 추가 2026-09-03 16:29:40 +09:00
rsone001 e58b84a01b [ST] - fix: input 제출 - 시계열 검증 요청 response & request 수정, 대시보드 구성 수정 2026-09-03 15:27:19 +09:00
rsone001 3d51f003c0 Merge remote-tracking branch 'origin/feature/sein' into iipbs/st/dev 2026-09-03 13:52:34 +09:00
yangsein b5a99e5f59 [feat] 대쉬 보드 일정관리 제거 , 통계 현황상태 추가 2026-09-03 13:44:09 +09:00
rsone001 afca685fd4 [ST] - fix: 2-3 매설연장 관리자일시 수정 가능하도록 적용, SearchFrom 로직 수정 2026-09-03 11:26:23 +09:00
rsone001 261941452a [ST] fix - 2-2 지표 컬럼 설정 및 저장 제출 로직 수정 2026-09-02 16:41:12 +09:00
chanii8 0f107ff29d Merge branch 'main' into deploy
Deploy Frontend / deploy (push) Successful in 6m16s
2026-09-02 16:32:42 +09:00
chanii8 a860650dfb Merge remote-tracking branch 'origin/iipbs/as/dev' 2026-09-02 16:24:39 +09:00
chanii8 63d2dd1f4f [as] feat: 결재선 지정 API 파라미터 추가 2026-09-02 11:54:52 +09:00
kiminchan 8d765c2825 Merge branch 'iipbs/sn/dev' 2026-09-02 09:43:30 +09:00
chanii8 5e69a9b6f0 Merge branch 'main' into deploy
Deploy Frontend / deploy (push) Successful in 3m44s
2026-09-01 17:20:52 +09:00
chanii8 47cb11d07b Merge remote-tracking branch 'origin/iipbs/st/dev' 2026-09-01 17:19:02 +09:00
rsone001 8576ef83b5 [ST] - refactor: 6-1 중심처리시설 row는 위탁계약기간 직접 수정 못하도록 적용 2026-09-01 16:25:55 +09:00
rsone001 e80b8953a0 [ST] - fix: 일반검증 수식 사용자 직접입력 등록시 공백제거 2026-09-01 14:41:49 +09:00
chanii8 ec9311c76b Merge branch 'main' into deploy
Deploy Frontend / deploy (push) Successful in 3m36s
2026-08-31 17:23:27 +09:00
chanii8 1d1141b59a Merge branch 'iipbs/as/dev' 2026-08-31 17:22:46 +09:00
chanii8 8fd1d59fe2 [as] feat: 파일 양식 다운로드 API 수정 2026-08-31 17:18:39 +09:00
chanii8 4c85a7d249 Merge branch 'main' into deploy
Deploy Frontend / deploy (push) Successful in 3m37s
2026-08-28 17:45:20 +09:00
chanii8 f54ce13711 Merge branch 'iipbs/as/dev' 2026-08-28 17:44:06 +09:00
chanii8 fb12eadf65 [as] feat: 필수첨부파일 섹션 수정(파일 양식 컬럼) 2026-08-28 17:43:47 +09:00
chanii8 09d4c2d26e Merge branch 'main' into deploy
Deploy Frontend / deploy (push) Successful in 4m6s
2026-08-28 17:13:45 +09:00
chanii8 bb3179f0e4 Merge remote-tracking branch 'origin/iipbs/st/dev' 2026-08-28 17:12:54 +09:00
rsone001 e8b309460e [ST] input 특광역시 로직 수정, 일반검증 엑셀 다운로드 추가, 모든 number 타입 컬럼에 footer 추가 2026-08-28 17:08:20 +09:00
chanii8 b8dac5dc72 Merge branch 'main' into deploy
Deploy Frontend / deploy (push) Successful in 3m47s
2026-08-28 17:08:08 +09:00
chanii8 ed0736dcde Merge branch 'iipbs/as/dev' 2026-08-28 17:06:34 +09:00
chanii8 edc5bb52af Merge branch 'iipbs/st/dev' 2026-08-28 17:06:20 +09:00
chanii8 37ac927c45 [st] feat: 시계열 검토 이력 > 지자체 진입 시 시도/시군구 자동 선택 2026-08-28 17:05:40 +09:00
chanii8 bf9a34cbe8 [st] feat: 하수도 통계 검증 > 시도/시군구 셀렉트에 '전체' 옵션 추가 2026-08-28 15:55:05 +09:00
chanii8 fc961e2946 [as] feat: 필수첨부파일관리 등록 다이얼로그 > 양식파일 첨부 로직 수정 2026-08-28 15:14:44 +09:00
chanii8 73cafc9786 Merge remote-tracking branch 'origin/main' into deploy
Deploy Frontend / deploy (push) Successful in 3m40s
2026-08-28 14:34:02 +09:00
chanii8 e401455f31 Merge remote-tracking branch 'origin/iipbs/as/dev' 2026-08-28 14:32:59 +09:00
chanii8 254986b48b [as] 필수펌부파일관리 내 양식파일 컬럼 추가 2026-08-28 14:31:54 +09:00
chanii8 4c1615853c [as] docs: 실무협의서 개발가이드 문서 추가 2026-08-28 11:11:32 +09:00
chanii8 c276ae0c5e Merge branch 'main' into deploy
Deploy Frontend / deploy (push) Successful in 4m2s
2026-08-27 17:25:06 +09:00
chanii8 f67e065c5e Merge branch 'iipbs/st/dev' of https://www.onasys.co.kr:3000/ONAANDALL/frontend 2026-08-27 17:23:40 +09:00
rsone001 18a11ab5fa [ST] - refactor: 입력화면 전용 시도 조회 api 추가 2026-08-27 17:22:34 +09:00
chanii8 6a7eb1f584 Merge branch 'iipbs/st/dev' of https://www.onasys.co.kr:3000/ONAANDALL/frontend 2026-08-27 17:17:25 +09:00
rsone001 214ff1a435 [ST] - fix: 일반 검증 타입 총계 검증일 시 group by 항목 렌더링 되지 않게 수정 2026-08-27 15:44:45 +09:00
rsone001 d7cc579335 [ST] - refactor: 지난검증결과 불러오기 필드 추가(시설명) 2026-08-27 15:32:12 +09:00
rsone001 9b53c1ae08 [ST] - fix: 일반검증 항목 관리, 참조 지표(컬럼) 오류 로직 수정 2026-08-27 13:07:33 +09:00
chanii8 b4ed061a53 Merge branch 'main' into deploy
Deploy Frontend / deploy (push) Successful in 3m40s
2026-08-27 11:36:49 +09:00
chanii8 4840a95097 Merge branch 'iipbs/as/dev' of https://www.onasys.co.kr:3000/ONAANDALL/frontend 2026-08-27 11:31:13 +09:00
chanii8 08a7a4d6bc [as] feat: 재원협의 내 실무협의서 관련 버튼 추가 2026-08-27 11:30:52 +09:00
chanii8 ddaaa8fc92 Merge branch 'main' into deploy
Deploy Frontend / deploy (push) Successful in 3m55s
2026-08-26 17:14:14 +09:00
chanii8 c02ac6f425 Merge branch 'iipbs/as/dev' of https://www.onasys.co.kr:3000/ONAANDALL/frontend 2026-08-26 17:12:36 +09:00
chanii8 8bda55c653 [as] feat: 행정지원 신청 패널 전체접기/펼침 기능 2026-08-26 14:04:36 +09:00
chanii8 cff7b7da30 [as] feat: 행정지원 패널 전체접기/펼침 기능 2026-08-26 11:04:37 +09:00
chanii8 3a253df58a Merge branch 'main' into deploy
Deploy Frontend / deploy (push) Successful in 7m10s
2026-08-25 17:11:23 +09:00
chanii8 197c2583ed Merge remote-tracking branch 'origin/iipbs/st/dev' 2026-08-25 17:06:40 +09:00
chanii8 10f5d42122 Merge branch 'iipbs/as/dev' 2026-08-25 17:06:19 +09:00
chanii8 816b6b2ca8 [as] chore: 재원협의 폼 페이지 내 쿼리 무효화 코드 수정 2026-08-25 16:58:31 +09:00
rsone001 64ed53cf4d [ST] refactor: input - 자동 계산 cell 부동소수점 문제 해결을 위해 decimal.js 라이브러리 추가 및 계산 로직 수정, 6-2 지표 로직 수정 2026-08-25 16:04:31 +09:00
mind-one005 c8e9fc54ed Merge branch 'iipbs/wr/dev' 2026-08-25 15:48:05 +09:00
chanii8 21debe2f77 Merge branch 'main' into deploy
Deploy Frontend / deploy (push) Successful in 3m22s
2026-08-25 15:45:10 +09:00
chanii8 6927f77f86 Merge branch 'iipbs/as/dev' 2026-08-25 15:44:20 +09:00
chanii8 9c86058fe1 [as] chore: 설계자문 삭제 시 목록 쿼리 무효화 2026-08-25 15:43:47 +09:00
chanii8 c4291ceda5 [as] refactor: 행정지원 쿼리키 관리 방식 변경 2026-08-25 15:29:27 +09:00
chanii8 8dfd7cc290 [as] feat: 설치인가 저장 시 목록/상세 쿼리 무효화 2026-08-25 15:01:19 +09:00
chanii8 0e4f662dfd Merge branch 'main' into deploy
Deploy Frontend / deploy (push) Successful in 4m19s
2026-08-25 10:34:34 +09:00
chanii8 e5498ef04b Merge branch 'iipbs/as/dev' 2026-08-25 10:33:14 +09:00
chanii8 7baebf2cbc [as] feat: 부동소수점 처리 2026-08-25 10:31:40 +09:00
chanii8 a4c14433cd [as] feat: 행정지원 문서 파일 첨부 로직 수정 2026-08-25 09:42:05 +09:00
rsone001 043fd3d4a8 [ST] - 입력 6-1 공공하수처리시설 컬럼 설정 변경 2026-08-24 17:19:33 +09:00
chanii8 4a797808c1 Merge branch 'main' into deploy
Deploy Frontend / deploy (push) Successful in 4m1s
2026-08-24 17:18:45 +09:00
jcchoi eda0b6b813 Merge remote-tracking branch 'origin/iipbs/st/dev' 2026-08-24 16:51:28 +09:00
rsone001 6dd09971f1 [ST] - 컬럼 자동 계산식 오류 수정 2026-08-24 16:39:24 +09:00
chanii8 23424b5eee [st] chore: 통계 검증 리포트 초기 조회 파라미터 수정 2026-08-24 14:09:12 +09:00
chanii8 4bd5276fc5 [as] rename: 스펙 문서 폴더 이동 2026-08-24 09:32:46 +09:00
chanii8 92ed3233af [as] design: 대시보드 스타일 수정 2026-08-24 09:21:30 +09:00
chanii8 3a81b822f2 Merge branch 'main' into deploy
Deploy Frontend / deploy (push) Successful in 3m54s
2026-08-21 17:29:09 +09:00
chanii8 2c1e4d18fc Merge branch 'iipbs/st/dev' 2026-08-21 17:28:11 +09:00
chanii8 3f190dfff6 Merge branch 'iipbs/st/dev' of https://www.onasys.co.kr:3000/ONAANDALL/frontend into iipbs/st/dev 2026-08-21 17:27:37 +09:00
chanii8 6cc02da7a4 [st] feat: 통계 검증 리포트 목록 조회 API 수정 2026-08-21 17:27:30 +09:00
rsone001 c87ccf8788 [ST] - fix: 입력 저장&제출시 빈값 null처리 2026-08-21 14:56:34 +09:00
chanii8 dcaa2d5ef3 Merge branch 'iipbs/as/dev' 2026-08-21 11:42:18 +09:00
chanii8 e0715ce177 [as] feat: 증감액, 증감률 서버 데이터 매핑(사업규모, 총사업비, 재원분담) 2026-08-21 11:38:43 +09:00
chanii8 3333f1c8e6 [as] style: 행정지원 문서 진행 상태 테이블 스타일 수정 2026-08-21 09:59:14 +09:00
rsone001 8e757ab9db Merge branch 'iipbs/st/dev' of https://www.onasys.co.kr:3000/ONAANDALL/frontend into iipbs/st/dev 2026-08-20 16:31:13 +09:00
rsone001 de7e66afb4 [ST] - fix: 11-4 데이터 체크 수정 2026-08-20 16:31:00 +09:00
chanii8 63050b0efa [as] design: 대시보드 전국 행정지원 지도 레이아웃 구현 2026-08-20 15:13:18 +09:00
chanii8 f5a7efb324 Merge branch 'main' into deploy
Deploy Frontend / deploy (push) Successful in 4m2s
2026-08-20 13:12:39 +09:00
chanii8 c4592a026a Merge branch 'iipbs/st/dev' 2026-08-20 13:08:24 +09:00
chanii8 c981b83284 Merge branch 'iipbs/st/dev' of https://www.onasys.co.kr:3000/ONAANDALL/frontend into iipbs/st/dev 2026-08-20 13:07:50 +09:00
chanii8 927bb7cb3a [st] chore: eslint 해결 2026-08-20 11:48:18 +09:00
chanii8 725976b5c0 [st] feat: 시도/시군구 조회 쿼리 변경 2026-08-20 11:47:50 +09:00
rsone001 cdbe6a855f [ST] - fix: 검토 하수관로 컬럼 수정 및 엑셀 내보내기 셀 스타일 수정 2026-08-19 12:31:37 +09:00
chanii8 a1c96e9e98 Merge branch 'iipbs/st/dev' of https://www.onasys.co.kr:3000/ONAANDALL/frontend into iipbs/st/dev 2026-08-19 11:58:06 +09:00
chanii8 1488805242 [as] feat: 통계 검증 리포트 새로고침 버튼 추가 2026-08-19 11:57:58 +09:00
chanii8 cf681125a6 [as] docs: 문서 수정 2026-08-19 11:40:13 +09:00
chanii8 abc3ca184d Merge branch 'main' into deploy
Deploy Frontend / deploy (push) Successful in 4m49s
2026-08-18 18:03:01 +09:00
chanii8 7dcef46829 Merge branch 'iipbs/st/dev' of https://www.onasys.co.kr:3000/ONAANDALL/frontend 2026-08-18 18:02:05 +09:00
chanii8 36a7654973 Merge branch 'iipbs/as/dev' 2026-08-18 18:01:37 +09:00
rsone001 5b12f81b8f [ST] - fix: 통계 집계표 조회 기본값 수정, 엑셀 다운로드 스타일 적용, 요약표 마우스 툴팁 추가 / 입력단 오류 수정 및 검토단 컬럼 설정 수정 2026-08-18 17:44:22 +09:00
chanii8 7c88b609f7 [as] feat: 대시보드 공지사항 섹션 구현 2026-08-18 17:35:54 +09:00
chanii8 5af4f0f27b [as] feat: 대시보드 페이지 구현 2026-08-18 16:40:21 +09:00
chanii8 267c022d58 Merge branch 'main' into deploy
Deploy Frontend / deploy (push) Successful in 3m36s
2026-08-13 17:52:39 +09:00
chanii8 cf115b769f Merge branch 'iipbs/as/dev' 2026-08-13 17:50:18 +09:00
chanii8 774144846d [as] chore: 하수도기본계획 게이트 코드 수정 2026-08-13 17:49:57 +09:00
chanii8 0fc2844928 Merge branch 'main' into deploy
Deploy Frontend / deploy (push) Successful in 3m56s
2026-08-13 17:22:37 +09:00
chanii8 3b2f6c386d Merge branch 'iipbs/st/dev' of https://www.onasys.co.kr:3000/ONAANDALL/frontend 2026-08-13 17:21:09 +09:00
chanii8 987e5fcbfd [as] chore: 알림이력 notiUrl 리졸버 추가 2026-08-13 17:20:30 +09:00
chanii8 335872eaec [as] feat: 행정지원 문서 상세페이지 게이트 구현 2026-08-13 17:11:13 +09:00
rsone001 df24e7c2a8 [ST] - fix: 일반 검증 수식 안내 문구 수정 2026-08-13 16:20:54 +09:00
rsone001 3b5fee047b [ST] - fix: qa 조치, 일반 검증 개발 수정 2026-08-13 15:30:00 +09:00
chanii8 9bcc8b95e7 [as] feat: 알림이력 페이지 구현 2026-08-13 11:51:21 +09:00
jcchoi 9ea3474d43 Merge branch 'main' of https://www.onasys.co.kr:3000/ONAANDALL/frontend 2026-08-11 18:00:02 +09:00
jcchoi a26bd5ea5b 이미지 정리 2026-08-11 17:59:52 +09:00
chanii8 7f1fe42ace Merge branch 'main' of https://www.onasys.co.kr:3000/ONAANDALL/frontend into deploy
Deploy Frontend / deploy (push) Successful in 3m36s
2026-08-11 17:46:50 +09:00
chanii8 4ee0eab653 Merge branch 'iipbs/as/dev' of https://www.onasys.co.kr:3000/ONAANDALL/frontend 2026-08-11 17:45:56 +09:00
chanii8 fe92ba3c27 [as] feat: 내행정지원 페이지 2026-08-11 17:45:28 +09:00
chanii8 e8870d3bcb [as] chore: 불필요한 상수 제거 2026-08-11 11:43:18 +09:00
jcchoi 9152782a65 Merge branch 'main' of https://www.onasys.co.kr:3000/ONAANDALL/frontend 2026-08-11 11:14:20 +09:00
jcchoi 0dc378285c 전화번호 수정 2026-08-11 11:14:08 +09:00
chanii8 c83a13d680 Merge branch 'main' into deploy
Deploy Frontend / deploy (push) Successful in 4m7s
2026-08-10 18:06:03 +09:00
chanii8 403946c50a Merge branch 'iipbs/st/dev' 2026-08-10 18:01:40 +09:00
chanii8 bdc7c64c61 Merge branch 'iipbs/as/dev' 2026-08-10 18:01:33 +09:00
rsone001 181730bcde [ST] - fix: 입력: 새로고침 시 열려있는 모든 텝의 공통 api가 실행되는 현상 수정, 하수도보급률 저장&제출 request 수정(외국인총인구 제거, 총면적 추가), 검증 툴팁 max-width 추가, 지난 검증 결과 및 제출 결과 데이터 마우스 툴팁 추가 2026-08-10 17:58:05 +09:00
chanii8 d79e0e585a [as] feat: 행정지원 업무별 상세페이지 내 진행상태 섹션 추가 2026-08-10 17:57:39 +09:00
chanii8 678af2f06e [st] chore: 승인 정보 입력 폼 수정 2026-08-10 15:48:19 +09:00
chanii8 dbda77a75b [as] feat: 결재현황 ui 수정 2026-08-10 15:27:53 +09:00
chanii8 0459c0b231 [as] feat: 행정지원 업무별 목록 내 진행상황 ui 수정 2026-08-10 10:26:27 +09:00
rsone001 82d67bb4c6 [ST] - fix: 검증 항목 명칭 변경 2026-08-07 17:22:08 +09:00
chanii8 3d7dcb6d0e Merge branch 'main' into deploy
Deploy Frontend / deploy (push) Successful in 4m17s
2026-08-07 17:03:11 +09:00
chanii8 ed54fe9646 Merge branch 'iipbs/st/dev' of https://www.onasys.co.kr:3000/ONAANDALL/frontend 2026-08-07 17:02:06 +09:00
rsone001 85ef1805e5 [ST] - 공단 오류 및 개선 요청사항 처리 2026-08-07 16:24:39 +09:00
chanii8 07a625e03b [as] rename: SectionCollapsible 컴포넌트 shared 레이어로 이동 2026-08-07 15:34:26 +09:00
chanii8 a46f24d6d7 Merge branch 'main' into deploy
Deploy Frontend / deploy (push) Successful in 3m44s
2026-08-06 17:12:47 +09:00
chanii8 ee19f9ca59 Merge branch 'iipbs/cm/dev' 2026-08-06 17:11:52 +09:00
chanii8 b9e9814664 Merge branch 'iipbs/st/dev' 2026-08-06 17:11:50 +09:00
chanii8 8303fa3860 Merge branch 'iipbs/as/dev' 2026-08-06 17:11:38 +09:00
chanii8 6d9042ac0f [cm] typo: 404 페이지 문구 수정 2026-08-06 17:06:58 +09:00
chanii8 26f64988ef [st] typo: 404 페이지 문구 수정 2026-08-06 17:06:00 +09:00
chanii8 12a0a37d24 [as] typo: 404 페이지 문구 수정 2026-08-06 17:04:56 +09:00
chanii8 2e4f2428f0 Merge branch 'main' into deploy
Deploy Frontend / deploy (push) Successful in 4m41s
2026-08-06 16:50:33 +09:00
chanii8 c8c11b19de Merge branch 'iipbs/as/dev' 2026-08-06 16:49:45 +09:00
chanii8 45a500817f [as] chore: 검토인력관리 인원배치 api 수정 2026-08-06 16:39:36 +09:00
chanii8 706cebfeae Merge branch 'main' into deploy 2026-08-06 16:22:09 +09:00
chanii8 8a352a8e69 Merge remote-tracking branch 'origin/iipbs/as/dev' 2026-08-06 16:18:00 +09:00
chanii8 999fd610af [as] feat: 결재선 검증 로직 수정 2026-08-06 16:17:44 +09:00
chanii8 dd6aff5ebf Merge remote-tracking branch 'origin/iipbs/as/dev' 2026-08-06 15:52:02 +09:00
chanii8 bc3c841b89 Merge remote-tracking branch 'origin/iipbs/st/dev' 2026-08-06 15:51:58 +09:00
chanii8 99ea453d23 Merge remote-tracking branch 'origin/iipbs/cm/dev' 2026-08-06 15:51:46 +09:00
chanii8 71389a104c Merge remote-tracking branch 'origin/iipbs/cs/dev' 2026-08-06 15:51:34 +09:00
chanii8 6e2ebe9025 [as] feat: 결재선관리 > 결재구분 변경 시 결재자 초기화 2026-08-06 15:39:33 +09:00
chanii8 29e39f98af Merge branch 'iipbs/st/dev' of https://www.onasys.co.kr:3000/ONAANDALL/frontend into iipbs/st/dev 2026-08-06 15:15:09 +09:00
chanii8 5192f3e3c0 [st] feat: 404 페이지 구현 2026-08-06 15:15:04 +09:00
chanii8 95c8416754 [cm] feat: 404 페이지 구현 2026-08-06 15:14:46 +09:00
chanii8 b102c4feba [as] feat: 404 페이지 구현 2026-08-06 15:14:26 +09:00
chanii8 daff1610e3 [cs] feat: 404 페이지 구현 2026-08-06 15:12:48 +09:00
chanii8 ee7f5e3a07 Merge branch 'main' into deploy 2026-08-06 13:42:33 +09:00
chanii8 054dcc4eee [common/components] chore: 모듈 참조 경로 수정 2026-08-06 13:40:02 +09:00
chanii8 1f1f72451b [common/components] feat: 페이지 타이틀 내 도움말 다이얼로그 구현 2026-08-06 13:37:31 +09:00
chanii8 3d0160f21f Merge branch 'main' into deploy 2026-08-05 17:23:37 +09:00
chanii8 ae51b8273f Merge branch 'iipbs/as/dev' 2026-08-05 17:22:50 +09:00
chanii8 defe2a8e9b [as] fix: 타입 에러 2026-08-05 17:22:28 +09:00
chanii8 28aa932676 Merge branch 'iipbs/as/dev' 2026-08-05 17:20:23 +09:00
chanii8 4aa2d0a62c [as] chore: 행정지원 메뉴 수정 2026-08-05 17:18:38 +09:00
chanii8 1d8d9c7b3d [as] feat: 행정지원 메뉴(설치인가, 재원협의, 설계자문, 하수도기본계획) 수정 2026-08-05 15:58:48 +09:00
rsone001 83de317308 Merge branch 'iipbs/st/dev' of https://www.onasys.co.kr:3000/ONAANDALL/frontend into iipbs/st/dev 2026-08-05 10:09:58 +09:00
rsone001 84b1fd07ae [ST] - management service code refresh 로직 추가 2026-08-05 10:08:26 +09:00
chanii8 e105f52fea [as] feat: 기본계획 상세 페이지 구현 2026-08-04 17:22:21 +09:00
chanii8 0aa0126caf Merge branch 'main' into deploy
Deploy Frontend / deploy (push) Successful in 5m43s
2026-08-03 17:47:39 +09:00
chanii8 f637f096f9 chore: x-device-id 제거 코드 수정 (domain 옵션 추가) 2026-08-03 17:45:10 +09:00
chanii8 5ecdd37187 Merge branch 'main' into deploy
Deploy Frontend / deploy (push) Successful in 3m52s
2026-08-03 17:28:33 +09:00
chanii8 7f497f60af Merge branch 'iipbs/st/dev' 2026-08-03 17:24:35 +09:00
chanii8 7a019b063b Merge branch 'iipbs/cs/dev' 2026-08-03 17:24:30 +09:00
chanii8 e5e20a789a Merge branch 'iipbs/as/dev' 2026-08-03 17:24:24 +09:00
chanii8 8883f5ba55 Merge branch 'iipbs/cm/dev' 2026-08-03 17:24:18 +09:00
chanii8 d64a694e20 docs: authService 리팩토링 문서 업데이트 2026-08-03 17:24:02 +09:00
chanii8 a88f8ab33e [st] feat: 로그아웃 라우트 핸들러 적용 2026-08-03 15:56:48 +09:00
chanii8 5e3d263750 [cs] feat: 로그아웃 라우트 핸들러 적용 2026-08-03 15:40:48 +09:00
chanii8 70136aa65d [as] feat: 로그아웃 라우트 핸들러 적용 2026-08-03 14:57:47 +09:00
chanii8 b514bdc472 [cm] feat: 로그아웃 라우트 핸들러 적용 2026-08-03 14:53:42 +09:00
chanii8 20cc2ca248 Merge branch 'main' into deploy
Deploy Frontend / deploy (push) Successful in 3m48s
2026-08-03 11:45:43 +09:00
chanii8 5031f0c582 Merge branch 'iipbs/as/dev' of https://www.onasys.co.kr:3000/ONAANDALL/frontend 2026-08-03 11:36:57 +09:00
chanii8 25287dc3d9 [as] chore: 기본계획 스키마 수정 2026-08-03 11:36:45 +09:00
chanii8 5527ad8c41 Merge branch 'iipbs/st/dev' 2026-08-03 11:20:13 +09:00
chanii8 75525ec7a8 Merge branch 'iipbs/cs/dev' 2026-08-03 11:20:04 +09:00
chanii8 21e967473b Merge branch 'iipbs/as/dev' 2026-08-03 11:19:50 +09:00
chanii8 c67b5844b6 [st] chore: metadata 수정 2026-08-03 11:14:48 +09:00
chanii8 ced62ec0c4 [cs] chore: metadata 수정 2026-08-03 11:12:06 +09:00
chanii8 84bbaa09da [as] chore: metadata 수정 2026-08-03 11:08:42 +09:00
chanii8 dfa35542c5 [cm] chore: metadata 수정 2026-08-03 11:06:43 +09:00
chanii8 a6348050fd [as] refactor: 로그인 페이지 auth 그룹으로 분리 2026-08-03 11:03:03 +09:00
chanii8 4d700c3566 Merge branch 'main' of https://www.onasys.co.kr:3000/ONAANDALL/frontend into iipbs/as/dev 2026-08-03 10:55:33 +09:00
chanii8 f2d7e947d7 [cm] refactor: 로그인 페이지 auth 그룹으로 분리 2026-08-03 10:52:01 +09:00
chanii8 3c65e6f8f5 chore: css 모듈 처리 2026-08-03 10:43:17 +09:00
chanii8 5cfd7d65ec Merge branch 'main' into deploy
Deploy Frontend / deploy (push) Successful in 7m45s
2026-07-31 18:00:11 +09:00
chanii8 1efe2dbe89 remove: ClientIpGuard 제거 2026-07-31 17:56:21 +09:00
chanii8 b2260e0b61 [as] chore: 기본계획 신청 페이지 수정 2026-07-31 17:32:25 +09:00
chanii8 3c2ca24dea [as] feat: 하수도기본계획 > 이전기본계획조회 다이얼로그 구현 2026-07-31 16:58:46 +09:00
chanii8 2acbeb83d0 [as] feat: 행정지원 업무 신청 시 시도/구군 선택 로직 추가 2026-07-31 13:50:07 +09:00
chanii8 e0956e05a6 [as] chore: 설치인가 조회 조건 수정 2026-07-31 09:55:05 +09:00
chanii8 e67492853c [as] WIP: 기본계획조회 다이얼로그 구현 2026-07-31 09:30:09 +09:00
chanii8 3b8307b674 Merge branch 'main' into deploy 2026-07-30 11:37:16 +09:00
chanii8 36e56dcaef Merge branch 'iipbs/st/dev' 2026-07-30 11:28:08 +09:00
chanii8 e42e28d062 [st] chore: 통계검증리포트 > 승인정보입력 다이얼로그 수정 2026-07-30 11:27:36 +09:00
chanii8 0786053022 Merge remote-tracking branch 'origin/iipbs/st/dev' 2026-07-30 11:24:13 +09:00
chanii8 e56938f7ff [as] WIP: 하수도기본계획 신청 페이지(이전기본계획조회) 2026-07-30 11:23:44 +09:00
rsone001 791ec1ae2d [ST] - code-refresh 로직 추가 2026-07-30 11:01:02 +09:00
chanii8 b8b0b24b0e [as] feat: dc service 추가 2026-07-29 15:39:37 +09:00
chanii8 afbebb9dfc [as] feat: 하수도기본계획 목록 페이지 구현 2026-07-29 10:24:38 +09:00
rsone001 4e4942313a [ST] - fix: 통계일정관리 불필요 컬럼 제거 2026-07-28 11:34:46 +09:00
chanii8 fe76864a05 [as] feat: 하수도기본계획 등록 폼 레이아웃 구현 2026-07-27 17:29:23 +09:00
chanii8 2f800c960d Merge branch 'main' into deploy
Deploy Frontend / deploy (push) Successful in 10m24s
2026-07-24 16:59:38 +09:00
chanii8 e6fcbfbde4 chore: tsserver 용량 관리 2026-07-24 16:25:55 +09:00
chanii8 ed4d1b73cb refactor: X-Device-Id 공통 로직으로 변경 2026-07-24 15:53:23 +09:00
chanii8 2728b4058b Merge branch 'main' into deploy
Deploy Frontend / deploy (push) Successful in 4m8s
2026-07-24 10:57:06 +09:00
chanii8 e3dac52d7e Merge branch 'iipbs/st/dev' 2026-07-24 10:56:09 +09:00
chanii8 d1d8a1eff3 [st] feat: 통계 검증 리포트 시도/시군구 셀렉트 로직 수정 2026-07-24 10:47:57 +09:00
chanii8 cae9f94388 [as] docs: 설계자문 구현 문서 생성 2026-07-24 09:57:40 +09:00
jcchoi 4502d55ce1 Merge remote-tracking branch 'origin/iipbs/st/dev' 2026-07-23 20:49:31 +09:00
chanii8 cad87496ac [as] feat: 기본계획 엔티티 생성 2026-07-23 17:41:16 +09:00
rsone001 3d3b978070 [st] - fix: 검토이력조회 request 누락 수정 2026-07-23 16:36:29 +09:00
rsone001 117082c862 [st] - fix: 입력 메뉴 데이터 항목 추가 시 해당사항없음 false로 갱신되지 않던 오류 수정 2026-07-23 14:26:11 +09:00
chanii8 b842905572 Merge branch 'main' into deploy
Deploy Frontend / deploy (push) Successful in 4m28s
2026-07-23 11:44:35 +09:00
chanii8 bfa03aabc0 Merge branch 'iipbs/st/dev' of https://www.onasys.co.kr:3000/ONAANDALL/frontend 2026-07-23 11:43:30 +09:00
chanii8 f535cd1010 [st] feat: 대시보드 일정안내 empty 구현 및 일정관리 월 이동 구현 2026-07-23 11:33:21 +09:00
chanii8 b388edb23c [as] feat: 업무신청에서 재이용수 선택 로직 구현 2026-07-22 17:03:46 +09:00
chanii8 85fa9a8658 Merge branch 'main' of https://www.onasys.co.kr:3000/ONAANDALL/frontend into deploy
Deploy Frontend / deploy (push) Successful in 10m37s
2026-07-22 14:42:42 +09:00
chanii8 2f6d4ebec1 chore: 코드 정리 2026-07-22 14:13:38 +09:00
rsone001 1c3cc5b61b Merge branch 'iipbs/st/dev' of https://www.onasys.co.kr:3000/ONAANDALL/frontend into iipbs/st/dev 2026-07-22 13:39:57 +09:00
rsone001 db7584cb01 [st] - fix: 집계06.공공하수처리시설 조회 조건 추가, 입력06.공공하수처리시설 매핑 계약기간 DatePicker 수정, 입력자 예외 제출 valuedation 수정 2026-07-22 13:38:58 +09:00
chanii8 5ae56481c2 Merge remote-tracking branch 'origin/main' into deploy
Deploy Frontend / deploy (push) Successful in 7m23s
2026-07-22 11:44:50 +09:00
chanii8 b842408f41 Merge remote-tracking branch 'origin/iipbs/as/dev' 2026-07-22 11:36:12 +09:00
chanii8 6e33a62ed9 [as] feat: 설계자문 페이지 구현 2026-07-22 11:34:38 +09:00
mind-one005 36231a5eca Merge branch 'iipbs/wr/dev' 2026-07-22 09:23:43 +09:00
chanii8 84fb81b245 Merge branch 'main' of https://www.onasys.co.kr:3000/ONAANDALL/frontend into iipbs/as/dev 2026-07-21 15:56:50 +09:00
chanii8 e18f759fab [as] feat: 설계자문 엔티티 생성 2026-07-21 14:32:14 +09:00
chanii8 ce9c7b17a2 Merge remote-tracking branch 'origin/iipbs/st/dev' 2026-07-21 13:54:32 +09:00
chanii8 c2d6f7fe37 Merge remote-tracking branch 'origin/iipbs/cs/dev' 2026-07-21 13:54:28 +09:00
chanii8 9b18aa9cef Merge remote-tracking branch 'origin/iipbs/cm/dev' 2026-07-21 13:54:22 +09:00
chanii8 5eccb58aa9 Merge remote-tracking branch 'origin/iipbs/as/dev' 2026-07-21 13:54:16 +09:00
chanii8 acce1364fa Merge branch 'iipbs/st/dev' of https://www.onasys.co.kr:3000/ONAANDALL/frontend into iipbs/st/dev 2026-07-21 13:53:30 +09:00
chanii8 625c5f5fe2 [st] chore: 토큰 자동 리프레시 코드 주석 처리 2026-07-21 13:53:24 +09:00
chanii8 dffa9d74a0 [cs] chore: 토큰 자동 리프레시 코드 주석 처리 2026-07-21 13:52:59 +09:00
chanii8 668a8ef844 [cm] chore: 토큰 자동 리프레시 코드 주석 처리 2026-07-21 13:52:26 +09:00
chanii8 df4d708981 [as] chore: 토큰 자동 리프레시 코드 주석 처리 2026-07-21 13:51:10 +09:00
chanii8 c48a46c7ab feat: 토큰 시간 만료 시 authService.logout 함수 실행 2026-07-21 13:50:19 +09:00
chanii8 8079d7d6af Merge branch 'main' into deploy
Deploy Frontend / deploy (push) Successful in 7m20s
2026-07-21 11:51:55 +09:00
chanii8 da844fb3a7 Merge branch 'main' of https://www.onasys.co.kr:3000/ONAANDALL/frontend 2026-07-21 11:50:36 +09:00
chanii8 3e1f89d90f [common/components] chore: 로그인 연장 콘솔 추가(임시) 2026-07-21 11:47:39 +09:00
chanii8 7c8eef1715 Merge branch 'main' into deploy
Deploy Frontend / deploy (push) Successful in 6m51s
2026-07-20 18:44:05 +09:00
chanii8 aaf898fb4c Merge branch 'main' of https://www.onasys.co.kr:3000/ONAANDALL/frontend into iipbs/cm/dev 2026-07-20 17:47:19 +09:00
chanii8 f10b3179c2 Merge branch 'main' into deploy
Deploy Frontend / deploy (push) Successful in 10m25s
2026-07-20 17:11:46 +09:00
rsone001 533e89c919 [st] - fix: 이상치선별보고서 css 수정 2026-07-20 17:11:10 +09:00
chanii8 aab8cf9622 [cm] feat: 인증 서비스에 "X-Device-Id" 적용 2026-07-20 15:37:17 +09:00
chanii8 2e0bb5aa84 Merge branch 'main' of https://www.onasys.co.kr:3000/ONAANDALL/frontend into deploy
Deploy Frontend / deploy (push) Successful in 10m10s
2026-07-20 13:52:10 +09:00
chanii8 7ae8d42731 Merge branch 'main' into deploy
Deploy Frontend / deploy (push) Successful in 9m2s
2026-07-16 18:39:59 +09:00
chanii8 a9e65af77b Merge branch 'main' into deploy
Deploy Frontend / deploy (push) Successful in 3m25s
2026-07-13 11:14:34 +09:00
chanii8 d21ecc975f Merge branch 'main' into deploy
Deploy Frontend / deploy (push) Successful in 2m14s
2026-07-09 17:54:09 +09:00
chanii8 452509281a Merge branch 'main' into deploy
Deploy Frontend / deploy (push) Successful in 5m8s
2026-07-09 17:40:25 +09:00
chanii8 2e3602eee6 Merge branch 'main' into deploy
Deploy Frontend / deploy (push) Successful in 11m12s
2026-07-08 18:55:17 +09:00
jcchoi 7dfd535841 Merge branch 'main' into deploy
Deploy Frontend / deploy (push) Successful in 2m54s
2026-07-06 16:54:57 +09:00
jcchoi 39d928b5b1 Merge remote-tracking branch 'origin/iipbs/st/dev' into deploy 2026-07-06 16:51:29 +09:00
jcchoi 525695072a Merge branch 'main' into deploy
Deploy Frontend / deploy (push) Successful in 2m56s
2026-07-05 11:07:27 +09:00
jcchoi 972a4ce789 Merge branch 'main' into deploy
Deploy Frontend / deploy (push) Successful in 5m30s
2026-07-02 18:34:53 +09:00
jcchoi 879aadc068 Merge remote-tracking branch 'origin/iipbs/st/dev' into deploy
Deploy Frontend / deploy (push) Successful in 3m2s
2026-07-02 18:29:55 +09:00
1393 changed files with 92690 additions and 30076 deletions
+1
View File
@@ -27,6 +27,7 @@ settings.json
.npmrc .npmrc
.cursor .cursor
.agent .agent
CLAUDE.MD
apps/**/tsconfig.tsbuildinfo apps/**/tsconfig.tsbuildinfo
packages/**/tsconfig.tsbuildinfo packages/**/tsconfig.tsbuildinfo
+1 -1
View File
@@ -4,7 +4,7 @@ import { Providers } from "@/app/providers";
import "@/app/styles/globals.css"; import "@/app/styles/globals.css";
export const metadata: Metadata = { export const metadata: Metadata = {
title: "행정지원시스템", title: "유역하수도통합플랫폼 Hasuon",
description: "행정지원시스템", description: "행정지원시스템",
}; };
+9 -5
View File
@@ -19,11 +19,15 @@
} }
], ],
"paths": { "paths": {
"@/*": ["./src/*"], "@/*": ["./src/*"]
"@common/components": ["../../packages/common/components/dist/"],
"@common/network": ["../../packages/common/network/"]
} }
}, },
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], "include": [
"exclude": ["node_modules"] "next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts",
"../../packages/common/types/**/*.d.ts"
],
"exclude": ["node_modules", ".next", "dist", "coverage", "storybook-static", "public"]
} }
+13
View File
@@ -0,0 +1,13 @@
import "@/app/styles/globals.css";
export const metadata = {
title: "유역하수도통합플랫폼 Hasuon",
};
export default function AuthLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="ko">
<body>{children}</body>
</html>
);
}
@@ -0,0 +1,3 @@
import { DashboardPage } from "@/pages/dashboard/ui/dashboard-page";
export default DashboardPage;
@@ -0,0 +1,10 @@
import { DesignEditPage } from "@/pages/design-cnstn";
interface PageProps {
params: Promise<{ id: string }>;
}
export default async function Page({ params }: PageProps) {
const { id } = await params;
return <DesignEditPage id={id} />;
}
@@ -0,0 +1,10 @@
import { DesignDetailGate } from "@/pages/design-cnstn";
interface PageProps {
params: Promise<{ id: string }>;
}
export default async function Page({ params }: PageProps) {
const { id } = await params;
return <DesignDetailGate id={id} />;
}
@@ -0,0 +1,11 @@
import { Suspense } from "react";
import { DesignListPage } from "@/pages/design-cnstn";
export default function Page() {
return (
<Suspense>
<DesignListPage />
</Suspense>
);
}
@@ -0,0 +1,27 @@
import { DesignRegistPage } from "@/pages/design-cnstn";
import { parseTaskSeComCdFromApi } from "@/pages/design-cnstn/lib/design-cnstn-regist-schema";
interface PageProps {
searchParams: Promise<{
taskSeComCd?: string;
admdstCd?: string;
sido?: string;
gugun?: string;
}>;
}
export default async function Page({ searchParams }: PageProps) {
const params = await searchParams;
const taskSeComCd = parseTaskSeComCdFromApi(params.taskSeComCd);
return (
<DesignRegistPage
defaults={{
taskSeComCd: taskSeComCd.length ? taskSeComCd : undefined,
admdstCd: params.admdstCd,
sido: params.sido,
gugun: params.gugun,
}}
/>
);
}
@@ -1,4 +1,4 @@
import { FnrsViewPage } from "@/pages/fnrs-cnsltn"; import { FnrsDetailGate } from "@/pages/fnrs-cnsltn";
interface PageProps { interface PageProps {
params: Promise<{ id: string }>; params: Promise<{ id: string }>;
@@ -6,5 +6,5 @@ interface PageProps {
export default async function Page({ params }: PageProps) { export default async function Page({ params }: PageProps) {
const { id } = await params; const { id } = await params;
return <FnrsViewPage id={id} />; return <FnrsDetailGate id={id} />;
} }
@@ -0,0 +1,11 @@
import { Suspense } from "react";
import { FnrsListPage } from "@/pages/fnrs-cnsltn";
export default function Page() {
return (
<Suspense>
<FnrsListPage />
</Suspense>
);
}
@@ -0,0 +1,27 @@
import { FnrsRegistPage } from "@/pages/fnrs-cnsltn";
import { parseTaskSeComCdFromApi } from "@/pages/fnrs-cnsltn/lib/fnrs-cnsltn-regist-schema";
interface PageProps {
searchParams: Promise<{
taskSeComCd?: string;
admdstCd?: string;
sido?: string;
gugun?: string;
}>;
}
export default async function Page({ searchParams }: PageProps) {
const params = await searchParams;
const taskSeComCd = parseTaskSeComCdFromApi(params.taskSeComCd);
return (
<FnrsRegistPage
defaults={{
taskSeComCd: taskSeComCd.length ? taskSeComCd : undefined,
admdstCd: params.admdstCd,
sido: params.sido,
gugun: params.gugun,
}}
/>
);
}
@@ -1,6 +1,6 @@
import { InstlViewPage } from "@/pages/instl-sncn"; import { InstlSncnDetailGate } from "@/pages/instl-sncn";
export default async function InstlSncnDetailPage({ params }: { params: Promise<{ id: string }> }) { export default async function InstlSncnDetailPage({ params }: { params: Promise<{ id: string }> }) {
const { id } = await params; const { id } = await params;
return <InstlViewPage id={id} />; return <InstlSncnDetailGate id={id} />;
} }
@@ -0,0 +1,11 @@
import { Suspense } from "react";
import { InstlListPage } from "@/pages/instl-sncn";
export default function Page() {
return (
<Suspense>
<InstlListPage />
</Suspense>
);
}
@@ -0,0 +1,28 @@
import { InstlRegistPage } from "@/pages/instl-sncn";
interface InstlSncnRegistRouteProps {
searchParams: Promise<{
taskSeComCd?: string;
admdstCd?: string;
sido?: string;
gugun?: string;
}>;
}
export default async function Page({ searchParams }: InstlSncnRegistRouteProps) {
const params = await searchParams;
const taskSeComCd = params.taskSeComCd
? params.taskSeComCd.split(",").filter(Boolean)
: undefined;
return (
<InstlRegistPage
defaults={{
taskSeComCd,
admdstCd: params.admdstCd,
sido: params.sido,
gugun: params.gugun,
}}
/>
);
}
@@ -0,0 +1,3 @@
import { MyPbadmsSprtListPage } from "@/pages/my-pbadms-sprt";
export default MyPbadmsSprtListPage;
@@ -0,0 +1,3 @@
import { NtiHstryListPage } from "@/pages/nti-hstry";
export default NtiHstryListPage;
@@ -0,0 +1,3 @@
import { NtitlkSndngPage } from "@/pages/ntitlk-sndng";
export default NtitlkSndngPage;
@@ -0,0 +1,13 @@
import { SewerBscPlanEditPage } from "@/pages/sewer-bsc-plan";
interface PageProps {
params: Promise<{ id: string }>;
searchParams: Promise<{ chgCycl?: string }>;
}
export default async function Page({ params, searchParams }: PageProps) {
const { id } = await params;
const { chgCycl } = await searchParams;
const propChgCycl = chgCycl ? Number(chgCycl) : undefined;
return <SewerBscPlanEditPage id={id} propChgCycl={propChgCycl} />;
}
@@ -0,0 +1,13 @@
import { SewerBscPlanDetailGate } from "@/pages/sewer-bsc-plan";
interface PageProps {
params: Promise<{ id: string }>;
searchParams: Promise<{ chgCycl?: string }>;
}
export default async function Page({ params, searchParams }: PageProps) {
const { id } = await params;
const { chgCycl } = await searchParams;
const propChgCycl = chgCycl ? Number(chgCycl) : undefined;
return <SewerBscPlanDetailGate id={id} propChgCycl={propChgCycl} />;
}
@@ -0,0 +1,11 @@
import { Suspense } from "react";
import { SewerBscPlanListPage } from "@/pages/sewer-bsc-plan";
export default function Page() {
return (
<Suspense>
<SewerBscPlanListPage />
</Suspense>
);
}
@@ -0,0 +1,19 @@
import { SewerBscPlanRegistPage } from "@/pages/sewer-bsc-plan";
interface PageProps {
searchParams: Promise<{
taskSeComCd?: string;
taskSeCdNm?: string;
sewerBscPlanSeComCd?: string;
sewerBscPlanSeCdNm?: string;
admdstCd?: string;
sido?: string;
gugun?: string;
}>;
}
export default async function Page({ searchParams }: PageProps) {
const params = await searchParams;
return <SewerBscPlanRegistPage defaults={params} />;
}
@@ -6,8 +6,8 @@ import { Providers } from "@/app/providers";
import "@/app/styles/globals.css"; import "@/app/styles/globals.css";
export const metadata: Metadata = { export const metadata: Metadata = {
title: "행정지원시스템", title: "유역하수도통합플랫폼 Hasuon",
description: "행정지원시스템", description: "디지털 행정지원 시스템",
}; };
interface RootLayoutProps { interface RootLayoutProps {
@@ -1,3 +0,0 @@
import { FnrsListPage } from "@/pages/fnrs-cnsltn";
export default FnrsListPage;
@@ -1,12 +0,0 @@
import { FnrsRegistPage } from "@/pages/fnrs-cnsltn";
import { parseTaskSeComCdFromApi } from "@/pages/fnrs-cnsltn/lib/fnrs-cnsltn-regist-schema";
interface PageProps {
searchParams: Promise<{ taskSeComCd?: string }>;
}
export default async function Page({ searchParams }: PageProps) {
const params = await searchParams;
const taskSeComCd = parseTaskSeComCdFromApi(params.taskSeComCd);
return <FnrsRegistPage taskSeComCd={taskSeComCd.length ? taskSeComCd : undefined} />;
}
@@ -1,3 +0,0 @@
import { InstlListPage } from "@/pages/instl-sncn";
export default InstlListPage;
@@ -1,14 +0,0 @@
import { InstlRegistPage } from "@/pages/instl-sncn";
interface InstlSncnRegistRouteProps {
searchParams: Promise<{ taskSeComCd?: string }>;
}
export default async function Page({ searchParams }: InstlSncnRegistRouteProps) {
const params = await searchParams;
const taskSeComCd = params.taskSeComCd
? params.taskSeComCd.split(",").filter(Boolean)
: undefined;
return <InstlRegistPage taskSeComCd={taskSeComCd} />;
}
-1
View File
@@ -1 +0,0 @@
export { MainPage as default } from "@/pages/home";
+10
View File
@@ -0,0 +1,10 @@
import { cookies } from "next/headers";
import { NextResponse } from "next/server";
import { COOKIE_DOMAIN, REFRESH_TOKEN } from "@/shared/config";
export async function POST() {
const cookieStore = await cookies();
cookieStore.delete({ name: REFRESH_TOKEN, domain: COOKIE_DOMAIN });
return NextResponse.json({ message: "Logged out" }, { status: 200 });
}
+38
View File
@@ -0,0 +1,38 @@
import type { Metadata } from "next";
import Image from "next/image";
import "@/app/styles/globals.css";
export const metadata: Metadata = {
title: "유역하수도통합플랫폼 Hasuon",
description: "페이지가 존재하지 않습니다. 관리자에게 문의하여 주십시오.",
};
export default function GlobalNotFound() {
return (
<html lang="ko">
<body>
<main className="flex min-h-screen flex-col items-center justify-center bg-[#f0f4f8] px-4 py-16">
<div className="mb-16 text-center">
<h1 className="text-[3.2rem] font-bold leading-snug tracking-tight">
<span className="text-primary">디지털 행정지원 시스템</span>에 오신 것을 환영합니다.
</h1>
<p className="mt-6 text-[2rem] text-[#666]">오류가 발생하였습니다.</p>
</div>
<section className="card card-border flex w-full max-w-[72rem] flex-col items-center !rounded-2xl !py-16">
<Image
src="/images/magnifying-glass-with-warning.png"
alt=""
width={280}
height={280}
className="size-[28rem] object-contain"
/>
<p className="mt-4 text-[2.4rem] font-bold text-[#444]">페이지가 존재하지 않습니다.</p>
<p className="mt-2 text-[1.6rem] text-[#666]">관리자에게 문의하여 주십시오</p>
</section>
</main>
</body>
</html>
);
}
@@ -1,367 +0,0 @@
# 재원협의(fnrs-cnsltn) vs 설치인가(instl-sncn) 차이 분석
| 항목 | 내용 |
| --------- | ----------------------------------------------------------------------------- |
| 작성일 | 2026-07-09 |
| 기준 구현 | `fnrs-cnsltn` (추가 요구사항 반영 완료) |
| 비교 대상 | `instl-sncn` |
| 목적 | 두 페이지의 동작·구조 차이를 정리하고, 설치인가 정렬 시 이식 대상을 명확히 함 |
---
## 1. 요약
두 모듈은 **동일한 PBADMS(공공행정 지원) 업무 패턴**을 따르는 형제 페이지다.
공통 흐름:
```
목록 → TaskSe 선택 후 신청 → 저장(SV) → 신청완료(ST)
→ 환경청제출(LE) → 기술검토 → 검토결과 회신(EP)
→ 인가승인(TA) → 고시(TB)
```
| 구분 | fnrs-cnsltn | instl-sncn |
| ------------- | ----------------- | ---------------- |
| 업무명 | 재원협의 | 설치인가 |
| 문서번호 | `fnrsCnsltnDocNo` | `instlSncnDocNo` |
| docSeComCd | `FNRS` | `INSTL` |
| 라우트 | `/fnrs-cnsltn/*` | `/instl-sncn/*` |
| pages 파일 수 | 39 | 32 |
**결론:** `fnrs-cnsltn`이 추가 요구사항이 반영된 기준(reference) 구현이고, `instl-sncn`은 동일 골격이지만 보완요청·결재선 편집·등록/수정 폼 완성도 등에서 미반영·TODO·단순화 상태다.
---
## 2. 파일/폴더 구조
### 2-1. fnrs-cnsltn에만 존재
| 경로 | 역할 |
| -------------------------------------------- | ------------------------- |
| `ui/sections/fnrs-shar-section.tsx` | 재원분담 조회 섹션 |
| `ui/sections/supplement-demand-section.tsx` | 보완요청 이력 섹션 |
| `ui/components/sortable-prgrs-stts-row.tsx` | 결재선 DnD 편집 행 |
| `ui/components/sortable-apln-picker-row.tsx` | 결재선 템플릿 피커 DnD 행 |
| `ui/components/aprvr-picker-dialog.tsx` | 결재자 선택 다이얼로그 |
| `ui/components/elapr-apln-picker-grid.tsx` | 결재선 편집 그리드 |
| `ui/components/elapr-tmplt-select-table.tsx` | 결재서식 선택 테이블 |
| `model/reviewer-option.ts` | 결재자 옵션 타입 |
### 2-2. instl-sncn에만 존재
| 경로 | 역할 | 비고 |
| -------------------------------------- | ------------------- | ------------------------------------------ |
| `ui/sections/history-info-section.tsx` | 등록/수정 이력 표시 | **어디에서도 import되지 않음 (dead code)** |
### 2-3. 공통 구조
```
index.ts
ui/
{list,view,form,regist,edit}-page.tsx
sections/ (basic-info, contact, scale, cost, checklist, attachments, tech-review)
components/ (floating-action-buttons, elapr-approval-dialog, elapr-tmplt-picker-dialog, checklist-task-panel)
lib/ button-config, save-payload-utils, form-data-mapper, regist-schema, checklist/review utils
model/ columns.tsx, view-types.ts
```
App Router도 동일: `page.tsx`, `regist/page.tsx`, `[id]/page.tsx`, `[id]/edit/page.tsx`
---
## 3. 목록 페이지
### 3-1. 검색 필터
| 항목 | fnrs-cnsltn | instl-sncn |
| ------------------ | ----------------------------------------- | --------------------------------- |
| 시도/시군구 API | `@/entities/adstdstr` | 동일 |
| 지역 검색 파라미터 | **`admdstCd`** (시군구 우선, 없으면 시도) | **`sidoComCd` + `sggComCd`** 분리 |
| "전체" 옵션 | `ALL_SELECT_VALUE = "__ALL__"` SelectItem | 없음 (placeholder만) |
| 구분 CodeSelect | `upCd="INSTL"` ※ | `upCd="INSTL"` |
| 사업명 | 동일 | 동일 |
> ※ fnrs 목록의 구분 필터가 `INSTL` 코드를 사용 중이며, TaskSePickerDialog는 `codeUpCd="FNRS"`를 쓴다. **불일치 가능성**이 있어 확인이 필요하다.
### 3-2. 정렬·페이지네이션
| 항목 | fnrs-cnsltn | instl-sncn |
| ------------------------ | ---------------------- | ------------------------------------- |
| 기본 sort (list-page) | `fnrsCnsltnDocNo,desc` | `instlSncnDocNo,desc` |
| fallback sort (list.tsx) | `fnrsCnsltnDocNo,desc` | **`regDt,desc`** (list-page와 불일치) |
| 페이지 크기 | 10 | 10 |
| URL 동기화 | 없음 (`useState` 로컬) | 없음 |
### 3-3. 테이블 컬럼
`model/columns.tsx` 로직은 사실상 동일하다. DTO 타입명만 다르다.
컬럼: 시도, 시군구, 사업명, 업무구분, 담당자, 해당기관, 진행상황, 신청일
### 3-4. 행 클릭·신청
| 항목 | fnrs-cnsltn | instl-sncn |
| ------------ | ------------------------------------------------------------------ | ------------------ |
| 행 클릭 | **조건부 라우팅**: `SV`/`SL` + 등록자 본인 → `/edit`, 그 외 → view | **항상 view** |
| useUserStore | 사용 | 미사용 |
| 신청 버튼 | TaskSePickerDialog `codeUpCd="FNRS"` | `codeUpCd="INSTL"` |
```46:54:apps/as/src/pages/fnrs-cnsltn/ui/fnrs-list.tsx
const handleRowClick = (row: FnrsCnsltnMngResponseDto) => {
const canEdit =
(row.prgrsSttsComCd === "SV" || row.prgrsSttsComCd === "SL") &&
row.rgtr === userInfo?.userUnqId;
router.push(
canEdit ? `/fnrs-cnsltn/${row.fnrsCnsltnDocNo}/edit` : `/fnrs-cnsltn/${row.fnrsCnsltnDocNo}`,
);
};
```
---
## 4. 상세(View) 페이지
### 4-1. 섹션 구성
| 섹션 | fnrs-cnsltn | instl-sncn |
| ------------------------------- | ------------------------------------------ | -------------------------- |
| BasicInfo | 환경청구분, 확정예산사업번호, 예산신청연도 | **사업구분(`sncnSeCdNm`)** |
| ContactInfo | 동일 패턴 | 동일 |
| BusinessScale / Cost | 동일 | 동일 |
| **FnrsShar (재원분담)** | **있음** | **없음** (도메인 고유) |
| Checklist | 동일 | 동일 |
| Attachments | 동일 | 동일 |
| **SupplementDemand (보완요청)** | **있음** | **없음** |
| TechReviewOpinion | 동일 | 동일 |
| HistoryInfo | 없음 | 파일만 존재, view 미연결 |
| FloatingActionButtons | `docSeComCd="FNRS"` | `docSeComCd="INSTL"` |
### 4-2. 데이터 훅
| 항목 | fnrs-cnsltn | instl-sncn |
| --------- | ---------------------------- | -------------------- |
| 상세 훅 | `useFnrsCnsltnMng` | `useInstlSncnDetail` |
| 에러 판정 | `isError \|\| !data?.detDto` | `isError \|\| !data` |
| 로딩 | Skeleton | Skeleton |
---
## 5. 등록/수정(Form) 페이지
### 5-1. 폼 완성도
| 항목 | fnrs-cnsltn | instl-sncn |
| ------------- | --------------------------------- | --------------- |
| 저장(SV) | O | O (SV 고정) |
| 신청완료(ST) | O (edit 모드) | **없음** |
| 삭제 | O (edit 모드, `useConfirm`) | **없음** |
| 재원분담 섹션 | `FnrsSharFormSection` + 잔액 검증 | 없음 |
| 저장 후 이동 | **`/edit` 유지** | **view로 이동** |
| 목록 버튼 | `/fnrs-cnsltn` 고정 | `router.back()` |
### 5-2. 기본정보 Form 필드
| 필드 | fnrs | instl |
| ----------------- | ---------------- | --------------------------------------- |
| taskSe 코드 | `FNRS` | `INSTL` |
| 사업구분 | 없음 | **`sncnSeComCd` (SNCNSE 라디오, 필수)** |
| BnsPicker 연동 | bizNm ~ bizCn | + **`bgtBizCd`, `bgtAplyYr`** |
| 재이용(RE) 조건부 | rtlztnFclt / Usg | 동일 |
### 5-3. Schema
| 항목 | fnrs | instl |
| ------------------- | ------------------- | -------------- |
| 관리 객체 | `fnrsCnsltnMng` | `instlSncnMng` |
| 예산 필드 | `cfmtnBgtBizNo` | `bgtBizCd` |
| 재원분담 | `fnrsSharList` 배열 | 없음 |
| sharAtchFileGroupId | 있음 | 없음 |
| privacyConsent | 있음 | 있음 |
### 5-4. Edit 페이지 래퍼
| 항목 | fnrs | instl |
| ------------------ | ------------------------------- | ------------------------------- |
| 로딩 Skeleton | 있음 | **없음** |
| 데이터 없음 메시지 | "상세 정보를 찾을 수 없습니다." | "수정 대상을 찾을 수 없습니다." |
---
## 6. FloatingActionButtons / 결재 / 보완
### 6-1. FAB 동작 차이
공통: 역할×상태 기반 버튼(`button-config.ts`), checklist 진행률 검증, elapr pending approval 체크
| 기능 | fnrs-cnsltn | instl-sncn |
| ------------------------------- | ------------------------------------------------------- | ------------------------------------------------------- |
| 보완완료 (`splmntCmptn`) | **`submitSupplement` API 연동** | **`// TODO` stub** |
| 보완요청 (`dmndSplmnt`) | **`buildSupplementReqPayload` + `submitReqSupplement`** | **`// TODO` stub** (문자열만 전달) |
| 검토완료 (`rvwCmptn`) | `techReViewSaveCd: "SV"` 후 **결재 다이얼로그 오픈** | `techReViewSaveCd: "CP"`, **다이얼로그 없이 즉시 완료** |
| 검토자지정 | `buildElaprPrgrsSttsListFromAplnRows(aplnRows)` | `buildElaprPrgrsSttsListFromApln(aplnPage.content)` |
| atrzAprv / atrzRply / grndsChck | TODO handler | TODO handler |
```576:617:apps/as/src/pages/instl-sncn/ui/components/floating-action-buttons.tsx
async function handleSplmntCmptn(context: ActionHandlerContext): Promise<void> {
// TODO: 보완완료 API 호출
context.onSuccess?.();
}
// ...
async function handleDmndSplmnt(context: ActionHandlerContext): Promise<void> {
// TODO: 보완요청 API 호출
context.onSuccess?.();
}
```
### 6-2. ElaprApprovalDialog (가장 큰 UX 격차)
| 항목 | fnrs (419줄) | instl (231줄) |
| ---------------- | ------------------------------------------------------ | -------------------------------- |
| 결재선 표시 | DnD sortable 편집 테이블 | **읽기 전용** 테이블 |
| 결재선 추가/저장 | **지원** (`usePrgrsSttsRows`, `useSaveElaprPrgrsStts`) | 없음 |
| 결재자 변경 | **AprvrPickerDialog** | 없음 |
| 전결 체크박스 | **있음** (`finalApprovalYn`) | 없음 |
| 반려 사유 | API에 `rjctRsn` 전달 | UI는 있으나 **payload에 미전달** |
| 저장 전 검증 | `hasUnsavedPrgrsSttsChanges` | 없음 |
### 6-3. ElaprTmpltPickerDialog
| 항목 | fnrs | instl |
| --------------- | -------------------------------------------------------------------- | ----------------------- |
| UI | 2-pane: 서식 테이블 + **결재선 편집 그리드** | 단일 테이블 + 선택 버튼 |
| docSeComCd 필터 | `FNRS` | `INSTL` |
| onConfirm | `{ tmplt, aplnRows }` 편집 결과 | `onSelect(tmplt)`만 |
| 검증 | `validateAllRowsForSave`, `validateAtrzOrder`, `validateUniqueSeqNo` | 없음 |
---
## 7. Entity / API / Hook
### 7-1. Service 메서드
| 메서드 | fnrs | instl |
| ----------------------- | --------------------------- | --------- |
| 목록 | `findPage` | `getPage` |
| 상세 | `findById` | `getById` |
| saveApply | O | O |
| techReview | O | O |
| changePrgrsSttsComCd | O | O |
| remove | O | O |
| **submitSupplement** | **O** (`/submitsupplement`) | **X** |
| **submitReqSupplement** | **O** (`/reqsupplement`) | **X** |
### 7-2. 상세 DTO 확장
**fnrs 추가 필드**
- `fnrsSharList`, `sharAtchFileList`
- `splmntDmndList` (typed)
**instl 추가 필드**
- `sncnSeComCd` / `sncnSeCdNm` (인가구분)
- `bgtBizCd` (예산사업코드)
- `rtlztnFcltSeComNm`, `rtlztnUsgSeComNm`
- `splmntDmndList` 타입은 base에 존재하나 view/API 미연결
- `instlSncnRegionService` (시도/시군구 — 목록은 adstdstr 사용, 미활용)
### 7-3. save-payload-utils
| 함수 | fnrs | instl |
| -------------------------------- | ---------------- | --------- |
| buildSavePayload | + `fnrsSharList` | shar 없음 |
| buildTechReviewReplyPayload | O | O |
| **buildSupplementReqPayload** | **O** | **X** |
| **buildSupplementSubmitPayload** | **O** | **X** |
---
## 8. 비즈니스 도메인 차이
| 영역 | fnrs-cnsltn | instl-sncn |
| -------------- | ----------------------------------------- | ------------------------ |
| 업무 코드 | `FNRS` | `INSTL` |
| TaskSe | FNRS 하위 | INSTL 하위 |
| 고시 버튼 라벨 | "재원협의 고시" | "설치인가 고시" |
| 재원분담 | **핵심 도메인** (`fnrs-shar-se`) | 해당 없음 |
| 인가구분 | 없음 | **`SNCNSE` 코드 (필수)** |
| 예산 식별 | `cfmtnBgtBizNo` | `bgtBizCd` |
| 진행상태 코드 | SV, ST, LE, ER, TE, EP, TA, TB, SL, TP 등 | **동일 세트 공유** |
| 역할 매핑 | ROLE_USER05/04/03/01 | 동일 |
---
## 9. fnrs-cnsltn에만 있는 기능 (추가 요구사항 후보)
instl-sncn 정렬 시 **우선 이식 대상**:
1. 목록 행 클릭 조건부 edit 라우팅 (`SV`/`SL` + 등록자)
2. 보완요청 전체 플로우 — view 섹션, payload builder, API, FAB handler
3. 보완완료 API 연동 (`submitSupplement`)
4. 등록/수정 폼 완성 — 신청완료, 삭제, 저장 후 edit 유지
5. 결재선 DnD 편집형 `ElaprApprovalDialog` (+ aprvr-picker, sortable rows)
6. 결재선 편집형 `ElaprTmpltPickerDialog` (+ apln grid, validation)
7. 검토완료 → 결재 다이얼로그 연계 흐름
8. 검색 필터 "전체" 옵션 및 `admdstCd` 통합 방식 (백엔드 API에 맞게)
9. Edit 페이지 로딩 Skeleton
10. entity에 supplement API/hooks 추가
---
## 10. instl-sncn에만 있는 기능 (유지 필요)
1. **`sncnSeComCd` (사업구분/인가구분)** — 도메인 고유
2. **`bgtBizCd` / `bgtAplyYr`** BnsPicker 연동
3. **`history-info-section.tsx`** — 구현만 있고 view 미연결 (활성화 또는 삭제 결정 필요)
4. entity **`instlSncnRegionService`** — 목록은 adstdstr 사용 중이라 중복/레거시 가능
5. **`INSTL_SNCN_REGISTER_SELECTION_KEY`** sessionStorage 키
---
## 11. 동일·거의 동일 패턴
| 영역 | 동일도 |
| ----------------------------------------------------- | --------------------- |
| App Router 4-route 구조 | 100% |
| `button-config.ts` 역할×상태 매트릭스 | ~99% (액션명만 다름) |
| `columns.tsx` 진행상황 Badge | ~100% |
| checklist / contact / scale / cost / attachments 섹션 | ~95% (import 경로만) |
| review-step-utils 로직 | ~100% (상수명만 다름) |
| TaskSePickerDialog 신청 플로우 | 100% |
| FAB 전체 구조·대부분 handler | ~85% |
| checklist-form-state, business-cost-utils | 거의 동일 |
| shared widgets (`@/widgets/pbadms-sprt`) | 동일 |
---
## 12. instl-sncn 정렬 권장 우선순위
| 우선순위 | 항목 | 근거 |
| -------- | ------------------------------------------------------- | ---------------------------- |
| P0 | 보완요청/보완완료 API + UI | fnrs 완전 구현 vs instl TODO |
| P0 | ElaprApprovalDialog 고도화 | 결재선 편집·전결·반려사유 |
| P0 | ElaprTmpltPickerDialog 2-pane | 검토자지정 품질 |
| P1 | Form 신청완료/삭제 + 저장 후 edit 유지 | 등록 UX parity |
| P1 | 목록 행 클릭 edit 라우팅 | fnrs 추가 요구사항 |
| P1 | 검토완료 → 결재 다이얼로그 연계 | 흐름 통일 |
| P2 | sort fallback 불일치 수정 (`regDt` vs `instlSncnDocNo`) | instl 내부 버그성 |
| P2 | history-info-section 연결 또는 삭제 | dead code 정리 |
| P3 | URL search params 동기화 | 양쪽 모두 미구현 (공통 개선) |
---
## 13. 참고 코드 위치
| 주제 | 경로 |
| ---------------------------- | ------------------------------------------------------------ |
| 목록 edit 라우팅 (fnrs) | `pages/fnrs-cnsltn/ui/fnrs-list.tsx` |
| 목록 항상 view (instl) | `pages/instl-sncn/ui/instl-list.tsx` |
| view 추가 섹션 (fnrs) | `pages/fnrs-cnsltn/ui/fnrs-view-page.tsx` |
| FAB 보완 TODO (instl) | `pages/instl-sncn/ui/components/floating-action-buttons.tsx` |
| 결재 다이얼로그 (fnrs) | `pages/fnrs-cnsltn/ui/components/elapr-approval-dialog.tsx` |
| 결재 다이얼로그 (instl) | `pages/instl-sncn/ui/components/elapr-approval-dialog.tsx` |
| 폼 완성도 (fnrs) | `pages/fnrs-cnsltn/ui/fnrs-form-page.tsx` |
| 폼 단순화 (instl) | `pages/instl-sncn/ui/instl-form-page.tsx` |
| supplement API (fnrs entity) | `entities/fnrs-cnsltn/api/fnrs-cnsltn-service.ts` |
@@ -0,0 +1,250 @@
# 재원협의·실무협의 프론트엔드 개발 가이드
> **대상 앱**: `frontend/apps/as`
> **작성일**: 2026-08-27
> **개정일**: 2026-09-07
> **버전**: 2.0
> **Process**: PR-AS-003 (재원협의 관리 — 실무협의 및 AI 검증 통합)
>
> **SoT (2026-09-03 완결)**:
>
> - 프로세스·상태·버튼: `03.재원협의_실무협의_프로세스_통합_v완결.md`
> - REST 설계: `05.재원협의_상태별_endpoint_설계.md`
> - 공통코드: `FNRSSTTS` (`SV, ST, LE, FV, ER, SL, AC, TE, TP, RM, RB, RC, RA, AV, EV, RK, RR, EP, TA`)
>
> **백엔드 서비스**:
>
> - `as-service` — `/api/v1/fnrs-cnsltn-mng`, `/api/v1/pwrk-cnsltn-mng`, `/api/v1/pwrk-cnsltn-doc-crt-rslt`, `/api/v1/dcrag-to`
> - `as-elapr-service` — `/api/v1/elapr-dmnd`, `/api/v1/elapr-prgrs-stts`, `/api/v1/elapr-apln`
>
> **현재 프론트 상태**: 재원협의(`fnrs-cnsltn`)·전자결재(`elapr`)·DC RAG TO(`dcrag-to`)는 존재한다. 실무협의(`pwrk-cnsltn-`\*) 엔티티·화면은 **미구현**. 버튼 매트릭스·상태코드는 **v2.1 잔존**이며 v3.0(`FNRSSTTS`)과 불일치한다. [9절](#9-현재-코드-vs-to-be-갭) 참고.
---
## 1. 문서 목적
재원협의 4대 주체(기초·광역·환경청·공단)의 **상태 전이·버튼·API**를 `apps/as`에 구현할 때 쓰는 프론트엔드 가이드이다.
v1.0은 공단 접수자의 **자료검증 → 초안생성 → 웹기안기**만 다뤘다. v2.0은 2026-09-03 완결 프로세스 기준으로 **신청부터 최종종결(**`TA`**)까지**, 실무협의·AI 3파이프라인·결재선·보완 루프를 포함한다.
상세 표·시퀀스·DTO는 본문에 넣지 않고 레퍼런스로 분리한다. 구현 중에는 본문의 규칙만 보고, 표가 필요하면 해당 파일을 연다.
---
## 2. 문서 맵
| 파일 | 내용 | 언제 보는가 |
| -------------------------------------------------------- | ------------------------------------------------------------------------- | ----------------------- |
| [development-guide.md](./development-guide.md) (본 문서) | 도메인, 핵심 규칙, 화면 흐름, 갭, 구현 순서 | 일상 구현 |
| [ref-process-status.md](./ref-process-status.md) | 4-Lane 시퀀스, Activity, `FNRSSTTS`, 보완 3루프, AI 타임아웃, 결재선 복사 | 상태 전이·폴링 UX |
| [ref-screen-buttons.md](./ref-screen-buttons.md) | 38행 화면·역할·버튼 매트릭스, 버튼 가시성 | `button-config.ts` 개편 |
| [ref-endpoint.md](./ref-endpoint.md) | 버튼→API 총괄, 단계별 입출력, FE 호출 금지 API, Gap Analysis | 엔티티·service 매핑 |
| [ref-frontend-entities.md](./ref-frontend-entities.md) | ENDPOINT, 슬라이스, Service 시그니처, 폴링, 웹기안기, DTO | 엔티티 레이어 구현 |
| [ref-webhwp-api.md](./ref-webhwp-api.md) | 웹한글기안기 v3 `HwpCtrl` Properties/Methods, Open/SaveAs | 기안기 위젯 연동 |
---
## 3. 도메인 개요
실무협의서(PWRK)는 재원협의서(FNRS)의 **하위 문서**이다. 재원협의 1건당 실무협의서는 **1건만** 생성된다.
| 문서 | 번호 예시 | 필드 |
| -------- | ----------------- | ----------------------------- |
| 재원협의 | `F-20260824-0001` | `fnrsCnsltnDocNo` / `upDocNo` |
| 실무협의 | `P-20260827-0001` | `pwrkCnsltnDocNo` |
진행상태는 **재원협의 마스터** `tb_as_fnrs_cnsltn_mng.prgrs_stts_com_cd` (`FNRSSTTS`)가 SSOT이다. 실무협의 문서의 `prgrsSttsComCd`는 연계 표시용이며, 화면 버튼은 재원협의 상태로 분기한다.
### 3-1. 4대 주체
| 역할 | 코드 | 화면 소유 | 핵심 구간 |
| --------------- | ------------- | -------------- | ------------------------------------ |
| 기초자치단체 | `ROLE_USER05` | 작성/수정/상세 | 신청, 3종 보완, 조치결과, 최종확인 |
| 광역자치단체 | `ROLE_USER04` | 상세 | 경유 제출, 결과 전달 |
| 유역·지방환경청 | `ROLE_USER03` | 상세 | 의뢰/자체종결, 1·2차 회신 |
| 한국환경공단 | `ROLE_USER01` | 상세 + 결재선 | 파일검증, 검토자지정, 1·2차 기술검토 |
### 3-2. 핵심 식별자
| 필드 | 의미 | 비고 |
| ----------------- | ---------------- | ----------------------------------------------------------- |
| `fnrsCnsltnDocNo` | 재원협의문서번호 | 상태 전이·목록 PK |
| `pwrkCnsltnDocNo` | 실무협의문서번호 | `P-YYYYMMDD-NNNN`. 검토자지정(`ER→AC`) 시 발급 |
| `upDocNo` | 상위문서번호 | 재원협의 번호. `upDocSeComCd = "FNRS"` |
| `crtDmndUuid` | 생성요청 UUID | 초안 폴링·웹기안기·다운로드 키 |
| `verSn` | 버전일련번호 | 복합 PK `(crtDmndUuid, verSn)`. 저장 시 서버가 `+1` |
| `orgnlOtptDocCn` | HWP 바이너리 | 목록/상세 응답은 항상 `null`. 다운로드·`webhwp/open`만 사용 |
| `elaprCycl` | 결재 차수 | 1차 `TE`, 2차 `RK`. 금액검증 완료 후 1차 결재선 복사 |
### 3-3. IIPBS 약어
| 한글 | 약어 | 필드 예 |
| ---- | ------------ | --------------------------- |
| 재원 | FNRS | `fnrsCnsltnDocNo` |
| 실무 | PWRK | `pwrkCnsltnDocNo` |
| 협의 | CNSLTN | `fnrs-cnsltn-mng` |
| 생성 | CRT | `crtDmndUuid` |
| 요청 | DMND | `crtDmndUuid`, `elapr-dmnd` |
| 검증 | VRFC | `vrfcSttsComCd` |
| 보완 | SPLMNT | `splmntDmndList` |
| 결재 | ELAPR / ATRZ | `elaprCycl`, `atrzSeComCd` |
---
## 4. 상태코드 한눈에 보기
상위코드 `FNRSSTTS`. v1.0의 `PWRKSTTS`(`DV`, `AR`, `TR` 등)는 **폐기**한다.
정방향:
```
SV → ST → LE → FV → ER → AC → TE → TP → RM → RB → RC → RA → AV → EV → RK → RR → EP → TA
```
자체종결: `LE → EP → TA`
보완: `ER|TE|RK → SL` 후 직전 상태로 복귀.
| 코드 | 명칭 | FE에서 할 일 |
| ---- | ---------------------- | ------------------------------------------------- |
| `SV` | 저장 | 기초 작성화면 저장/신청/삭제 |
| `ST` | 신청완료 | 광역 환경청제출 |
| `LE` | 환경청 검토 | 자체종결(`EP`) 또는 기술검토 의뢰(`FV`) |
| `FV` | 파일검증 | AI 진행 표시. **30분** 후 `ER` Fallback |
| `ER` | 기술검토 의뢰 | 공단 접수자: 파일보완 / 수동파일검증 / 검토자지정 |
| `SL` | 보완요청 | 기초 수정화면. 복귀 대상은 이력의 직전 상태 |
| `AC` | 초안생성 | AI 진행 표시. **30분** 후 `TE` Fallback |
| `TE` | 기술검토 | 웹기안기·검토완료·보완. 결재선 1차 |
| `TP` | 기술검토 회신 | 환경청 → 광역 `RM` |
| `RM` | 재원협의결과회신 | 광역 → 기초 `RB` |
| `RB` | 지자체회신 | 기초 실무협의서 관리 / 조치완료 |
| `RC` | 조치결과서 제출 | 광역 → 환경청 `RA` |
| `RA` | 조치결과서제출(환경청) | 환경청 2차 의뢰 `AV` |
| `AV` | 금액검증 | AI 진행 표시. **30분** 후 `EV` Fallback |
| `EV` | 조치결과검토의뢰 | 공단 접수자: 2차 결재선 검토자지정 → `RK` |
| `RK` | 조치결과서검토 | 웹기안기·금액검증 재실행·보완. 결재선 2차 |
| `RR` | 조치결과서검토 회신 | 환경청 → 광역 `EP` |
| `EP` | 검토결과 회신 | 광역 → 기초 `TA` |
| `TA` | 검토결과 회신(종료) | 기초 조회만. **고시(**`TB`**) 아님** |
코드 의미·구버전 대조·보완 루프·AI Fallback은 [ref-process-status.md](./ref-process-status.md).
---
## 5. 프론트가 지켜야 할 핵심 규칙
1. **상태 SSOT는** `FNRSSTTS`. 버튼 가시성은 `(dtlInstCd, prgrsSttsComCd, atrzSeComCd)`로만 계산한다. 공단은 결재선 구분(`ATRZ01` 접수자, `ATRZ02` 검토자, `ATRZ03` 승인자, `ATRZ99` 최종승인자)을 추가로 본다.
2. **AI 콜백 API는 화면에서 호출하지 않는다.** `POST /dcrag/documents/verify-result`, `POST /dcrag/draft/complete`, `POST /dcrag/cost/verify-result`는 서버 전용이다.
3. **초안생성 1차 트리거는 검토자지정이다.** `POST /elapr-dmnd` (`ER → AC`). 예전처럼 `POST /pwrk-cnsltn-mng`로 초안을 시작하지 않는다.
4. **기술검토 의뢰는** `LE → FV` **전용 API** `POST /fnrs-cnsltn-mng/{docNo}/request-examine`. 현재 코드의 `updatePrgrsStts(ER)` 직행은 폐기.
5. **수동 AI 재실행은 상태를 바꾸지 않는다.**
- `ER` 파일검증 → `POST /dcrag-to/documents/transfer`
- `TE` 초안생성 → `POST /dcrag-to/documents/draft/generate` (타임아웃·실패 시에만)
- `RK` 금액검증 → `POST /dcrag-to/documents/cost/verify`
6. **진행상태만 바꾸는 버튼은** `POST /fnrs-cnsltn-mng/doc/updatePrgrsStts`**를 재사용한다.** (`ST→LE`, `RM→RB`, `RC→RA`, `RR→EP`, `EP→TA`)
7. **보완요청/보완완료는 공통 API** `reqsupplement` / `submitsupplement`. 복귀 상태(`ER`/`TE`/`RK`)는 서버가 이력에서 결정한다. 프론트는 직전 상태를 추측해 보내지 않는다.
8. **결재 승인/회신은** `PUT /elapr-prgrs-stts/{id}/process` **하나**. 1차 최종승인자 → `TP`, 2차 → `RR`은 서버가 Feign으로 처리한다.
9. **2차 결재선은 EV에서 수동 검토자지정**. `POST /elapr-dmnd` (`elaprCycl: 2`, `docPrgrsSttsComCd: RK`). 1차 결재선 자동 copy-line은 사용하지 않는다.
10. **웹기안기는 `webhwp/open` 응답을 axios `blob`으로 조회한 뒤 `File`로 감싸 `HwpCtrl.Open`에 전달한다.** `blob:` Object URL과 `orgnlOtptDocCn`은 쓰지 않는다.
---
## 6. 화면 데이터 흐름
재원협의 **목록 → 상세/작성**이 본체다. 실무협의 목록은 공단 검토 보조 화면이다.
```
BEGIN
CALL fnrsCnsltnMngService.findPage(...)
ON 행 CLICK → /fnrs-cnsltn/{fnrsCnsltnDocNo}
CALL findById(fnrsCnsltnDocNo)
COMPUTE buttons = getButtonConfigs(dtlInstCd, prgrsSttsComCd, atrzSeComCdList)
// ----- 기초 ROLE_USER05 -----
ON [저장] (빈값/SV) → saveApply(prgrsSttsComCd: SV)
ON [신청완료] (SV) → saveApply(prgrsSttsComCd: ST)
ON [삭제] (SV) → remove
ON [파일보완완료] (SL←ER) → submitsupplement
ON [보완완료] (SL←TE|RK) → submitsupplement
ON [실무협의서 관리] (RB) → GET webhwp/open (blob) → HwpCtrl.Open(File)
ON [조치완료] (RB) → submit-actn-result { isSubmit: true }
ON [결과회신] (TA) → API 없음 (조회)
// ----- 광역 ROLE_USER04 -----
ON [환경청제출] (ST) → updatePrgrsStts(LE)
ON [지자체회신] (RM) → updatePrgrsStts(RB)
ON [조치결과서제출] (RC) → updatePrgrsStts(RA)
ON [결과회신] (EP) → updatePrgrsStts(TA)
// ----- 환경청 ROLE_USER03 -----
ON [결과회신] (LE 자체종결) → tech-review → EP
ON [기술검토 의뢰] (LE) → request-examine → FV → START POLL FV
ON [재원협의 결과 회신] (TP) → tech-review → RM
ON [실무협의서 관리] (RA) → GET webhwp/open (blob) → HwpCtrl.Open(File) (저장 불가)
ON [조치결과 검토의뢰] (RA) → request-actn-review → AV → START POLL AV
ON [결과회신] (RR) → updatePrgrsStts(EP)
// ----- 공단 ROLE_USER01 -----
ON [파일보완] (ER, ATRZ01) → GET file-vrfc-results → 팝업 → reqsupplement
ON [파일검증] (ER, ATRZ01) → dcrag-to transfer (상태 유지)
ON [검토자지정] (ER, ATRZ01) → POST elapr-dmnd (docPrgrsSttsComCd: AC) → START POLL AC
ON [검토자지정] (EV, ATRZ01) → POST elapr-dmnd (elaprCycl: 2, docPrgrsSttsComCd: RK)
ON [실무협의서관리] (TE|RK) → GET webhwp/open (blob) → HwpCtrl.Open(File)
ON [실무협의서생성] (TE, 실패/타임아웃) → dcrag-to draft/generate
ON [검토결과조회] (TE) → pwrk-cnsltn-mng findById | findByUpDocNo
ON [저장] (TE|RK) → pwrk-cnsltn-mng save-apply
ON [검토완료] → elapr-prgrs-stts process (상신)
ON [보완요청] (TE|RK) → 보완 팝업 → reqsupplement
ON [금액검증] (RK) → dcrag-to cost/verify
ON [결재(승인)] → process (중간)
ON [결재(회신)] → process (최종 → TP 또는 RR)
// ----- AI 진행 중 (FV / AC / AV) -----
POLL 상태 또는 draft status. 30분 안내. 종료 후 상세 invalidate
END
```
38행 버튼 표는 [ref-screen-buttons.md](./ref-screen-buttons.md). API 상세는 [ref-endpoint.md](./ref-endpoint.md).
---
## 7. 버튼 → API 축약
프론트가 치는 것만. 콜백·스케줄러는 제외.
| 주체 | 버튼 | From → To | Method & URI |
| --------- | -------------------------- | ----------------------------- | ---------------------------------------------------------------------------------- |
| 기초 | 저장 / 신청완료 | →`SV` / `SV→ST` | `POST /fnrs-cnsltn-mng/save-apply` |
| 기초 | 삭제 | `SV` | `DELETE /fnrs-cnsltn-mng/{docNo}` |
| 광역 | 환경청제출 | `ST→LE` | `POST /fnrs-cnsltn-mng/doc/updatePrgrsStts` |
| 환경청 | 결과회신(자체종결) | `LE→EP` | `POST /fnrs-cnsltn-mng/{docNo}/tech-review` |
| 환경청 | 기술검토 의뢰 | `LE→FV` | **신규** `POST /fnrs-cnsltn-mng/{docNo}/request-examine` |
| 공단 접수 | 파일보완 팝업 | `ER` | `GET /fnrs-cnsltn-mng/{docNo}/file-vrfc-results` |
| 공단 접수 | 파일보완요청 | `ER→SL` | `POST /fnrs-cnsltn-mng/{docNo}/reqsupplement` |
| 공단 접수 | 파일검증 재실행 | `ER` 유지 | `POST /dcrag-to/documents/transfer` |
| 공단 접수 | 검토자지정 | `ER→AC` | `POST /elapr-dmnd` (`elaprCycl: 1`) |
| 공단 접수 | 검토자지정 | `EV→RK` | `POST /elapr-dmnd` (`elaprCycl: 2`) |
| 공단 검토 | 실무협의서관리 | `TE`/`RK` | `GET .../pwrk-cnsltn-doc-crt-rslt/{uuid}/webhwp/open` |
| 공단 검토 | 실무협의서생성 | `TE` 유지 | `POST /dcrag-to/documents/draft/generate` |
| 공단 검토 | 저장 | `TE`/`RK` | `POST /pwrk-cnsltn-mng/save-apply` |
| 공단 | 검토완료 / 결재(승인·회신) | `TE` 유지 / `TE→TP` / `RK→RR` | `PUT /elapr-prgrs-stts/{id}/process` |
| 공단 검토 | 보완요청 | `TE→SL` / `RK→SL` | `POST /fnrs-cnsltn-mng/{docNo}/reqsupplement` |
| 기초 | 파일보완완료 / 보완완료 | `SL→ER` / `TE` / `RK` | `POST /fnrs-cnsltn-mng/{docNo}/submitsupplement` |
| 환경청 | 재원협의 결과 회신 | `TP→RM` | `POST /fnrs-cnsltn-mng/{docNo}/tech-review` |
| 광역 | 지자체회신 | `RM→RB` | `updatePrgrsStts` |
| 기초 | 실무협의서 관리 | `RB` 유지 | `GET .../pwrk-cnsltn-doc-crt-rslt/{uuid}/webhwp/open` |
| 기초 | 조치완료 | `RB→RA` | **신규** `POST /pwrk-cnsltn-mng/{pwrkDocNo}/submit-actn-result` (`isSubmit: true`) |
| 광역 | 조치결과서제출 | `RC→RA` | `updatePrgrsStts` |
| 환경청 | 실무협의서 관리 | `RA` 유지 (조회) | `GET .../pwrk-cnsltn-doc-crt-rslt/{uuid}/webhwp/open` |
| 환경청 | 조치결과 검토의뢰 | `RA→AV` | **신규** `POST /fnrs-cnsltn-mng/{docNo}/request-actn-review` |
| 공단 검토 | 금액검증 재실행 | `RK` 유지 | `POST /dcrag-to/documents/cost/verify` |
| 환경청 | 결과회신 | `RR→EP` | `updatePrgrsStts` |
| 광역 | 결과회신 | `EP→TA` | `updatePrgrsStts` |
| 웹기안기 | 저장 | 버전 +1 | `POST /pwrk-cnsltn-doc-crt-rslt` multipart |
| 공통 | 다운로드 | — | `GET /pwrk-cnsltn-doc-crt-rslt/{uuid}/download` |
신규 4개: `request-examine`, `submit-actn-result`, `request-actn-review`, EV 2차 `elapr-dmnd`.
---
+334
View File
@@ -0,0 +1,334 @@
# 레퍼런스 — 상태별 Endpoint
> 상위: [개발가이드.md](./개발가이드.md)
> SoT: `05.재원협의_상태별_endpoint_설계.md` (v1.0, 2026-09-03)
> Gateway: `/as-service`, `/as-elapr-service`
> Auth: `Authorization`, `X-Site-Id`, `token-id` (기존 `client` 인터셉터)
> 날짜: `yyyy-MM-dd HH:mm:ss`
프론트가 치는 API만 상세히 적는다. AI 콜백·스케줄러·Feign은 [§6 호출 금지](#6-프론트-호출-금지).
---
## 1. 총괄 매트릭스 (38 + WebHWP)
| 순번 | 버튼 | From → To | 서비스 | Method & URI | 구분 |
| :---: | --- | --- | --- | --- | --- |
| 1 | 저장 | →`SV` | as | `POST /api/v1/fnrs-cnsltn-mng/save-apply` | 기존 |
| 2 | 신청완료 | `SV→ST` | as | 동일 (`prgrsSttsComCd=ST`) | 기존 |
| 3 | 삭제 | `SV` | as | `DELETE /api/v1/fnrs-cnsltn-mng/{docNo}` | 기존 |
| 4 | 환경청제출 | `ST→LE` | as | `POST /api/v1/fnrs-cnsltn-mng/doc/updatePrgrsStts` | 기존 |
| 5 | 결과회신(자체종결) | `LE→EP` | as | `POST /api/v1/fnrs-cnsltn-mng/{docNo}/tech-review` | 기존 |
| 6 | 기술검토 의뢰 | `LE→FV` | as | `POST /api/v1/fnrs-cnsltn-mng/{docNo}/request-examine` | **신규** |
| 7 | 파일검증 콜백 | `FV→ER` | as | `POST /api/v1/dcrag/documents/verify-result` | 서버 |
| 8 | 파일보완 팝업 | `ER` | as | `GET /api/v1/fnrs-cnsltn-mng/{docNo}/file-vrfc-results` | 기존 |
| 9 | 파일보완요청 | `ER→SL` | as | `POST /api/v1/fnrs-cnsltn-mng/{docNo}/reqsupplement` | 기존 |
| 10 | 파일검증 재실행 | `ER` | as | `POST /api/v1/dcrag-to/documents/transfer` | 기존 |
| 11 | 검토자지정 | `ER→AC` | elapr | `POST /api/v1/elapr-dmnd` | 기존 |
| 12 | 초안 콜백 | `AC→TE` | as | `POST /api/v1/dcrag/draft/complete` | 서버 |
| 13 | 실무협의서관리 | `TE` | as | `GET /api/v1/pwrk-cnsltn-mng/updocno/{upDocNo}` | 기존 |
| 14 | 검토결과조회 | `TE` | as | `GET /api/v1/pwrk-cnsltn-mng/{pwrkDocNo}` | 기존 |
| 15 | 실무협의서생성 | `TE` | as | `POST /api/v1/dcrag-to/documents/draft/generate` | 기존 |
| 16 | 저장(실무) | `TE` | as | `POST /api/v1/pwrk-cnsltn-mng/save-apply` | 기존 |
| 17 | 검토완료 | `TE` | elapr | `PUT /api/v1/elapr-prgrs-stts/{id}/process` | 기존 |
| 18 | 보완요청 | `TE→SL` | as | `POST .../reqsupplement` | 기존 |
| 19 | 결재(승인) | `TE` | elapr | `PUT .../process` | 기존 |
| 20 | 결재(회신) | `TE→TP` | elapr | `PUT .../process` | 기존 |
| 21 | 파일보완완료 | `SL→ER` | as | `POST .../submitsupplement` | 기존 |
| 22 | 보완완료(기술) | `SL→TE` | as | 동일 | 기존 |
| 23 | 보완완료(조치) | `SL→RK` | as | 동일 | 기존 |
| 24 | 재원협의 결과 회신 | `TP→RM` | as | `POST .../{docNo}/tech-review` | 기존 |
| 25 | 지자체회신 | `RM→RB` | as | `POST .../doc/updatePrgrsStts` | 기존 |
| 26 | 실무협의서 관리 | `RB` | as | `GET .../pwrk-cnsltn-doc-crt-rslt/{uuid}/webhwp/open` | 기존 |
| 27 | 조치완료 | `RB→RA` | as | `POST /api/v1/pwrk-cnsltn-mng/{pwrkDocNo}/submit-actn-result` (`isSubmit=true`) | **신규** |
| 28 | 조치결과서제출 | `RC→RA` | as | `updatePrgrsStts` | 기존 |
| 29 | 조치결과 검토의뢰 | `RA→AV` | as | `POST /api/v1/fnrs-cnsltn-mng/{docNo}/request-actn-review` | **신규** |
| 30 | 금액검증 콜백 | `AV→EV` | as | `POST /api/v1/dcrag/cost/verify-result` | 서버 |
| 30a | 검토자지정(2차) | `EV→RK` | elapr | `POST /api/v1/elapr-dmnd` (`elaprCycl=2`) | 기존 |
| 31 | 실무협의서관리 | `RK` | as | `GET /pwrk-cnsltn-mng/{pwrkDocNo}` | 기존 |
| 32 | 금액검증 재실행 | `RK` | as | `POST /api/v1/dcrag-to/documents/cost/verify` | 기존 |
| 33 | 보완요청(금액) | `RK→SL` | as | `reqsupplement` | 기존 |
| 34 | 결재(승인) | `RK` | elapr | `process` | 기존 |
| 35 | 결재(회신) | `RK→RR` | elapr | `process` | 기존 |
| 36 | 결과회신 | `RR→EP` | as | `updatePrgrsStts` | 기존 |
| 37 | 결과회신 | `EP→TA` | as | `updatePrgrsStts` | 기존 |
| 38 | 결과회신(확인) | `TA` | — | 없음 | — |
| W1 | 웹기안기 Open | — | as | `GET /api/v1/pwrk-cnsltn-doc-crt-rslt/{uuid}/webhwp/open` | 기존 |
| W2 | HWPX 저장 | — | as | `POST /api/v1/pwrk-cnsltn-doc-crt-rslt` multipart | 기존 |
| W3 | 다운로드 | — | as | `GET /api/v1/pwrk-cnsltn-doc-crt-rslt/{uuid}/download` | 기존 |
| E-N | 결재선 복사 | `AV→RK` 부수 | elapr | `POST /api/v1/elapr-apln/copy-line` | **신규·서버** |
조회 공통: `GET /fnrs-cnsltn-mng`, `GET /fnrs-cnsltn-mng/{docNo}`, `GET /pwrk-cnsltn-mng`, `GET /dcrag-to/documents/draft/status/{crtDmndUuid}`.
---
## 2. 공통 요청
### `DocPrgrsUpdateRequestDto`
`POST /fnrs-cnsltn-mng/doc/updatePrgrsStts` — `ST→LE`, `RM→RB`, `RC→RA`, `RR→EP`, `EP→TA`.
```json
{
"docNo": "F-20260824-0001",
"docSeComCd": "FNRS",
"prgrsSttsComCd": "LE",
"rgtrUuid": "optional-uuid",
"rmrk": "optional"
}
```
응답: `204/200` void. 프론트 엔티티: 기존 `changePrgrsSttsComCd`.
### `FnrsCnsltnApplySaveRequestDto`
`save-apply`, `tech-review`, `reqsupplement`, `submitsupplement`가 공유. 핵심은 `fnrsCnsltnMng.prgrsSttsComCd`와 중첩 리스트(`bzScaleList`, `bizCstList`, `cklstResultList`, `esntlAtchFileList`, `splmntDmndList`).
---
## 3. 단계별 FE API
### 3-1. `SV → ST → LE`
**저장 / 신청완료** — `POST /fnrs-cnsltn-mng/save-apply`
권한: `ROLE_USER05`. 임시저장은 `prgrsSttsComCd=SV`, 신청은 `ST`.
응답: `string` (문서번호). 기존 `fnrsCnsltnMngService.saveApply`.
**삭제** — `DELETE /fnrs-cnsltn-mng/{docNo}` (`SV`만).
**환경청제출** — `updatePrgrsStts` `LE`. 권한: `ROLE_USER04`.
### 3-2. `LE → FV | EP`
**자체종결** — `POST /fnrs-cnsltn-mng/{docNo}/tech-review`
Body: `FnrsCnsltnApplySaveRequestDto` (환경청 의견·체크리스트). `LE→EP`.
기존 `techReview`. 현재 화면은 `TP`에서도 이 API를 쓰며 목표 상태를 `EP`로 넣고 있다 → **`TP`는 `RM`으로 바꿔야 함**.
**기술검토 의뢰 (신규)** — `POST /fnrs-cnsltn-mng/{docNo}/request-examine`
```json
{ "prgrsSttsComCd": "FV" }
```
응답: `string` (docNo). `LE→FV` + AI 파일검증 + 30분 타이머(서버).
프론트: `fnrsCnsltnMngService.requestExamine(docNo, body)` 추가. **`updatePrgrsStts("ER")` 금지.**
### 3-3. `FV → ER → SL → ER`
파일검증 콜백은 서버. FE는 `FV`에서 상세 refetch.
**검증결과 목록** — `GET /fnrs-cnsltn-mng/{fnrsCnsltnDocNo}/file-vrfc-results`
응답: `FnrsCnsltnFileVrfcRsltResponseDto[]`
| 필드 | 설명 |
| --- | --- |
| `fileSeComCd` | `ESSENTIAL` / `ADDITIONAL` |
| `fileSeNm` | 구분명 |
| `esntlAtchFileId` | 필수첨부 ID |
| `atchFileNm` | 파일명 |
| `vrfcRsltComCd` | `PASS` / `FAIL` / `WARN` |
| `vrfcOpnn` | 검증의견 |
엔티티에 `findFileVrfcResults` 추가. 첨부 행 배지는 상세 `esntlFileList`/`etcFileList`의 `vrfcSttsComCd`, `vrfcRsltCn`, `vrfcDt`도 사용 (타입 확장 필요).
**파일보완요청** — `POST .../{docNo}/reqsupplement` (`ER→SL`).
**파일보완완료** — `POST .../{docNo}/submitsupplement` (`SL→ER`). 복귀는 서버.
**파일검증 재실행** — `POST /dcrag-to/documents/transfer`
```json
{ "fnrsCnsltnDocNo": "F-20260824-0001", "taskSeComCd": "SF", "taskSeCdNm": "하수처리시설" }
```
응답: `{ resultCode, message, requestId }`. 상태 유지. 기존 `dcRagApiToService.requestDocumentTransfer`.
### 3-4. `ER → AC → TE → TP`
**검토자지정** — `POST /elapr-dmnd`
기존 `elaprDmndService.save`.
| 필드 | 값 |
| --- | --- |
| `elaprDocNo` | 재원협의 번호 |
| `elaprCycl` | `1` |
| `docPrgrsSttsComCd` | **`AC`** (현행은 현재 상태 `ER`를 넣고 있음 → 수정) |
| `elaprPrgrsSttsList[].atrzOrdr` | 1 검토자, 2 승인자, 3 최종 |
서버가 실무협의 문서 생성 + RAG 초안 트리거 + 30분 타이머. 응답의 문서번호/`crtDmndUuid`가 있으면 폴링 키로 쓴다. 없으면 `findByUpDocNo`로 복원.
**초안 상태 폴링** — `GET /dcrag-to/documents/draft/status/{crtDmndUuid}`
기존 `getDraftGenerationStatus`. 주기 5~10초. 종료 `COMPLETED`/`FAILED`.
**수동 초안생성** — `POST /dcrag-to/documents/draft/generate` (`TE` 유지)
```json
{
"fnrsCnsltnDocNo": "F-...",
"pwrkCnsltnDocNo": "P-...",
"crtDmndUuid": "optional"
}
```
202 + `DcRagDraftGenerateResponseDto` (`crtDmndUuid`, `crtSttsComCd`, `estimatedMinutes`).
v1.0 가이드의 “이 API를 프론트가 치면 안 된다”는 **폐기**. 1차 트리거는 검토자지정이고, 이 API는 Fallback 전용.
**실무 저장** — `POST /pwrk-cnsltn-mng/save-apply` → `string`.
**보완요청/완료** — fnrs `reqsupplement` / `submitsupplement`.
**결재** — `PUT /elapr-prgrs-stts/{elaprPrgrsId}/process`
```json
{
"atrzPrgrsSttsComCd": "ATRZ_PRGRS_003",
"atrzOpnnCn": "의견",
"finalApprovalYn": false
}
```
반려: `ATRZ_PRGRS_004`. 기존 `elaprPrgrsSttsService.process`. 최종 시 서버가 `TP`/`RR` 전이.
### 3-5. `TP → RM → RB → RA`
**환경청 1차 회신** — `tech-review` (`TP→RM`).
**광역 지자체회신** — `updatePrgrsStts` `RB`.
**조치완료 (신규)** — `POST /pwrk-cnsltn-mng/{pwrkDocNo}/submit-actn-result`
```json
{
"isSubmit": true,
"actnList": [
{
"rvwOpnnSn": 1,
"actnRsltCn": "수용 — 관로 연장 재산정",
"atchFileGroupId": "optional"
}
],
"totActnSummary": "제출 시 권장",
"muniDocNo": "제출 시 권장"
}
```
- `isSubmit: false` → `RB` 유지 (API만 존재, **화면 버튼 없음**. HWP·첨부는 실무협의서 관리 다이얼로그에서 저장)
- `isSubmit: true` → `RB→RA` (조치완료)
응답: `string` (pwrkDocNo). DTO명 `PwrkActnResultSubmitDto`.
### 3-6. `RA → AV → EV → RK`
**2차 의뢰 (신규)** — `POST /fnrs-cnsltn-mng/{docNo}/request-actn-review`
```json
{
"specialRqstCn": "중점 검증 요청 (선택)"
}
```
`RA→AV`. 응답: `string` (docNo). DTO명 `EnvoActnReviewReqDto`. `specialRqstCn`만 선택.
금액검증 콜백은 서버 → `AV→EV`.
**2차 검토자지정** — `POST /elapr-dmnd` (`elaprCycl: 2`, `docPrgrsSttsComCd: RK`). 1차 결재선(`findDocPrgrsByDocNo`, cycl=1)을 시드로 편집.
**금액검증 재실행** — `POST /dcrag-to/documents/cost/verify`
```json
{
"fnrsCnsltnDocNo": "F-...",
"pwrkCnsltnDocNo": "P-..."
}
```
202. 기존 `requestCostVerification`. `RK` 유지.
### 3-7. `RK → RR | SL`
보완은 3-4와 동일 API. 팝업만 금액 체크.
결재 `process` 2차 최종 → 서버가 `RR`.
### 3-8. `RR → EP → TA`
둘 다 `updatePrgrsStts`. `TA` 확인은 API 없음.
---
## 4. 웹기안기
설계서 기준 **기존**. 구현 시 컨트롤러 존재 여부를 확인한다.
| ID | 용도 | URI | 응답 |
| --- | --- | --- | --- |
| W1 | 웹기안기 Open | `GET .../pwrk-cnsltn-doc-crt-rslt/{crtDmndUuid}/webhwp/open` | `inline` HWPX blob → `HwpCtrl.Open(File)` |
| W2 | 편집 저장 | `POST .../pwrk-cnsltn-doc-crt-rslt` multipart `file` + `crtDmndUuid` | 201, `verSn+1` |
| W3 | PC 저장 | `GET .../{crtDmndUuid}/download` | `attachment` blob |
버전 지정: `GET .../{crtDmndUuid}/{verSn}/webhwp/open`, `.../{verSn}/download`.
`/{uuid}/latest`, `/{uuid}/download`가 `/{uuid}/{verSn}`보다 우선. path를 섞지 말 것.
다운로드:
- axios `responseType: "blob"`
- 파일명: `Content-Disposition` `filename*=UTF-8''...` (`parseFilenameFromResponse`)
- 서버 파일명: `재원협의 실무협의서({bizNm})_v{verSn}.hwp`
- Content-Type은 `application/x-hwp` 또는 `application/haansofthwpx`일 수 있음. 타입에 의존하지 말 것
Open은 axios `responseType: "blob"`으로 인증된 API를 호출하고, 응답 Blob을 HWPX `File`로 감싸 `HwpCtrl.Open`에 전달한다. `blob:` Object URL은 사용하지 않는다.
multipart `Content-Type` 헤더를 수동 지정하지 않는다 (boundary). JSON `saveJson`은 UI에서 쓰지 않는다.
---
## 5. 전자결재 (`as-elapr-service`)
| API | URI | FE |
| --- | --- | --- |
| 검토자지정·1차 결재요청 | `POST /elapr-dmnd` | O |
| 결재 처리 | `PUT /elapr-prgrs-stts/{id}/process` | O |
| 결재선 복사 | `POST /elapr-apln/copy-line` | **X (서버)** |
`copy-line` body (참고만):
```json
{
"docNo": "P-20260827-0001",
"sourceElaprCycl": 1,
"targetElaprCycl": 2,
"rgtrUuid": "system-uuid"
}
```
`RK`에서 결재현황은 `findDocPrgrsByDocNo`(전체) 조회 후 `elaprCycl`로 1차/2차 탭 표시.
---
## 6. 프론트 호출 금지
| URI | 이유 |
| --- | --- |
| `POST /dcrag/documents/verify-result` | AI→AS 콜백 |
| `POST /dcrag/draft/complete` | AI→AS 콜백 |
| `POST /dcrag/cost/verify-result` | AI→AS 콜백. 여기서 copy-line도 호출 |
| `POST /elapr-apln/copy-line` | 콜백 Feign |
| `POST /pwrk-cnsltn-mng` (`save` 초안 트리거) | v3.0 1차 트리거는 `elapr-dmnd`. 컨트롤러가 남아 있어도 화면에서 쓰지 않음 |
`POST /dcrag-to/documents/draft/generate`는 **Fallback에서만** 허용.
---
## 7. Gap Analysis (프론트 엔티티)
### 이미 있는 것
`fnrsCnsltnMngService`: `findPage`, `findById`, `save`, `saveApply`, `update`, `techReview`, `submitSupplement`, `submitReqSupplement`, `remove`, `changePrgrsSttsComCd`
`dcRagApiToService`: `requestDocumentTransfer`, `requestDraftGeneration`, `getDraftGenerationStatus`, `requestCostVerification`
`elaprDmndService.save`, `elaprPrgrsSttsService.process`
### 추가할 것
| 메서드 | URI |
| --- | --- |
| `requestExamine` | `POST /fnrs-cnsltn-mng/{id}/request-examine` |
| `findFileVrfcResults` | `GET /fnrs-cnsltn-mng/{id}/file-vrfc-results` |
| `requestActnReview` | `POST /fnrs-cnsltn-mng/{id}/request-actn-review` |
| `pwrkCnsltnMngService.*` | 슬라이스 신규 |
| `submitActnResult` | `POST /pwrk-cnsltn-mng/{id}/submit-actn-result` |
| `pwrkCnsltnDocCrtRsltService.*` | 슬라이스 신규 |
`copyLine`은 프론트 서비스에 넣지 않는다.
@@ -0,0 +1,350 @@
# 레퍼런스 — 프론트 엔티티·DTO
> 상위: [개발가이드.md](./개발가이드.md)
> 엔티티 규칙: `frontend/.cursor/skills/backend-controller-to-entity`
---
## 1. ENDPOINT
`apps/as/src/shared/config/api.ts`
```ts
AS_SERVICE: {
// 기존
FNRS_CNSLTN_MNG: "/as-service/api/v1/fnrs-cnsltn-mng",
DCRAG_TO: "/as-service/api/v1/dcrag-to",
// 추가
PWRK_CNSLTN_MNG: "/as-service/api/v1/pwrk-cnsltn-mng",
PWRK_CNSLTN_DOC_CRT_RSLT: "/as-service/api/v1/pwrk-cnsltn-doc-crt-rslt",
}
AS_ELAPR_SERVICE: {
ELAPR_TMPLT: "/as-elapr-service/api/v1/elapr-tmplt",
ELAPR_APLN: "/as-elapr-service/api/v1/elapr-apln",
ELAPR_DMND: "/as-elapr-service/api/v1/elapr-dmnd",
ELAPR_PRGRS_STTS: "/as-elapr-service/api/v1/elapr-prgrs-stts",
}
```
`copy-line`은 `ELAPR_APLN` 하위이나 FE에서 호출하지 않음.
---
## 2. 슬라이스 배치
```
entities/fnrs-cnsltn/ # 확장
entities/dcrag-to/ # 구현됨
entities/elapr/ # 구현됨
entities/pwrk-cnsltn-mng/ # 신규
entities/pwrk-cnsltn-doc-crt-rslt/ # 신규
```
```
entities/pwrk-cnsltn-mng/
├── model/
│ ├── pwrk-cnsltn-mng-interface.ts
│ └── pwrk-cnsltn-mng-payload-schema.ts
├── api/
│ ├── pwrk-cnsltn-mng-service.ts
│ └── queries.ts
└── index.ts
```
`submit-actn-result` Request는 zod SSOT (`PwrkActnResultSubmitDto`).
`request-examine` / `request-actn-review`도 fnrs payload-schema에 추가.
라우트:
| 화면 | 경로 |
| --- | --- |
| 재원협의 목록/상세/작성 | 기존 `app/(main)/(with-base-layout)/fnrs-cnsltn/...` |
| 실무협의 목록 | `.../pwrk-cnsltn/page.tsx` (선택) |
| 실무협의 상세 | `.../pwrk-cnsltn/[id]/page.tsx` (선택) |
| 페이지 UI | `pages/fnrs-cnsltn/`, `pages/pwrk-cnsltn/` |
---
## 3. Service 시그니처
`fnrsCnsltnMngService`에 추가:
```ts
requestExamine(docNo: string, body: FnrsExamineRequestDto): Promise<string>;
findFileVrfcResults(fnrsCnsltnDocNo: string): Promise<FnrsCnsltnFileVrfcRsltResponseDto[]>;
requestActnReview(docNo: string, body: EnvoActnReviewReqDto): Promise<string>;
```
실무협의:
```ts
export interface PwrkCnsltnMngService {
findPage(searchDto: PwrkCnsltnMngSearchDto): Promise<PageResponse<PwrkCnsltnMngResponseDto>>;
findById(pwrkCnsltnDocNo: string): Promise<PwrkCnsltnMngDetResponseDto>;
findByUpDocNo(upDocNo: string): Promise<PwrkCnsltnMngDetResponseDto>;
saveApply(body: PwrkCnsltnApplySaveRequestDto): Promise<string>;
submitActnResult(
pwrkCnsltnDocNo: string,
body: PwrkActnResultSubmitDto,
): Promise<string>;
update(pwrkCnsltnDocNo: string, body: PwrkCnsltnMngUpdateRequestDto): Promise<string>;
remove(pwrkCnsltnDocNo: string): Promise<void>;
changePrgrsSttsComCd(body: DocPrgrsUpdateRequestDto): Promise<void>;
}
```
v1.0의 `save(): PwrkCnsltnMngSaveResponseDto`(초안 트리거)는 화면에서 사용하지 않는다. 컨트롤러가 남아 있으면 엔티티에 넣더라도 UI 연결 금지.
생성결과:
```ts
export interface PwrkCnsltnDocCrtRsltService {
findPage(
searchDto: PwrkCnsltnDocCrtRsltSearchDto,
): Promise<PageResponse<PwrkCnsltnDocCrtRsltResponseDto>>;
findById(crtDmndUuid: string, verSn: number): Promise<PwrkCnsltnDocCrtRsltResponseDto>;
findLatestByCrtDmndUuid(crtDmndUuid: string): Promise<PwrkCnsltnDocCrtRsltResponseDto>;
openWebHwpLatest(crtDmndUuid: string): Promise<AxiosResponse<Blob>>;
download(crtDmndUuid: string, verSn: number): Promise<AxiosResponse<Blob>>;
downloadLatest(crtDmndUuid: string): Promise<AxiosResponse<Blob>>;
saveMultipart(formData: FormData): Promise<PwrkCnsltnDocCrtRsltResponseDto>;
updateMultipart(
crtDmndUuid: string,
verSn: number,
formData: FormData,
): Promise<PwrkCnsltnDocCrtRsltResponseDto>;
remove(crtDmndUuid: string, verSn: number): Promise<void>;
}
```
`webhwp/open`은 인증된 axios 요청으로 Blob을 조회한다:
```ts
async function openWebHwpLatest(crtDmndUuid: string) {
return client.get<Blob>(`${BASE}/${crtDmndUuid}/webhwp/open`, {
responseType: "blob",
});
}
```
응답 Blob은 HWPX `File`로 감싸 `HwpCtrl.Open(file, "HWPX", ...)`에 전달한다. `blob:` Object URL은 사용하지 않는다.
DC RAG TO (구현됨, 시그니처 유지):
```ts
requestDocumentTransfer(body: DcRagDocumentTransferRequestDto): Promise<DcRagCommonResponseDto>;
requestDraftGeneration(body: DcRagDraftGenerateRequestDto): Promise<DcRagDraftGenerateResponseDto>;
getDraftGenerationStatus(crtDmndUuid: string): Promise<DcRagDraftStatusResponseDto>;
requestCostVerification(body: DcRagCostVerifyRequestDto): Promise<DcRagCommonResponseDto>;
```
---
## 4. DTO
타입명 = 백엔드 클래스명. Save/Apply는 zod.
### 4-1. 재원협의 추가
**`FnrsExamineRequestDto`**
| 필드 | 필수 | 값 |
| --- | --- | --- |
| `prgrsSttsComCd` | N | 기본 `FV` |
**`EnvoActnReviewReqDto`**
| 필드 | 필수 |
| --- | --- |
| `specialRqstCn` | N |
**`FnrsCnsltnFileVrfcRsltResponseDto`**: [ref-endpoint.md](./ref-endpoint.md) §3-3.
첨부 검증 배지용으로 `AtflMngClientResponseDto` / `EsntlAtchFileMngTreeResponseDto`에 `vrfcDt`, `vrfcRsltCn`, `vrfcSttsComCd` 확장.
### 4-2. `PwrkActnResultSubmitDto` (zod)
| 필드 | 필수 | 비고 |
| --- | --- | --- |
| `isSubmit` | Y | `false` API만(화면 버튼 없음), `true` 조치완료(`RB→RA`) |
| `actnList` | N | 조치완료 시 선택 |
| `actnList[].rvwOpnnSn` | Y (항목 있을 때) | |
| `actnList[].actnRsltCn` | N | |
| `actnList[].atchFileGroupId` | N | |
| `totActnSummary` | N | 제출 시 권장 |
| `muniDocNo` | N | 제출 시 권장 |
| `pwrkAtchFileGroupId` | N | |
### 4-3. `PwrkCnsltnMng*`
**Search**: `pwrkCnsltnDocNo`, `upDocNo`, `upDocSeComCd`, `prgrsSttsComCd`, `admdstCd`, `envoSeComCd`, `taskSeComCd`, `bizNm`
**DetResponse 중첩**
- `cnsltnList`: 협의내역 (`rvwOpnnSn`, `rvwOpnnClsfComCd`, `cnsltnCn`, `rfltYn`, …)
- `pjtcoAjmtList`: 사업비조정
- `crtDmndList[].crtRslt`: 생성요청 + 최신 결과
- `splmntDmndList`: 보완요청
- `sttsChgHstryList`: 상태변경이력 — `SL` 복귀 라벨에 사용
- `trsmFileList`: 전송파일 (Java Det DTO 확인 후 반영)
마스터 조인: `bizNm`, `admdstCd`, `envoSeComCd`, `taskSeComCd`, `aplcntNm` 등.
`crtRslt.orgnlOtptDocCn`은 항상 `null`.
**상세 예**
```json
{
"pwrkCnsltnDocNo": "P-20260827-0001",
"upDocNo": "F-20260824-0001",
"upDocSeComCd": "FNRS",
"prgrsSttsComCd": "TE",
"cnsltnList": [],
"pjtcoAjmtList": [],
"crtDmndList": [
{
"crtDmndUuid": "d0545bef-735f-4d7b-a781-88e96e03577a",
"pwrkCnsltnDocNo": "P-20260827-0001",
"rqstrUuid": "...",
"rqstrNm": "관리자",
"crtSttsComCd": "C",
"crtRslt": {
"crtDmndUuid": "d0545bef-735f-4d7b-a781-88e96e03577a",
"verSn": 1,
"rgtrNm": "관리자",
"orgnlOtptDocCn": null,
"crtCmptnDt": "2026-08-27 10:47:01"
}
}
],
"splmntDmndList": [],
"sttsChgHstryList": []
}
```
### 4-4. `PwrkCnsltnDocCrtRslt*`
복합 PK: `crtDmndUuid` + `verSn`
**Response**
| 필드 | 타입 | 비고 |
| --- | --- | --- |
| `crtDmndUuid` | `string` | |
| `verSn` | `number` | |
| `rgtrUuid` / `rgtrNm` | `string` | |
| `orgnlOtptDocCn` | `null` | 목록/상세 미포함 |
| `crtCmptnDt` | `string` | |
**Search** (`PageableParams`): `crtDmndUuid`(이력 조회 시 필수), `pwrkCnsltnDocNo`, `verSn`, `rgtrUuid`
```http
GET /as-service/api/v1/pwrk-cnsltn-doc-crt-rslt?page=0&size=10&crtDmndUuid={uuid}&sort=verSn,desc
```
**Save (zod)**: `crtDmndUuid` 필수. `rgtrUuid` 생략 시 서버 로그인 사용자. `file`은 multipart part (DTO 필드 아님).
### 4-5. DC RAG (구현됨)
```ts
interface DcRagDocumentTransferRequestDto {
fnrsCnsltnDocNo: string;
taskSeComCd?: string;
taskSeCdNm?: string;
}
interface DcRagDraftStatusResponseDto {
crtDmndUuid: string;
status: DraftStatus;
statusName?: string;
progressPercent?: number;
events?: EventInfoDto[];
startedDt?: string;
estimatedCompletionDt?: string;
errorMessage?: string | null;
}
type DraftStatus =
| "REQUESTED"
| "DOWNLOADING"
| "ANALYZING"
| "GENERATING"
| "COMPLETED"
| "FAILED";
```
`estimatedMinutes`는 초안 응답에 있을 수 있으나 UX 안내는 **30분 Fallback**을 따른다.
---
## 5. 폴링 훅
초안 (`AC` 또는 `TE`이면서 생성 중):
```ts
useQuery({
queryKey: [...dcragToKeys.draftStatus(crtDmndUuid)],
queryFn: () => dcRagApiToService.getDraftGenerationStatus(crtDmndUuid),
enabled: Boolean(crtDmndUuid) && isPolling,
refetchInterval: (query) => {
const status = query.state.data?.status;
if (status === "COMPLETED" || status === "FAILED") return false;
return 5_000;
},
});
```
`COMPLETED` 시 `fnrs-cnsltn` 상세·`pwrk-cnsltn-mng` `findByUpDocNo` invalidate.
`FV`/`AV`는 `findById`를 5~10초 refetch. `prgrsSttsComCd`가 `ER`/`RK`가 되면 중단. 시작 시각 기준 30분 경과 안내 문구.
`crtDmndUuid` 복원: `findByUpDocNo(fnrsCnsltnDocNo).crtDmndList[0]`.
---
## 6. 다운로드
```ts
const response = await client.get<Blob>(url, { responseType: "blob" });
const filename = parseFilenameFromResponse(response);
const objectUrl = window.URL.createObjectURL(response.data);
```
기존 `attachService`와 동일.
---
## 7. 실무협의 목록 컬럼 (선택 화면)
| 표시명 | Field |
| --- | --- |
| 실무협의번호 | `pwrkCnsltnDocNo` |
| 재원협의번호 | `upDocNo` |
| 진행상태 | `prgrsSttsComCdNm` |
| 사업명 | `bizNm` |
| 시도/구군 | `sido` / `gugun` |
| 신청자 | `aplcntNm` |
| 등록일 | `regDt` |
행 클릭 → `/pwrk-cnsltn/{pwrkCnsltnDocNo}` 또는 재원협의 상세로 이동 (화면설계 확정 후).
---
## 8. `elapr-dmnd` 저장 시 주의
현행 `floating-action-buttons.tsx`:
```ts
elaprCycl: data.detDto?.prgrsSttsComCd === "RK" ? 2 : 1,
docPrgrsSttsComCd: data.detDto?.prgrsSttsComCd, // ER
```
To-Be:
- 검토자지정은 **`ER`(1차)** 과 **`EV`(2차)** 에서.
- `ER`: `elaprCycl: 1`, `docPrgrsSttsComCd: "AC"`, 서식 선택 다이얼로그
- `EV`: `elaprCycl: 2`, `docPrgrsSttsComCd: "RK"`, 1차 결재선 시드 다이얼로그(서식 없음)
`RK` / ATRZ02 FAB: `findDocPrgrsByDocNo` 전체 조회 후 pending은 `elaprCycl === 2`만 사용. 검토완료 후 `ElaprApprovalDialog`에 `elaprCycl={2}`. 보완요청은 TE와 동일 `SplmntDmndDialog`.
@@ -0,0 +1,246 @@
# 레퍼런스 — 프로세스·상태 전이
> 상위: [개발가이드.md](./개발가이드.md)
> SoT: `03.재원협의_실무협의_프로세스_통합_v완결.md` (v3.0, 2026-09-03)
---
## 1. 프로세스 개요
| 항목 | 내용 |
| --- | --- |
| Process No. | PR-AS-003 |
| 메뉴 | 행정지원 > 2.2.2 재원협의 > 2.2.2.1 재원협의 관리 |
| 목적 | 기초 → 광역(경유) → 환경청(승인) → 공단(기술검토/AI) 4자 협의로 사업규모·국비·분담금을 확정 |
2-Pass:
- **Pass 1** 기술 타당성: `SV→ST→LE→FV→ER→AC→TE→TP→RM→RB`
- **Pass 2** 조치결과·금액: `RB→RA→AV→EV→RK→RR→EP→TA`
자체종결(기술검토 생략): `LE→EP→TA`
---
## 2. 4-Lane 시퀀스
```mermaid
sequenceDiagram
autonumber
actor 기초 as 기초 (ROLE_USER05)
actor 광역 as 광역 (ROLE_USER04)
actor 환경청 as 환경청 (ROLE_USER03)
actor 공단 as 공단 (ROLE_USER01)
actor AI as AI 엔진
rect rgb(240, 248, 255)
기초->>기초: [저장] SV
기초->>광역: [신청완료] ST
광역->>환경청: [환경청제출] LE
end
rect rgb(255, 250, 240)
alt 자체종결
환경청->>광역: [결과회신] LE → EP
광역->>기초: [결과회신] EP → TA
else 기술검토 의뢰
환경청->>AI: [기술검토 의뢰] LE → FV
end
end
rect rgb(245, 255, 245)
AI-->>공단: 파일검증 콜백 FV → ER
Note over 공단: 30분 미응답 시 ER + 수동 [파일검증]
alt 파일 미비
공단->>기초: [파일보완] ER → SL
기초->>공단: [파일보완완료] SL → ER
else 적격
공단->>AI: [검토자지정] ER → AC
end
end
rect rgb(255, 245, 245)
AI-->>공단: 초안 콜백 AC → TE
Note over 공단: 30분 미응답 시 TE + 수동 [실무협의서생성]
alt 설계 미비
공단->>기초: [보완요청] TE → SL
기초->>공단: [보완완료] SL → TE
else 검토 완료
공단->>공단: [검토완료] 상신, [결재(승인)] TE 유지
공단->>환경청: [결재(회신)] TE → TP
end
end
rect rgb(250, 240, 255)
환경청->>광역: [재원협의 결과 회신] TP → RM
광역->>기초: [지자체회신] RM → RB
기초->>환경청: [조치완료] RB → RA
end
rect rgb(240, 255, 255)
광역->>환경청: [조치결과서제출] RC → RA
환경청->>AI: [조치결과 검토의뢰] RA → AV
AI-->>공단: 금액검증 콜백 AV → EV
공단->>공단: [검토자지정] EV → RK
Note over 공단: EV에서 1차 결재선 시드로 2차 결재선 수동 배정
end
rect rgb(255, 255, 230)
alt 소명 미비
공단->>기초: [보완요청] RK → SL
기초->>공단: [보완완료] SL → RK
else 확정
공단->>환경청: [결재(회신)] RK → RR
end
end
rect rgb(255, 240, 240)
환경청->>광역: [결과회신] RR → EP
광역->>기초: [결과회신] EP → TA
end
```
거버넌스:
```
기초 (USER05) --ST--> 광역 (USER04) --LE--> 환경청 (USER03)
기초 <-----TA------ 광역 <-----EP/RM------ 환경청
환경청 --FV/ER--> 공단 (USER01) --TP/RR--> 환경청
```
---
## 3. Activity 정의 (14)
| No. | Activity | 주체 | From → To | FE 포인트 |
| --- | --- | --- | --- | --- |
| 1 | 재원협의 조회 | 기초 | — | 기본계획·자가점검 대조 |
| 2 | 등록 및 제출 | 기초·광역 | `SV→ST`, 광역 `LE` | `save-apply`, `updatePrgrsStts` |
| 3 | 접수 및 의뢰 | 환경청 | `LE→FV` / `EP` | 의뢰 vs 자체종결 분기 |
| 4 | AI 파일검증 | 공단·AI | `FV→ER` / `SL` | 30분 Fallback, 파일보완 팝업 |
| 5 | 자체종결 회신 | 환경청·광역 | `LE→EP→TA` | 공단 레인 없음 |
| 6 | 검토자지정·초안 | 공단·AI | `ER→AC→TE` | `elapr-dmnd`, 초안 폴링 |
| 7 | 1차 기술검토 | 공단 결재선 | `TE` / `SL` | 웹기안기, HWP 파싱은 서버 |
| 8 | 기술검토 회신 | 공단 최종 | `TE→TP` | `process` 최종 |
| 9 | 검토결과 회신 | 환경청·광역 | `TP→RM→RB` | 1차 회신. `EP`와 혼동 금지 |
| 10 | 조치결과 작성 | 기초 | `RB→RA` | `submit-actn-result` `isSubmit:true` (조치완료) |
| 11 | 조치 의뢰 | 환경청 | `RA→AV` | `request-actn-review` |
| 12 | AI 금액검증 | AI·공단 | `AV→EV` | 콜백/Fallback 후 접수자 대기 |
| 12a | 2차 검토자지정 | 공단 접수 | `EV→RK` | `elapr-dmnd` cycl=2 |
| 13 | 2차 검토·결재 | 공단 | `RK→RR` / `SL` | 2차 결재선 |
| 14 | 최종 회신 | 환경청·광역·기초 | `RR→EP→TA` | 종결 |
---
## 4. `FNRSSTTS` 상세
관리: `tb_cs_lwpo_cd` / `tb_as_fnrs_cnsltn_mng.prgrs_stts_com_cd`
| 코드 | 국문 | 영문 | 화면 주체 | 전이 |
| --- | --- | --- | --- | --- |
| `SV` | 저장 | Save | 기초 작성 | 신규/`SV` → `SV` |
| `ST` | 신청완료 | Submit to Province | 기초→광역 | `SV→ST` |
| `LE` | 환경청 검토 | Local Environment Review | 광역→환경청 | `ST→LE` |
| `FV` | 파일검증 | File Verification | AI | `LE→FV` |
| `ER` | 기술검토 의뢰 | Examine Request | 공단 접수자 | `FV→ER`, `SL→ER` |
| `SL` | 보완요청 | Supplement Loop | 공단→기초 | `ER\|TE\|RK → SL` |
| `AC` | 초안생성 | AI Auto Creation | AI | `ER→AC` |
| `TE` | 기술검토 | Technical Examine | 공단 1차 결재 | `AC→TE`, `SL→TE` |
| `TP` | 기술검토 회신 | Technical Pass to Basin | 공단→환경청 | `TE→TP` |
| `RM` | 재원협의결과회신 | Result Message to Province | 환경청→광역 | `TP→RM` |
| `RB` | 재원협의결과회신(지자체) | Result Back to Municipality | 광역→기초 | `RM→RB` |
| `RC` | 조치결과서 제출 | Result Complete | 기초→광역 | `RB→RC` |
| `RA` | 조치결과서 제출(환경청) | Result Ask to Basin | 기초→환경청 | `RB→RA` |
| `AV` | 금액검증 | Amount Verification | AI | `RA→AV` |
| `EV` | 조치결과검토의뢰 | Examine Verify | 공단 접수자 | `AV→EV` |
| `RK` | 조치결과서검토 | Result Check | 공단 2차 결재 | `EV→RK`, `SL→RK` |
| `RR` | 조치결과서검토 회신 | Result Return to Basin | 공단→환경청 | `RK→RR` |
| `EP` | 검토결과 회신 | Examine Pass to Province | 환경청→광역 | `LE→EP`(자체), `RR→EP`(최종) |
| `TA` | 검토결과 회신(종료) | Technical Approval | 광역→기초 | `EP→TA` |
### 4-1. v2.1 → v3.0 대조 (프론트 폐기 대상)
| 구간 | v2.1 | v3.0 |
| --- | --- | --- |
| 의뢰 직후 파일검증 | `ER` | **`FV` → `ER`** |
| 검토자지정 후 초안 | `EE` → `DV` | **`ER` → `AC` → `TE`** |
| 1차 내부 결재 | `TR` → `TP` | **`TE` 유지 → `TP`** |
| 환경청→광역 1차 회신 | `EP` | **`RM`** |
| 금액검증 | `RA` | **`RA` → `AV` → `RK`** |
| 공단 2차 회신 | `TP` | **`RR`** |
| 최종 완료 | `TP` / 고시 `TB` | **`TA`** (고시 없음) |
가이드 v1.0 `PWRKSTTS`(`DV`, `AR`, `TR`, `AA` 등)도 동일하게 폐기.
---
## 5. 보완(`SL`) 3루프
복귀 상태는 `tb_as_doc_stts_chg_hstry` 직전 코드. 프론트는 복귀 코드를 요청에 넣지 않고, 라벨만 이력으로 고른다.
```mermaid
stateDiagram-v2
ER --> SL: 파일보완 팝업(파일리스트)
SL --> ER: 파일보완완료
TE --> SL: 보완 검토팝업()
SL --> TE: 보완완료
RK --> SL: 보완 검토팝업(금액)
SL --> RK: 보완완료
```
| 루프 | 팝업 | 요청 | 제출 버튼 |
| --- | --- | --- | --- |
| 파일 무결성 | 파일리스트 + 검증결과 | 공단 접수자 `ER` | [파일보완완료] |
| 1차 기술 | 설계 미달·사유 | 공단 검토자 `TE` | [보완완료] |
| 2차 금액 | 공종별 금액 체크 | 공단 검토자 `RK` | [보완완료] |
---
## 6. AI 3파이프라인·30분 Fallback
| ID | 단계 | 진입 → 완료 | 프론트 진입 | Fallback | 수동 재실행 |
| --- | --- | --- | --- | --- | --- |
| AI-1 | 파일 무결성 | `FV→ER` | 환경청 [기술검토 의뢰] | 30분 → `ER` + [파일검증] | `POST /dcrag-to/documents/transfer` |
| AI-2 | 초안 생성 | `AC→TE` | 공단 [검토자지정] | 30분 → `TE` + [실무협의서생성] | `POST /dcrag-to/documents/draft/generate` |
| AI-3 | 금액 검증 | `AV→EV` | 환경청 [조치결과 검토의뢰] | 30분 → `EV` + 수동 [금액검증] | `POST /dcrag-to/documents/cost/verify` |
- 예상 소요 안내는 **30분** (v1.0의 10분은 폐기).
- 콜백 수신·스케줄러 전이는 서버. FE는 상세 refetch / draft status 폴링만.
- `AC→TE` 성공 시에만 [실무협의서관리]·[검토결과조회] 활성. 실패·타임아웃이면 [실무협의서생성].
초안 폴링 이벤트 예:
| `eventType` | `eventMessage` |
| --- | --- |
| `REQUESTED` | 요청을 접수했습니다. |
| `DOWNLOADING` | 신청서를 수신하는 중입니다. |
| `ANALYZING` | 자료 준비가 완료되었습니다. |
| `GENERATING` | 문서 저장이 완료되었습니다. |
| `COMPLETED` | 문서 생성이 완료되었습니다. |
| `FAILED` | `errorMessage` 표시 |
종료: `status === "COMPLETED" | "FAILED"` 또는 `events`에 해당 `eventType`.
---
## 7. 2차 결재선 (수동 검토자지정)
1. `TE`에서 1차 결재선(`elaprCycl = 1`) 완료.
2. 금액검증 콜백/Fallback → `AV→EV`.
3. 공단 접수자가 `EV`에서 [검토자지정]: 1차 결재선을 시드로 편집 후 `POST /elapr-dmnd` (`elaprCycl: 2`) → `RK`.
4. `copy-line` 자동 복사는 사용하지 않는다.
결재 `process`:
- 상신·중간 승인: 문서 상태 `TE` 또는 `RK` 유지.
- 1차 최종: 서버가 `TE→TP`.
- 2차 최종: 서버가 `RK→RR`.
- 반려: 직전 결재자 복귀. 문서 상태 유지.
---
## 8. 웹기안기
- `TE`/`RK` [실무협의서관리] → `webhwp/open` Blob 조회 후 팝업에서 `HwpCtrl.Open(File)`.
- [검토완료] 시 HWP 파싱·DB 등재는 **서버**. FE는 결재 `process`만.
- 편집본 저장: `POST /pwrk-cnsltn-doc-crt-rslt` multipart. `verSn`은 클라이언트가 보내지 않음.
@@ -0,0 +1,181 @@
# 레퍼런스 — 화면·버튼 매트릭스
> 상위: [개발가이드.md](./개발가이드.md)
> SoT: `03` §4 화면 및 버튼별 상세 상태전이 명세서 (Row 4~42, 38행)
구현 위치: `pages/fnrs-cnsltn/lib/button-config.ts` + `ui/components/floating-action-buttons.tsx`
---
## 1. 화면 구분
| 화면 | 소유 | 라우트 (현행) | 비고 |
| --- | --- | --- | --- |
| 작성화면 | 기초 `ROLE_USER05` | `/fnrs-cnsltn/new`, `/fnrs-cnsltn/{id}/edit` | `SV` |
| 수정화면 | 기초 | `/fnrs-cnsltn/{id}/edit` | `SL` 보완 |
| 상세화면 | 광역·환경청·공단·기초(`RB`/`TA`) | `/fnrs-cnsltn/{id}` | 조회 + 액션 버튼 |
| 자동화 | AI | 버튼 없음 | `FV`/`AC`/`AV` 진행 패널 |
공단 상세는 결재선 구분으로 같은 상태라도 버튼이 다르다.
| `atrzSeComCd` | 역할 |
| --- | --- |
| `ATRZ01` | 접수자 |
| `ATRZ02` | 검토자 |
| `ATRZ03` | 승인자 |
| `ATRZ99` | 최종승인자 |
---
## 2. 38행 매트릭스
API 컬럼은 [ref-endpoint.md](./ref-endpoint.md)와 동일.
| 순번 | 화면 | 권한 | 결재선 | 상태 | 버튼 | 전이 | 다음 담당 | FE 동작 |
| :---: | --- | --- | --- | --- | --- | --- | --- | --- |
| 1 | 작성 | USER05 | - | 빈값, `SV` | 저장 | `SV` | 본인 | `save-apply` `SV` |
| 2 | 작성 | USER05 | - | `SV` | 신청완료 | `ST` | 광역 | `save-apply` `ST` |
| 3 | 작성 | USER05 | - | `SV` | 삭제 | 삭제 | — | `DELETE` |
| 4 | 상세 | USER04 | - | `ST` | 환경청제출 | `LE` | 환경청 | `updatePrgrsStts` |
| 5 | 상세 | USER03 | - | `LE` | 결과회신 | `EP` | 광역 | `tech-review` 자체종결 |
| 6 | 상세 | USER03 | - | `LE` | 기술검토 의뢰 | `FV` | 공단 접수 | **`request-examine`** |
| 7 | 자동화 | AI | - | `FV` | (파일검증) | `ER` | 공단 접수 | FE 호출 없음. 진행 패널 |
| 8 | 상세 | USER01 | 접수자 | `ER` | 파일보완 | `SL` | 기초 | 팝업(파일리스트) → `reqsupplement` |
| 9 | 상세 | USER01 | 접수자 | `ER` | 파일검증 | `ER` | 본인 | `dcrag-to/transfer` 재실행 |
| 10 | 상세 | USER01 | 접수자 | `ER` | 검토자지정 | `AC` | 다음 결재자 | `POST /elapr-dmnd` |
| 11 | 자동화 | AI | - | `AC` | (초안생성) | `TE` | 공단 검토 | FE 호출 없음. 폴링 |
| 12 | 상세 | USER01 | 검토자 | `TE` | 실무협의서관리 | 유지 | — | 웹기안기 Open. **초안 성공 시만** |
| 13 | 상세 | USER01 | 검토자 | `TE` | 검토결과조회 | 유지 | — | 실무협의 상세 |
| 14 | 상세 | USER01 | 검토자 | `TE` | 실무협의서생성 | 유지 | — | **실패/타임아웃/초안 없음** 시 |
| 15 | 상세 | USER01 | 검토자 | `TE` | 저장 | `TE` | — | `pwrk save-apply` |
| 16 | 상세 | USER01 | 검토자 | `TE` | 검토완료 | `TE` | 다음 결재자 | `elapr process` 상신. 반려 시 직전 결재자 |
| 17 | 상세 | USER01 | 검토자 | `TE` | 보완요청 | `SL` | 기초 | 보완 팝업() → `reqsupplement` |
| 18 | 상세 | USER01 | 승인자 | `TE` | 결재(승인) | `TE` | 다음 결재자 | `process` |
| 19 | 상세 | USER01 | 최종승인자 | `TE` | 결재(회신) | `TP` | 환경청 | `process` 최종 |
| 20 | 수정 | USER05 | - | `SL` | 파일보완완료 | `ER` | 공단 접수 | `submitsupplement` (직전=`ER`) |
| 21 | 수정 | USER05 | - | `SL` | 보완완료 | `TE` | 공단 검토 | `submitsupplement` (직전=`TE`) |
| 22 | 수정 | USER05 | - | `SL` | 보완완료 | `RK` | 공단 검토 | `submitsupplement` (직전=`RK`) |
| 23 | 상세 | USER03 | - | `TP` | 재원협의 결과 회신 | `RM` | 광역 | `tech-review` |
| 24 | 상세 | USER04 | - | `RM` | 지자체회신 | `RB` | 기초 | `updatePrgrsStts` |
| 25 | 상세 | USER05 | - | `RB` | 실무협의서 관리 | 유지 | — | 웹기안기 Open |
| 26 | 상세 | USER05 | - | `RB` | 조치완료 | `RA` | 환경청 | **`submit-actn-result` `isSubmit:true`** |
| 27 | 상세 | USER04 | - | `RC` | 조치결과서제출 | `RA` | 환경청 | `updatePrgrsStts` |
| 28 | 상세 | USER03 | - | `RA` | 실무협의서 관리 | 유지 | — | 웹기안기 Open. **저장 불가** |
| 28a | 상세 | USER03 | - | `RA` | 조치결과 검토의뢰 | `AV` | 공단 검토 | **`request-actn-review`** |
| 29 | 자동화 | AI | - | `AV` | (금액검증) | `EV` | 공단 접수 | FE 호출 없음. 진행 패널 |
| 29a | 상세 | USER01 | 접수자 | `EV` | 검토자지정 | `RK` | 공단 검토 | `POST /elapr-dmnd` cycl=2. 1차 결재선 시드 |
| 30 | 상세 | USER01 | 검토자 | `RK` | 실무협의서관리 | 유지 | — | 웹기안기 |
| 31 | 상세 | USER01 | 검토자 | `RK` | 금액검증 | `RK` | — | `dcrag-to/cost/verify` |
| 32 | 상세 | USER01 | 검토자 | `RK` | 저장 | `RK` | — | `pwrk save-apply` |
| 33 | 상세 | USER01 | 검토자 | `RK` | 검토완료 | `RK` | 다음 결재자 | `process` |
| 34 | 상세 | USER01 | 승인자 | `RK` | 결재(승인) | `RK` | 다음 결재자 | `process` |
| 35 | 상세 | USER01 | 최종승인자 | `RK` | 결재(회신) | `RR` | 환경청 | `process` 최종 |
| 36 | 상세 | USER03 | - | `RR` | 실무협의서 관리(조회), 검토결과 회신 | `EP` | 광역 | `tech-review` |
| 37 | 작성/상세 | USER04 | - | `EP` | 결과회신, 실무협의서 관리(조회) | `TA` | 기초 | `updatePrgrsStts` |
| 38 | 상세 | USER05 | - | `TA` | 결과회신, 실무협의서 관리(조회) | — | — | **API 없음. 조회** |
`SL` 20~22행: 버튼은 직전 상태에 따라 **하나만** 노출. 파일 루프면 [파일보완완료], 그 외 [보완완료].
---
## 3. `button-config` 권장 구조
액션 키는 현행 `FNRS_CNSLTN_VIEW_BUTTON_ACTION`을 확장한다. 신규·이름 변경:
| 신규/변경 액션 | 라벨 | 비고 |
| --- | --- | --- |
| `fileSplmnt` | 파일보완 | `ER` 접수자. 현행에 없음 |
| `fileVrfc` | 파일검증 | 유지. `ER` 재실행 |
| `dsgntRvwr` | 검토자지정 | `ER→AC`(1차), `EV→RK`(2차) |
| `pwrkCnsltnDocMng` | 실무협의서관리 | 유지. `TE`·`RK`·`RB`·`RA`(조회 전용) |
| `pwrkCnsltnDocCrt` | 실무협의서생성 | 유지. 실패/타임아웃만 |
| `rvwRsltInq` | 검토결과조회 | 신규 |
| `actnCmptn` | 조치완료 | `RB→RA`. `isSubmit:true` |
| `actnSbmsn` | 조치결과서제출 | 광역 `RC→RA` |
| `rqstActnRvw` | 조치결과 검토의뢰 | 환경청 `RA→AV` |
| `costVrfc` | 금액검증 | `RK` |
| `lclRply` | 지자체회신 | 광역 `RM→RB` |
| `fnrsRsltRply` | 재원협의 결과 회신 | 환경청 `TP→RM` |
| `rsltRply` | 결과회신 | `LE→EP`, `RR→EP`, `EP→TA`, `TA` 조회 |
| `fileSplmntCmptn` | 파일보완완료 | `SL←ER` |
| `splmntCmptn` | 보완완료 | `SL←TE\|RK` |
**삭제**: `fnrsCnsltnAncmnt`(고시→`TB`), `sncnAprv`(인가승인). `EP`/`TA`는 `rsltRply`로 통일.
### 3-1. 역할 × 상태 (공단 제외)
```
ROLE_USER05
"" | SV → 저장, 신청완료, 삭제
SL → (직전 ER) 파일보완완료 | (직전 TE|RK) 보완완료
RB → 실무협의서 관리, 조치완료
TA → 결과회신 (조회), 실무협의서 관리(조회)
ROLE_USER04
ST → 환경청제출 // 현행 반려(rjct→SV)는 SoT 38행에 없음. 유지 시 별도 합의
RM → 지자체회신
RC → 조치결과서제출
EP → 결과회신, 실무협의서 관리(조회)
ROLE_USER03
LE → 결과회신(자체종결), 기술검토 의뢰
TP → 재원협의 결과 회신
RA → 실무협의서 관리(조회), 조치결과 검토의뢰
RR → 실무협의서 관리(조회), 검토결과 회신 (`tech-review` → `EP`)
```
### 3-2. 공단 결재선 × 상태
```
ER / ATRZ01 접수자
→ 파일보완, 파일검증, 검토자지정
EV / ATRZ01 접수자
→ 검토자지정 (2차 결재선. 1차 결재선 시드, 서식 선택 없음)
TE / ATRZ02 검토자
→ (초안 성공) 실무협의서관리, 검토결과조회, 저장, 검토완료, 보완요청
→ (초안 실패·타임아웃) 실무협의서생성 (+ 위 항목 중 관리/조회는 초안 생긴 뒤)
TE / ATRZ03 승인자 → 결재(승인)
TE / ATRZ99 최종 → 결재(회신)
RK / ATRZ02 검토자
→ (초안 완료) 실무협의서관리
→ (초안 완료 + 결재 대기 ATRZ_PRGRS_002, cycl2) 보완요청, 검토완료
· 보완요청 UI: TE와 동일 SplmntDmndDialog
RK / ATRZ03 → (초안 완료) 실무협의서관리, (cycl2 pending) 결재(승인)
RK / ATRZ99 → (초안 완료) 실무협의서관리, (cycl2 pending) 결재(회신)
· 결재 UI: ElaprApprovalDialog (elaprCycl=2) 승인/반려 → PUT process
```
`FV`, `AC`, `AV`: 액션 빈 배열 + 진행 패널.
`PENDING_APPROVAL_ACTIONS`: `dmndSplmnt`, `rvwCmptn`, `atrzAprv`, `atrzRply`만.
`pwrkCnsltnDocMng`는 결재 대기와 무관(초안 완료만). ATRZ02·ATRZ03·ATRZ99라도 pending 없으면 관리만 노출.
---
## 4. 초안 성공 vs 실패 버튼
`TE`에서 `crtDmndList[].crtSttsComCd` / 폴링 `status`로 분기.
| 조건 | 노출 |
| --- | --- |
| `C` / `COMPLETED` | 실무협의서관리, 검토결과조회, 저장, 검토완료, 보완요청 |
| `F` / `FAILED` 또는 30분 Fallback 후 초안 없음 | 실무협의서생성 |
| `R`/`I` 생성 중 | 진행 패널. 위 버튼 비활성 (`AC`와 동일 UX, 상태가 이미 `TE`면 패널+생성 버튼) |
`RK`는 금액검증 성공이 기본. Fallback 후 [금액검증] 재실행만 추가 노출.
---
## 5. 현행 `button-config.ts`와의 차이
| 항목 | 현재 | To-Be |
| --- | --- | --- |
| USER05 | `""/SV/TA(고시)` | `SL`, `RB` 추가. `TA`는 조회 |
| USER04 | `ST`(제출+반려), `EP`(인가승인) | `RM/RC/EP` 추가. 인가승인 삭제 |
| USER03 | `LE`, `TP`(→EP로 회신) | `RA`, `RR` 추가. `TP`는 `RM` |
| USER01 | `ER`(파일검증, 검토자지정), `TE` | `ER`에 파일보완. `RK` 전면 추가 |
| USER01 ATRZ | `ER`, `TE`만 `needsRoleUser01AtrzResolve` | `RK` 포함 |
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,674 @@
# dmbpRegist.jsp → React 마이그레이션 분석·구현 가이드
| 항목 | 내용 |
| ----------- | ------------------------------------------------------ |
| 원본 JSP | `adssNewOld/.../fnrsc/dmbp/dmbpRegist.jsp` |
| 대상 앱 | `frontend/apps/as` |
| 도메인 | 하수도기본계획 (구: 기본계획검토 / dmbp) |
| API 엔티티 | `@/entities/sewer-bsc-plan` (`SewerBscPlanController`) |
| 참조 패턴 | `@/pages/fnrs-cnsltn` + `@/widgets/pbadms-sprt` |
| 문서 목적 | contents 영역 **섹션별** UI·상태·API 매핑 및 구현 방향 |
| 작성 기준일 | 2026-07-27 |
---
## 1. 마이그레이션 범위
### 1-1. 포함 (contents만)
JSP `568~1274`행: `<div class="contents">` ~ `<!-- //contents -->`
| 영역 | JSP 식별 | 개요 |
| ---- | ---------------------------------- | --------------------------------------------------------------- |
| A | `gTitle1` | 페이지 타이틀·브레드크럼 |
| B | `slide_10` | 기본계획변경 신청(마스터·연도·개요) |
| C | `slide_15` | 신청자·설계자 정보 |
| D | `slide_19` | 개인정보 동의 |
| E | `slide_11` | 사업개요(단계별 인구·보급률) |
| F | `slide_12` | 총사업비 — `BizCstMatrixFormSection` 전용 |
| G | `slide_13` | 재원분담 |
| H | `slide_14` | 처리시설별 하수도정비기본계획 (Checkbox + Tabs) |
| I | `slide_16` | 체크리스트 + 필수첨부 + 기타첨부 |
| J | JSP 상단 `saveBtn` / `presentnBtn` | React: 폼 하단 액션바 (`fnrs-form-page` 279~323과 동일 구성, API만 sewer) |
### 1-2. 제외
- `<%@ include %>`: top / sideMenu / footer / `bscPlnPopup.jsp` / `sndrdBzAmnt.jsp`
- 레이아웃·메뉴·권한 셸 → 기존 `BaseLayout` / `PageLayout` / AUTH 체계
- `mStep1` 진행 단계 표시 (미구현 · 마이그레이션 범위 밖)
팝업(`이전기본계획조회`)은 **별도 Dialog 컴포넌트**로 재구현하며, include JSP 자체는 포팅하지 않는다.
---
## 2. 목표 아키텍처
```
app/(with-base-layout)/sewer-bsc-plan/regist/page.tsx
└─ pages/sewer-bsc-plan/ui/sewer-bsc-plan-regist-page.tsx
└─ sewer-bsc-plan-form-page.tsx # FormProvider + 섹션 조립
├─ sections/*-form-section.tsx # 도메인 전용
└─ widgets/pbadms-sprt/* # 공통 재사용
entities/sewer-bsc-plan/ # 이미 구현됨
saveApply / findDetails / submitSupplement …
```
**폼 스택 (fnrs-cnsltn과 동일)**
- `react-hook-form` + `zod` (`zodResolver`, `mode: "onChange"`)
- 섹션 래퍼: `SectionCollapsible` (Accordion 아님)
- UI: `@common/components` (Input, Select, Textarea, Button, Radio, Tabs, Table 등)
- 그리드가 필요한 곳만 `@tanstack/react-table` + `DataTable`
- 데이터: React Query (`useSaveApplySewerBscPlan` 등) — 목데이터는 API 미연동 구간만 `@faker-js/faker`
**Vercel React Best Practices (적용 요약)**
- `client-swr-dedup`: React Query로 체크리스트/필수첨부/코드 조회 중복 제거
- `bundle-dynamic-imports`: 처리시설 Tabs 패널·이전계획 Dialog는 `next/dynamic` 후보
- `rerender-derived-state`: 단계 연도·합계·진행률은 render 중 파생, 불필요 effect 금지
- `async-parallel`: 등록 진입 시 코드/체크리스트트리/필수첨부트리를 병렬 조회
---
## 3. 전체 레이아웃
`fnrs-form-page.tsx`와 동일한 **FormProvider + 세로 섹션 스택** 구조를 따르고,
하단 액션바는 **[`fnrs-form-page.tsx` 279~323](../../src/pages/fnrs-cnsltn/ui/fnrs-form-page.tsx)** 과 동일 구성(목록·저장·신청완료·삭제·보완완료)이며 **API/라우트만 sewer** 로 바꾼다.
타이틀·브레드크럼은 폼 밖(`PageLayout` / regist·edit·view 셸)에서 처리한다.
(상세 View의 sticky FAB·검토/결재 버튼은 본 등록 문서 범위 밖 — `fnrs-view-page` 대응 후속.)
### 3-1. 컴포넌트 트리 (fnrs 대응)
```
PageLayout (title / breadcrumb)
└─ SewerBscPlanRegistPage | SewerBscPlanEditPage | SewerBscPlanViewPage
└─ SewerBscPlanFormPage | SewerBscPlanViewPage 본문
FormProvider (등록/수정) / 읽기 섹션 (상세)
└─ <form|div className="mt-6 space-y-4">
├─ BasicInfoFormSection // JSP slide_10
├─ ContactInfoFormSection // JSP slide_15 (pbadms-sprt)
├─ PrivacyConsentSection // JSP slide_19 (pbadms-sprt, 등록·수정)
├─ OutlineFormSection // JSP slide_11 (사업개요, 신규)
├─ BizCstMatrixFormSection // JSP slide_12 (총사업비 전용, 신규)
├─ FnrsCstFormSection // JSP slide_13 (재원분담, 신규 ※ FnrsShar 아님)
├─ TmplntFormSection // JSP slide_14 (처리시설, Tabs로 시설별 패널)
├─ ChecklistFormSection // JSP slide_16 (pbadms-sprt)
├─ AttachmentsFormSection // JSP 필수·기타첨부 (pbadms-sprt)
├─ SplmntDmndFormSection // edit + SL 상태만 (pbadms-sprt)
└─ form footer action bar // fnrs-form-page 279~323 동일
// [목록] / [저장] / edit시 [신청완료][삭제] / SL시 [보완완료]
```
**폼 하단 액션바 버튼 구성**
기준: [`fnrs-form-page.tsx` 279~323](../../src/pages/fnrs-cnsltn/ui/fnrs-form-page.tsx). UI·분기만 동일하고 훅/라우트는 sewer.
| 순서 | 버튼 | 조건 | sewer 비고 |
| ---- | -------- | ----------------------------------------- | ---------------------------------------------------- |
| 1 | 목록 | 항상 | `router.push("/sewer-bsc-plan")` |
| 2 | 저장 | `!isSplmntPrgrsStts` | `type="submit"` → `saveApply` / 임시저장(SV) |
| 3 | 신청완료 | `!isSplmntPrgrsStts && isEditMode` | 체크리스트+필수첨부 검증 → `saveApply` / LE |
| 4 | 삭제 | `!isSplmntPrgrsStts && isEditMode` | confirm → `delete` → 목록 |
| 5 | 보완완료 | `isSplmntPrgrsStts` (`prgrsSttsComCd=SL`) | `submitSupplement` (fnrs `onSplmntCmptn` 대응) |
**fnrs와의 차이 (섹션 치환만, 골격 동일)**
| fnrs-form-page | sewer-bsc-plan |
| ---------------------------------------------- | ------------------------------------------------------ |
| `BasicInfoFormSection` | 동일 역할 (마스터·연도·개요 필드는 sewer DTO) |
| `ContactInfoFormSection` | 동일 (`fieldPrefix="sewerBscPlanDtl"`) |
| `PrivacyConsentSection` | 동일 |
| `BusinessScaleFormSection` | → `OutlineFormSection` (단계별 인구·보급률) |
| `BusinessCostFormSection` | → `BizCstMatrixFormSection` (단계×원가 계층 전용) |
| `FnrsSharFormSection` | → `FnrsCstFormSection` (국고·지방비·원인자·기타) |
| _(없음)_ | `TmplntFormSection` (체크박스 + Tabs 시설별 매트릭스) |
| `ChecklistFormSection` `docSeComCd="FNRS"` | `docSeComCd="SEWER"` (TBD) |
| `AttachmentsFormSection` | 동일 패턴 |
| `SplmntDmndFormSection` | 동일 (`readOnly={!isSplmntPrgrsStts}`) |
| 폼 하단 액션바 (목록·저장·신청완료·삭제·보완완료) | **동일 구성, API·라우트만 sewer** |
### 3-2. 본문 ASCII (폼 페이지)
```
┌─────────────────────────────────────────────────────────────────┐
│ PageLayout: 하수도기본계획 신청/수정 breadcrumb │
├─────────────────────────────────────────────────────────────────┤
│ FormProvider │
│ <form className="mt-6 space-y-4"> │
│ │
│ ┌─ SectionCollapsible: 기본계획 신청 ─────────────────────┐ │
│ │ BasicInfoFormSection │ │
│ └──────────────────────────────────────────────────────────┘ │
│ ┌─ 신청자 정보 ────────────────────────────────────────────┐ │
│ │ ContactInfoFormSection │ │
│ └──────────────────────────────────────────────────────────┘ │
│ ┌─ 개인정보 동의 ──────────────────────────────────────────┐ │
│ │ PrivacyConsentSection │ │
│ └──────────────────────────────────────────────────────────┘ │
│ ┌─ 사업개요 ────────────── [이전기본계획조회] ─────────────┐ │
│ │ OutlineFormSection │ │
│ └──────────────────────────────────────────────────────────┘ │
│ ┌─ 총사업비 (백만원) ──────────────────────────────────────┐ │
│ │ BizCstMatrixFormSection │ │
│ └──────────────────────────────────────────────────────────┘ │
│ ┌─ 재원분담 (백만원) ──────────────────────────────────────┐ │
│ │ FnrsCstFormSection │ │
│ └──────────────────────────────────────────────────────────┘ │
│ ┌─ 처리시설별 하수도정비기본계획 ──────────────────────────┐ │
│ │ TmplntFormSection │ │
│ │ ├─ BscFctList Checkbox │ │
│ │ └─ Tabs (선택 시설별 TabsTrigger / TabsContent) │ │
│ └──────────────────────────────────────────────────────────┘ │
│ ┌─ 체크리스트 ─────────────────────────────────────────────┐ │
│ │ ChecklistFormSection │ │
│ └──────────────────────────────────────────────────────────┘ │
│ ┌─ 첨부파일 ───────────────────────────────────────────────┐ │
│ │ AttachmentsFormSection │ │
│ └──────────────────────────────────────────────────────────┘ │
│ ┌─ 보완요구 (edit + SL) ───────────────────────────────────┐ │
│ │ SplmntDmndFormSection │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
│ ┌─ form footer (fnrs-form-page 279~323 동일) ──────────────┐ │
│ │ [목록] [저장] / edit: [신청완료][삭제] / SL: [보완완료] │ │
│ │ ※ API·라우트만 sewer — UI 분기는 fnrs와 동일 │ │
│ └──────────────────────────────────────────────────────────┘ │
│ </form> │
└─────────────────────────────────────────────────────────────────┘
```
### 3-3. 액션바 동작 (`fnrs-form-page` 279~323 동일, API만 sewer)
구현 위치: `sewer-bsc-plan-form-page.tsx` 하단
(`fnrs-form-page.tsx`의 `mx-4 flex flex-wrap justify-center gap-3` 블록을 그대로 옮기고 훅·path만 교체)
| 버튼 | 조건 | 대표 동작 |
| -------- | ---------------------------- | -------------------------------------------------------------------------- |
| 목록 | 항상 | `router.push("/sewer-bsc-plan")` |
| 저장 | `!isSplmntPrgrsStts` | `type="submit"` → `saveApply` + 임시저장(`SV`) |
| 신청완료 | edit && `!isSplmntPrgrsStts` | 체크리스트 완료 + **필수 첨부 완료** 검증 → `saveApply` + `LE` |
| 삭제 | edit && `!isSplmntPrgrsStts` | confirm → `delete` → 목록 |
| 보완완료 | `isSplmntPrgrsStts` (SL) | `form.handleSubmit` → `useSubmitSupplementSewerBscPlan` |
**신청완료 사전 검증** (fnrs `handleApplyClick`과 동일 정책)
1. **체크리스트**: `isFormChecklistComplete(cklstResultList)` — 미완료 시 `"모든 체크리스트 항목을 체크해야 신청이 가능합니다."`
2. **필수 첨부**: `esntlAtchYn === "Y"`인 필수첨부 슬롯마다 `esntlAtchFileList`에 `atchFileGroupId`가 있어야 함
- Form: `AttachmentsFormSection`의 트리 + 폼 `esntlAtchFileList`
- 미충족 시 예: `"필수 첨부파일을 모두 등록해야 신청이 가능합니다."`
- 헬퍼 후보: `isFormEsntlAtchComplete(requiredTree, esntlAtchFileList)` (`pbadms-sprt`에 추가)
저장에는 위 검증을 적용하지 않는다 (임시저장 허용).
저장/제출 버튼을 섹션 헤더(`gTitle2 gRt`)에 두지 않는다 — **폼 하단 액션바 단일 진입점** (§4-J).
### 3-4. 라우트
| 화면 | path | 셸 → FormPage |
| ---- | --------------------------- | ------------------------------------------ |
| 등록 | `/sewer-bsc-plan/regist` | `mode="create"` |
| 수정 | `/sewer-bsc-plan/[id]/edit` | `mode="edit"` + `useSewerBscPlanDetails` |
| 상세 | `/sewer-bsc-plan/[id]` | ViewPage (본 문서 범위 밖, fnrs-view 대응) |
`docSeComCd` 후보: 백엔드 체크리스트·필수첨부 등록값 확인 후 **`SEWER`** (또는 레거시 `DMBP`) — **TBD, CMS 코드와 맞출 것**.
---
## 4. 섹션별 상세 구현
### 4-0. Hidden / 세션 컨텍스트 (폼 루트)
JSP hidden → React 폼 기본값·세션 스토어로 흡수.
| JSP | 의미 | React |
| ------------------------------- | --------------- | ------------------------------------------------------- |
| `chgSe` / `chgSeSp` / `chgSeCd` | 변경구분 | `sewerBscPlan.sewerBscPlanSeComCd` + 표시용 코드명 |
| `progrsSttus` | SAVE / PRESENTN | 저장·제출 시 `prgrsSttsComCd`로 매핑 (임시저장 vs 제출) |
| `planSeCd` | 고정 `A` | 필요 시 상수 또는 API 필드 확인 |
| `adstDstrCd` / `rgnCd` | 행정구역 | `sewerBscPlan.admdstCd` |
| `sidoNm` / `gugunNm` | 표시용 | 읽기 전용 표시 (`detDto.sido`/`gugun`) |
구현: `getCreateDefaultValues(userInfo)`에서 로그인 사용자·소속 지자체 주입.
---
### 4-A. 타이틀·브레드크럼
| JSP | React |
| ------------------------ | ------------------------------------------------------ |
| `h3` 기본계획검토 | PageLayout title: **하수도기본계획 신청** (IIPBS 명명) |
| 설치인가 및 제원협의 > … | 메뉴 메타 기반 breadcrumb |
별도 섹션 컴포넌트 불필요. 라우트 `page.tsx` / layout metadata로 처리.
---
### 4-B. 기본계획변경 신청 (`slide_10`)
**파일**: `ui/sections/basic-info-form-section.tsx`
**래퍼**: `SectionCollapsible` title=`기본계획 신청`
**필드 접두사**: `sewerBscPlan.*` + `sewerBscPlanDtl.*`
| 표시명 | JSP | API 매핑 | UI | 필수 | 비고 |
| ---------------- | ----------------- | ------------------------------- | ------------------ | ---- | ---------------------------------- |
| 기본계획명 | `drngMtncPlanNm` | `sewerBscPlan.sewerBscPlanNm` | Input | Y | |
| 계획년도 | `planYr` | `sewerBscPlanDtl.planYr` | Select (현재~2005) | Y | |
| 기준년도 | `crtrYr` | `sewerBscPlanDtl.crtrYr` | Select | Y | |
| 1~4단계 목표연도 | `goalYrStep1~4` | `frlvlGoalYr`~`folvlGoalYr` | Select (현재~+30) | Y | 1단계 변경 시 +5/+10/+15 자동 연동 |
| 계획개요 | `bzCn` | `sewerBscPlanDtl.bizOtlnCn` | Textarea | Y | |
| 구분 | `#chgSeNm` | `sewerBscPlanSeComCd` (+코드명) | 읽기 또는 Select | | 신규/변경 등 |
| 신청 지자체 | `${lclgvrnmntNm}` | `admdstCd` | 읽기 전용 | | |
**동작**
- `goalYrStep1` onChange → 2·3·4단계 기본값 세팅 + E/F/G 헤더 연도 span 갱신 (`watch` 파생)
- JSP 상단 `gRt` 저장/제출은 사용하지 않음 → FormPage 하단 액션바 (§3-3, §4-J)
---
### 4-C. 신청자 정보 (`slide_15`)
**재사용**: `@/widgets/pbadms-sprt`의 `ContactInfoFormSection`
**props**: `fieldPrefix="sewerBscPlanDtl"` (또는 dtl 필드 구조에 맞게)
| 표시 | JSP | API |
| ---------------------------- | --------------------------- | --------------------------------------------------------------------------------------- |
| 신청자 이름/전화/부서/이메일 | `${UsrNm}` 등 서버렌더 | 로그인 사용자 읽기 전용 → `aplcntNm`, `aplcntMblTelno`, `aplcntOgdpNm`, `aplcntEmlAddr` |
| 설계자 | `dsgnrNm/Telno/Psitn/Email` | `dsgnrNm`, `dsgnrTelno`, `dsgnrOgdpNm`, `dsgnrEmlAddr` |
신청자 4필드는 세션에서 defaultValues에 채우고 Input `disabled`.
---
### 4-D. 개인정보 동의 (`slide_19`)
**재사용**: `PrivacyConsentSection`
| JSP | React |
| ----------------------------- | ------------------------------------------------------------------ |
| 고지 textarea + 보유기간 문구 | 동일 문구 상수/마크다운 |
| `privacyAgree` Y/N | 폼 필드 `privacyAgree: "Y" \| "N"` (API 미전송, 클라이언트 검증만) |
**검증**: 제출 시 `Y` 필수 (JSP `presentnBtn`과 동일). 저장(임시)은 동의 없이 허용할지 정책 TBD — 기본은 JSP와 같이 **제출만 강제**.
---
### 4-E. 사업개요 (`slide_11`)
**파일**: `ui/sections/outline-form-section.tsx` (신규, 도메인 전용)
**API**: `sewerBscPlanOtlnList: SewerBscPlanOtlnItem[]` (planStpNo 1~4)
| 행(구분) | JSP name | Item 필드 |
| ------------ | --------------- | ------------------- |
| 계획인구 | `arrPlanPpltn` | `sewerPlanPpltnCnt` |
| 처리인구 | `arrPrcsPpltn` | `sewerPrcsPpltnCnt` |
| 하수도보급율 | `arrDrngSplyRt` | `sewerPsplyRt` |
열: 1~4단계 (헤더에 `frlvlGoalYr` 등 표시).
**버튼**: `이전기본계획조회` → Dialog (`PreviousPlanLookupDialog`)
- 레거시: `fnbscPlnPopupToggle` + `bscPlnPopup.jsp`
- React: Modal + 조회 API (**TBD**: 이전 계획 조회 엔드포인트; 없으면 1차는 Dialog UI만 + mock)
숫자 입력: 콤마 제거 후 number 전송 (제출 시 JSP `replaceComma` 대체).
---
### 4-F. 총사업비 (`slide_12`)
**파일**: `ui/sections/biz-cst-matrix-form-section.tsx` (**전용 컴포넌트** — fnrs `BusinessCostFormSection` 재사용 금지)
**래퍼**: `SectionCollapsible` title=`총사업비` (단위 `백만원`은 헤더 우측)
**JSP 현실**: 단계(1~4) × 원가 계층(총사업비, 공사비, 처리시설, 하수관로 신설/보수, 펌프장, 배수설비, 기타, 부대경비, 설계/감리/부대비). 일부 readonly·자동합산 JS.
**신규 API**: `bizCstList: SewerBscPlanBizCstItem[]` = `{ planStpNo, acntgSeCd, cstrnAmt }`
**구현 방향 (`BizCstMatrixFormSection` 전용)**
- 행 = `useAcntgSeMngTree` → `buildAcntgCostItems` (fnrs `BusinessCostFormSection`과 동일 출처)
- `costItems` 로드 시 `useEffect`로 `bizCstList`를 `mergeBizCstList(existing, costItems)`로 동기화
- 열 = `planStpNo` 1~4 (헤더에 목표연도 표시)
- readonly 행(`CostItemDef.readonly=true`)은 children 합산으로 자동계산; Input에 `readOnly` + `bg-muted`
- 변경 시 `setAmt` → `recalculateBizCstMatrix(list, costItems)` → `setValue("bizCstList")`
- 저장 시 섹션 onChange 합산을 신뢰하여 `bizCstList` 그대로 전송 (`toBizCstSaveList` identity)
**표준사업비 조회** 버튼은 JSP에서 주석 처리됨 → **미구현**.
---
### 4-G. 재원분담 (`slide_13`)
**주의**: fnrs의 `FnrsSharFormSection`(재원협의 공유구분 트리)과 **다름**.
| 행 | JSP | `SewerBscPlanFnrsCstItem` |
| ------------ | -------------- | ------------------------- |
| 총사업비 | `arrTotAmnt` | `sumAmt` (또는 파생) |
| 국고 | `arrNlepAmt` | `gvexpAmt` |
| 지방비 | `arrLcepAmt` | `lcexpAmt` |
| 원인자부담금 | `arrCusrAlotm` | `orgprBdchgAmt` |
| 기타 | `arrEtcAmt` | `etcAmt` |
**파일**: `ui/sections/fnrs-cst-form-section.tsx` (신규)
데이터: `fnrsCstList` 길이 4 (`planStpNo` 1~4).
검증(선택): 단계별 `gvexp+lcexp+orgpr+etc === sumAmt` (JSP `calculateTotalFunding` 참고).
---
### 4-H. 처리시설별 하수도정비기본계획 (`slide_14`) — 핵심 복잡도
**JSP 동작**
1. `BscFctList` 체크박스로 처리시설 다중 선택 (`arrBscFctCd`)
2. 선택 시 `createFormSection(code, name)`로 **당초 4단계 | 현재 | 변경 4단계** 매트릭스 동적 삽입
→ **React**: 세로 삽입 대신 `@common/components` `Tabs`로 시설별 패널 관리 (§아래 Tabs 규칙)
3. 활성 `formFields`(축소판): 처리구역면적, 하수처리인구, 계획하수량, 시설용량, 분류식 오수/우수, 합류식, 차집, 배수설비, 오수중계/일반/우수 펌프
4. 시설별 필수첨부 `createFileAttachmentSection`
5. `fetchBasicPlanData`로 당초(이전) 데이터 바인딩
**API 매핑**
| UI | DTO |
| -------------------- | ------------------------------------------------------------- |
| 선택 시설 목록 | `sewerBscPlanTmplntList[].swtrmtFcltCd` (+ `atchFileGroupId`) |
| 단계별 수치 (변경측) | `sewerBscPlanTmplntDtlList[]` → `SewerBscPlanTmplntDtlItem` |
| 임시저장 | `tmplntTmprStrgList` (편집 중 임시, 필요 시) |
필드 대응 (변경 단계 기준):
| UI 라벨 | `SewerBscPlanTmplntDtlItem` |
| -------------- | --------------------------- |
| 처리구역면적 | `prcsZoneArea` |
| 하수처리인구 | `swtrmtPpltnCnt` |
| 계획하수량 | `planSwqty` |
| 시설용량 | `fcltCpct` |
| 분류식 오수 | `sprsysDrtwPplLen` |
| 분류식 우수 | `sprsysExlnPplLen` |
| 합류식 | `cbsysPplLen` |
| 차집관로 | `icptgPplLen` |
| 배수설비 | `drgCnt` |
| 오수 중계 펌프 | `drtwRlyPumpCnt` |
| 오수 일반 펌프 | `drtwPumpCnt` |
| 우수 펌프 | `exlnPumpCnt` |
**구현 구성**
```
ui/sections/tmplnt-form-section.tsx
├─ TmplntFacilityPicker # BscFctList Checkbox 목록 (시설 마스터 API TBD)
└─ Tabs (@common/components Tabs) # 선택 시설별 동적 패널
├─ TabsList / TabsTrigger # value=swtrmtFcltCd, 라벨=시설명
└─ TabsContent (시설 1개당)
├─ TmplntMatrixTable # 당초|현재|변경 매트릭스 (useFieldArray)
└─ TmplntFacilityAttachments # 시설별 필수첨부 (선택)
```
**Tabs 관리 규칙** (`packages/common/components/src/ui/tabs`)
- `BscFctList` 체크 **선택** → `useFieldArray` append + 해당 시설 `TabsTrigger`/`TabsContent` 추가 → `Tabs` `value`를 신규 시설로 전환
- 체크 **해제** → confirm 후 remove + 해당 탭 제거 → 남은 탭 중 하나로 `value` 이동 (없으면 Tabs 영역 비표시)
- 선택 시설이 0개면 Tabs UI 숨김; 1개 이상이면 Tabs로 시설별 패널 전환
- 매트릭스·첨부는 각 `TabsContent` 안에만 렌더 (JSP의 세로 동적 삽입을 탭 패널로 대체)
**매트릭스 열**
- **당초 열**: 이전계획 API 조회 결과 읽기 전용 (없으면 빈 칸)
- **현재 열**: 기준년도 시점 값 (API 제공 시)
- **변경 열**: 편집 가능 → 저장 payload의 dtl만 전송
시설 마스터 목록 API: 레거시 `BscFctList` 대체 — **TBD** (`swtrmtFclt` 계열).
`bundle-dynamic-imports`: `TmplntFormSection`(또는 TabsContent 내부 매트릭스)을 `next/dynamic` 후보.
---
### 4-I. 체크리스트 + 첨부 (`slide_16`)
#### I-1. 체크리스트
**재사용**: `ChecklistFormSection`
`docSeComCd="<SEWER|DMBP TBD>"`
`fieldPrefix`: 마스터에서 `taskSeComCd`를 읽는 경로 (예: `sewerBscPlan`)
| JSP | React |
| ----------------------- | ------------------------------------------- |
| 업무탭 `ceckEssntlArr` | 코드 `taskSeComCd` 다중 → Tabs |
| 진행률 / 전체 예·아니오 | 위젯 내장 로직 |
| `exmntResult` Y/N | `cklstResultList[].selected` (+ `picRvwCn`) |
저장: `cklstResultList: CklstResultItem[]`
#### I-2. 필수 첨부 (탭별)
**재사용**: `AttachmentsFormSection`
`docSeComCd` 동일, `esntlAtchFileList`
레거시: 체크리스트 탭마다 `essntlList` + `uploadFile` multipart.
신규: TUS/`attach-service` + `esntlAtchFileId` / `atchFileGroupId` (fnrs와 동일).
#### I-3. 기타 첨부
`AttachmentsFormSection`의 etc 영역 또는 `atchFileGroupId` on `sewerBscPlanDtl`.
JSP `uploadFileETC` multiple → Zip 안내 문구 유지.
---
### 4-J. 저장 / 제출
**UI 구성**: [`fnrs-form-page.tsx` 279~323](../../src/pages/fnrs-cnsltn/ui/fnrs-form-page.tsx) 과 **동일**.
마크업·분기(`isSplmntPrgrsStts` / `isEditMode`)·버튼 색상·`disabled={isSubmitting}` 패턴을 그대로 두고, **호출 API·목록 path만 sewer** 로 교체한다.
```tsx
<div className="mx-4 flex flex-wrap justify-center gap-3 sm:gap-4">
<Button color="white" type="button" onClick={() => router.push("/sewer-bsc-plan")} disabled={isSubmitting}>
목록
</Button>
{!isSplmntPrgrsStts ? (
<>
<Button type="submit" disabled={isSubmitting}>저장</Button>
{isEditMode && (
<>
<Button color="green" type="button" onClick={handleApplyClick} disabled={isSubmitting}>
신청완료
</Button>
<Button color="red" type="button" onClick={handleDelClick} disabled={isSubmitting}>
삭제
</Button>
</>
)}
</>
) : (
<Button type="button" onClick={form.handleSubmit(onSplmntCmptn)} disabled={isSubmitting}>
보완완료
</Button>
)}
</div>
```
| 버튼 | fnrs 대응 | sewer API / 동작 |
| -------- | ----------------------------- | ------------------------------------------------------------ |
| 목록 | `router.push("/fnrs-cnsltn")` | `router.push("/sewer-bsc-plan")` |
| 저장 | `saveApply` + 임시저장 | `useSaveApplySewerBscPlan` + `prgrsSttsComCd`=임시저장(`SV`) |
| 신청완료 | `handleApplyClick` → 제출 | 체크리스트+필수첨부 검증 → `saveApply` + `LE` (상태코드 TBD) |
| 삭제 | `handleDelClick` | `useDeleteSewerBscPlan`(또는 동등) → 목록 |
| 보완완료 | `onSplmntCmptn` | `useSubmitSupplementSewerBscPlan` |
| JSP | 진행값 | URL | React |
| ---- | ---------- | ------------------------- | ---------------------------------------------------------- |
| 저장 | `SAVE` | `insertFnrscDmbp.do` | `saveApply` + 임시저장 코드 |
| 제출 | `PRESENTN` | `insertFnrscDscss.do` (!) | `saveApply` + 제출 상태코드 — **백엔드 상태코드 확인 TBD** |
페이로드 조립 (`buildApplyPayload`):
```ts
SewerBscPlanApplySaveRequestDto {
sewerBscPlan, sewerBscPlanDtl,
sewerBscPlanOtlnList, bizCstList, fnrsCstList,
sewerBscPlanTmplntList, tmplntTmprStrgList?,
cklstResultList, esntlAtchFileList,
splmntDmndList?, rvwOpnnList? // 등록 화면에서는 보통 빈 배열
}
```
성공 후: `invalidateQueries(sewerBscPlanBaseKey)` → `router.push('/sewer-bsc-plan')` (신청완료는 상세로 보낼 수 있음 — fnrs와 동일 정책).
권한: JSP `AUTH3` → 기존 AS 권한 훅/역할로 버튼 `disabled`/`hidden`.
---
## 5. API / DTO 연결
Base: `/as-service/api/v1/sewer-bsc-plans`
엔티티: `@/entities/sewer-bsc-plan`
| 화면 동작 | Hook / Service | 비고 |
| -------------- | ---------------------------------------- | ---------------- |
| 등록 저장·제출 | `useSaveApplySewerBscPlan` → `saveApply` | 본 문서 핵심 |
| 수정 로드 | `useSewerBscPlanDetails(docNo, chgCycl)` | 편집 시 |
| 마스터만 | `useSewerBscPlan` | 목록 행 클릭 등 |
| 보완 제출 | `useSubmitSupplementSewerBscPlan` | edit + `prgrsSttsComCd=SL` 시 폼 하단 [보완완료] |
| 기술검토 | `useTechReviewSewerBscPlan` | 등록 JSP 범위 밖 |
등록 JSP에 없는 섹션(보완요구·검토의견)은 **상세/검토 화면**에서 `SplmntDmndFormSection` / 검토의견 섹션으로 후속 구현.
---
## 6. 권장 파일 배치
```
apps/as/
├── app/(with-base-layout)/sewer-bsc-plan/
│ └── regist/page.tsx
├── src/pages/sewer-bsc-plan/
│ ├── lib/
│ │ ├── sewer-bsc-plan-regist-schema.ts # zod
│ │ └── form-data-mapper.ts # Det ↔ Form, Apply payload
│ ├── model/
│ │ └── (필요 시 table columns)
│ └── ui/
│ ├── sewer-bsc-plan-regist-page.tsx
│ ├── sewer-bsc-plan-form-page.tsx
│ ├── components/
│ │ └── previous-plan-lookup-dialog.tsx
│ └── sections/
│ ├── basic-info-form-section.tsx
│ ├── outline-form-section.tsx # 사업개요
│ ├── biz-cst-matrix-form-section.tsx # 총사업비 (전용)
│ ├── fnrs-cst-form-section.tsx # 재원분담
│ └── tmplnt-form-section.tsx # 처리시설 (Checkbox + Tabs)
└── docs/sewer-bsc-plan/
└── dmbp-regist-migration.md # 본 문서
```
공통 재사용 (신규 작성 최소화):
- `ContactInfoFormSection`, `PrivacyConsentSection`
- `ChecklistFormSection`, `AttachmentsFormSection`
- `SectionCollapsible`
- `Tabs` / `TabsList` / `TabsTrigger` / `TabsContent` (`@common/components` — 처리시설 패널)
도메인 전용 (재사용 금지):
- `BizCstMatrixFormSection` — 총사업비 (fnrs `BusinessCostFormSection` 미사용)
- 폼 하단 액션바 — `fnrs-form-page` 279~323 복제, API만 sewer
---
## 7. 화면 데이터 흐름
```
BEGIN
// 최초 진입 (등록)
CALL 로그인사용자조회
CALL 코드조회(SEWERBSCPLANSE, SEWER, …) // 병렬
CALL 체크리스트트리(docSeComCd) // 병렬
CALL 필수첨부트리(docSeComCd) // 병렬
CALL 처리시설마스터목록 // TBD (BscFctList)
DISPLAY 폼 defaultValues
ON 1단계목표연도 CHANGE DO
SET 2·3·4단계 연도 (+5/+10/+15)
DISPLAY E/F/G/H 헤더 연도
END ON
ON 처리시설 체크 DO
ADD tmplntList 항목
ADD TabsTrigger / TabsContent (시설 패널)
SET Tabs value = 신규 시설
CALL 이전기본계획수치(시설코드) // 당초열, TBD
END ON
ON 처리시설 체크해제 DO
CONFIRM 제거
REMOVE tmplntList + 해당 Tabs 패널
SET Tabs value = 남은 시설 (없으면 Tabs 숨김)
END ON
ON [이전기본계획조회] CLICK DO
OPEN PreviousPlanLookupDialog
END ON
ON [저장] CLICK DO
VALIDATE zod (동의 제외 가능)
CALL saveApply(payload, 임시저장 상태)
NAVIGATE 목록
END ON
ON [신청완료] CLICK DO
VALIDATE zod + privacyAgree=Y + 체크리스트 완료 + 필수첨부(esntlAtchYn=Y) 완료
CONFIRM 신청 확인
CALL saveApply(payload, 제출 상태 LE)
NAVIGATE 상세
END ON
END
```
---
## 8. JSP ↔ 신규 갭·리스크
| # | 이슈 | 대응 |
| --- | ---------------------------------------------------- | ------------------------------------------------- |
| 1 | JSP 제출 URL이 `dscss`로 분기 (오타/구프로세스 가능) | 신규는 `saveApply` 단일 진입으로 통일 |
| 2 | 총사업비 UI 계층 ≠ `BizCstItem` | `BizCstMatrixFormSection` 전용 + `acntgSeCd` 매핑 테이블 |
| 3 | 재원분담 ≠ fnrs shar | 전용 `fnrs-cst-form-section` |
| 4 | 처리시설 매트릭스(당초/현재/변경) | Checkbox 선택 → Tabs 패널; 저장은 변경 dtl 중심; 당초는 조회 전용 |
| 5 | 시설·이전계획 API | TBD — 미정 시 체크박스 목록 mock(`faker`)만 |
| 6 | `docSeComCd` 값 | CMS/체크리스트 마스터와 합의 |
| 7 | 레거시 multipart vs TUS | fnrs 첨부 패턴 준수 |
| 8 | 주석 처리된 상세 `formFields` (자연발생인구 등) | **축소된 formFields만** 이관; 주석 블록 복원 금지 |
---
## 9. 구현 순서 (권장)
1. 라우트 + `FormPage` 골격 + zod 스키마(마스터·dtl·동의) + 폼 하단 액션바(§4-J)
2. B·C·D 섹션 (공통 위젯 최대 활용)
3. E 사업개요 + G 재원분담 (4열 매트릭스 패턴 공유)
4. F 총사업비 (`BizCstMatrixFormSection` 전용)
5. I 체크리스트·첨부
6. H 처리시설 (`BscFctList` Checkbox + `@common/components` Tabs — 가장 복잡)
7. 저장/신청완료/삭제/보완완료 API 연동 (§4-J)
8. 이전계획 Dialog (API 확정 후)
---
## 10. 체크리스트 (jsp-migration-as)
- [ ] contents만 이관 (include 제외)
- [ ] vercel-react-best-practices 적용 (Query 병렬·파생상태·dynamic)
- [ ] `@common/components` + 필요 시 DataTable
- [ ] API·모델은 `@/entities/sewer-bsc-plan` 우선
- [ ] 목데이터는 `@faker-js/faker`만 (시설 마스터 등 TBD 구간)
- [ ] 로딩·빈·에러 상태
- [ ] `router.push`로 목록 복귀
---
## 11. 후속 문서
- 목록(`dmbpList.jsp`) / 상세(`dmbpView.jsp`) / 수정(`dmbpModify.jsp`)은 본 문서와 동일 섹션 체계로 별도 확장
- 화면설계서 SPEC 형식이 필요하면 `docs/sewer-bsc-plan/SPEC.md`로 분리 (영역 A~J 표 + API 표)
+498
View File
@@ -0,0 +1,498 @@
# 화면설계서: 전국 행정지원 지도 (대시보드 메인)
## 1. 기본 정보
| 항목 | 내용 |
| ---------- | -------------------------------------------- |
| 버전 | 1.2 |
| 프로세스명 | UI설계 |
| 화면명 | 전국 행정지원 지도 (대시보드 메인) |
| 화면 ID | TBD |
| 화면 유형 | [v] 조회 [ ] 입력 [ ] 수정 [ ] 삭제 [ ] 출력 |
| 메뉴 경로 | 메인 |
---
## 2. 화면 목적
로그인 사용자의 **기관 권한·당해 연도** 기준으로 문서별 진행상태 통계와 행정지원 알림을 한 화면에서 조회한다. 좌측 지도에서 전국 행정지원 현황을 보고, 우측에서 진행상황 카드·알림이력·공지사항을 확인한 뒤 알림 [처리하기]로 연계 업무 화면으로 이동한다.
---
## 3. 화면 구성
### 3-1. 전체 레이아웃
브레드크럼·페이지 타이틀은 스크린샷에 없음. 홈(`/`) 메인 콘텐츠로 구성한다.
```
┌────────────────────────────────┬─────────────────────────────────────────────────┐
│ [영역 A] 전국 행정지원 지도 │ [영역 B] 문서별 진행상황 카드 (2×2) │
│ 타이틀: 전국 행정지원 지도 │ ┌────────────────┐ ┌────────────────┐ │
│ │ │ 재원협의 │ │ 설치인가 │ │
│ (대한민국 시도·시군구 경계) │ │ 신청/진행/완료 │ │ 신청/진행/완료 │ │
│ 마커: 서울 강남구, │ ├────────────────┤ ├────────────────┤ │
│ 대전 유성구, │ │ 설계자문 │ │ 기본계획 │ │
│ 부산 해운대구 │ │ 신청/진행/완료 │ │ 신청/진행/완료 │ │
│ │ └────────────────┘ └────────────────┘ │
│ ├─────────────────────────────────────────────────┤
│ │ [영역 C] 알림이력 │
│ │ 알림이력 업데이트 HH:mm [전체보기] │
│ │ [오늘 N건] [지연 N건] [완료대기 N건] │
│ │ [뱃지] [일자] 제목 [처리하기] │
│ │ … (최대 5건) │
│ ├─────────────────────────────────────────────────┤
│ │ [영역 D] 공지사항 [더보기] │
│ │ ┌────┬────────┬────────┬────────┬──────┐ │
│ │ │번호│ 제목 │ 작성자 │ 작성일 │조회수│ │
│ │ └────┴────────┴────────┴────────┴──────┘ │
└────────────────────────────────┴─────────────────────────────────────────────────┘
```
### 3-2. 화면 데이터 흐름
```
BEGIN
// 최초 진입
// searchYear 미전달 → 서버가 당해 연도로 고정. 알림 목록은 최신 5건
CALL getMainDashboard(DashboardMainSearchDto) // Authorization은 client 인터셉터
DISPLAY 진행상황 카드 (영역 B) // docPrgrsSttsList, TOTAL 행 제외
DISPLAY 알림 통계 칩 · 알림 목록 (영역 C) // notiStats, notiList
CALL getUserPostList(UserPostSearchParams) // board-service, AS_DASHBOARD_BBS_ID
DISPLAY 공지사항 그리드 (영역 D)
CALL 지도 마커/음영 조회(…) // TBD — 대시보드 API에 없음
DISPLAY 전국 지도 (영역 A)
ON 영역 C [전체보기] 버튼 CLICK DO
NAVIGATE /nti-hstry
END ON
ON 영역 C [처리하기] 버튼 CLICK DO
CALL readNoti(notiId) // entities/noti
NAVIGATE notiUrl // 비어 있으면 이동하지 않음
CALL getMainDashboard(…) // → 영역 C 갱신 (invalidate)
END ON
ON 영역 D [더보기] 버튼 CLICK DO
NAVIGATE /cms/bbsnotice/{AS_DASHBOARD_BBS_ID} // as 앱 공지 목록 라우트 미구현. st/cm 관례
END ON
ON 영역 D 행 CLICK DO
NAVIGATE /cms/bbsnotice/{AS_DASHBOARD_BBS_ID}/{pstNo}
END ON
ON 영역 B 카드 CLICK DO
NAVIGATE 해당 문서 목록 // §5-3
END ON
ON 영역 A 마커 CLICK DO
// TBD — 팝업/필터/이동 여부 미확정
END ON
END
```
---
## 4. 영역 A — 전국 행정지원 지도
스크린샷 타이틀: **전국 행정지원 지도**.
`getMainDashboard` 응답에 지도·좌표·행정구역 음영 데이터는 **없다**. 마커·음영 소스 API는 **TBD**.
### 4-1. 표시 요소
| 표시명 | 입력 유형 | 필수 | 기본값 | API 파라미터 매핑 | 비고 |
| ----------- | --------- | ---- | ------ | ----------------- | ----------------------------------------------------------------- |
| 섹션 타이틀 | Text | — | — | — | 고정 문구 `전국 행정지원 지도` |
| 전국 지도 | Map | — | — | TBD | 시도·시군구 경계. 북·동부 음영(청) / 남서부 비음영(백) — 의미 TBD |
| 마커 라벨 | Marker | — | — | TBD | 스크린샷 샘플: `서울 강남구`, `대전 유성구`, `부산 해운대구` |
### 4-2. 상호작용
| 동작 | 설명 |
| ---------- | ----------------------------------------- |
| 마커 클릭 | TBD (상세 팝업 / 해당 지자체 필터 / 이동) |
| 지도 줌·팬 | TBD (스크린샷에 컨트롤 없음) |
`entities/adstdstr`는 시도·구군 **콤보**만 제공하며 좌표를 주지 않는다. 지도 라이브러리도 as 앱에 없음 — 선정 TBD.
---
## 5. 영역 B — 문서별 진행상황 카드
`DashboardMainResponseDto.docPrgrsSttsList`를 **`docSeComCd`로 매핑**해 4장 카드를 그린다. 배열 순서에 의존하지 않는다.
스크린샷 배치(좌→우, 상→하):
| 화면 카드 타이틀 | `docSeComCd` | API `docSeNm` |
| ----------------- | ------------ | -------------- |
| 재원협의 진행상황 | `FNRS` | 재원협의 |
| 설치인가 진행상황 | `INSTL` | 설치인가 |
| 설계자문 진행상황 | `CNFM` | 설계자문 |
| 기본계획 진행상황 | `SEWER` | 하수도기본계획 |
서버 SQL `ORDER BY`는 `INSTL → FNRS → SEWER → CNFM` 이며, `GROUP BY ROLLUP`으로 **`docSeComCd = TOTAL`** 합계 행이 추가된다. 카드에는 TOTAL 행을 **표시하지 않는다**.
카드 타이틀은 스크린샷 문구(`… 진행상황`)를 쓴다. `SEWER`의 API명 `하수도기본계획`은 화면에서 `기본계획`으로 축약한다.
### 5-1. 카드 내부 필드
각 카드 공통 구조. `totCnt`는 화면에 없음.
| 표시명 | Field | 비고 |
| ------ | ---------- | ------------------ |
| 신청 | `reqCnt` | 파란 아이콘 + 건수 |
| 진행 | `prgrsCnt` | 초록 아이콘 + 건수 |
| 완료 | `cmptnCnt` | 주황 아이콘 + 건수 |
상태 그룹(서버): 임시저장(`SV`/`CV`) 제외. 문서별 `prgrs_stts_com_cd` → `REQ`/`PRG`/`CMP`.
### 5-2. 검색 파라미터 (화면 미표시)
영역 B에 연도·필터 UI는 없다. 서버 기본값을 따른다.
| 표시명 | 입력 유형 | 필수 | 기본값 | API 파라미터 매핑 | 비고 |
| ------ | --------- | ---- | --------- | ----------------- | --------------------------------------------------------------------- |
| (없음) | — | — | 당해 연도 | `searchYear` | 미전달 시 서버 `LocalDate.now().getYear()`. 연도 셀렉터 추가 여부 TBD |
`docSeComCd` 쿼리는 **알림 목록 필터**에만 쓰이며, 진행상황 SQL에는 전달되지 않는다. 카드 4종은 항상 전원 집계한다.
### 5-3. 상호작용
카드 클릭 시 **해당 문서 목록 페이지**로 이동한다. `docSeComCd` → 라우트 고정 매핑.
| 동작 | `docSeComCd` | 이동 경로 |
| --------- | ------------ | ----------------- |
| 카드 클릭 | `FNRS` | `/fnrs-cnsltn` |
| 카드 클릭 | `INSTL` | `/instl-sncn` |
| 카드 클릭 | `CNFM` | `/design-cnstn` |
| 카드 클릭 | `SEWER` | `/sewer-bsc-plan` |
---
## 6. 영역 C — 알림이력
서버가 cms-service 알림을 대시보드 응답에 합친다. 목록은 **page=0, size=5** (최신 5건). 페이지네이션 없음.
### 6-1. 헤더
| 표시명 | Field / 소스 | 비고 |
| ------------- | ------------ | --------------------------------------------------------------------------------------- |
| 섹션 타이틀 | — | 고정 문구 `알림이력` |
| 업데이트 시각 | (client) | 스크린샷 `업데이트 10:32`. API 필드 없음. `getMainDashboard` 마지막 fetch 시각(`HH:mm`) |
| 전체보기 | Button | `/nti-hstry`로 이동 |
### 6-2. 통계 칩
`NotiAsStatResponseDto`.
| 표시명 | Field | 비고 |
| ------------ | -------------- | -------------------------------------- |
| 오늘 N건 | `todayCount` | 당일 00:00 이후 수신 |
| 지연 N건 | `delayCount` | `notReadYn=N` 이고 수신 후 3일 이상 |
| 완료대기 N건 | `pendingCount` | `notReadYn=Y` 이고 `notiPrcsCmptnYn=N` |
### 6-3. 목록 행
| 표시명 | Field | 비고 |
| ------ | --------- | --------------------------------------------- |
| 일자 | `regDt` | 표시 `YYYY-MM-DD`. 응답 `yyyy-MM-dd HH:mm:ss` |
| 제목 | `notiTtl` | 알림 제목 |
| 처리 | (action) | 스크린샷 라벨 `처리하기`. 클릭 §6-4 |
응답에 있으나 이 목록에 **표시하지 않음**:
| Field | 비고 |
| ----------------- | ----------------------------------------- |
| `notiId` | 행 키 · `readNoti` 파라미터 |
| `recvUnqId` | 수신 회원 UUID (서버 로그인 사용자 고정) |
| `senderUnqId` | 발신 회원 UUID |
| `notiType` | `ADSS` |
| `notiCn` | 본문 |
| `notiUrl` | 처리 후 이동 URL. 컬럼 미표시 |
| `notiLnkgUnqNo` | 알림연결고유번호 |
| `docSeComCd` | 문서구분 코드 |
| `docSeComNm` | 문서구분명 (알림이력 화면에서는 연계업무) |
| `notReadYn` | 뱃지 판정 TBD에 사용 가능 |
| `notiIdntyYn` | 확인 여부 |
| `notiIdntyDt` | 확인 일시 |
| `notiPrcsCmptnYn` | 처리 완료 여부 |
| `notiPrcsCmptnDt` | 처리 완료 일시 |
### 6-4. 행 상호작용
| 동작 | 설명 |
| --------------- | ----------------------------------------------------------------------------------------------------------------- |
| [전체보기] 클릭 | `/nti-hstry` (알림이력 목록)로 이동 |
| [처리하기] 클릭 | `readNoti(notiId)` 후 `notiUrl`로 이동. URL이 비면 이동하지 않음(읽음 처리는 수행). `dashboardBaseKey` invalidate |
`DashboardController`에 읽음 API는 없다. **`entities/noti`의 `readNoti`** 를 쓴다. 알림이력 화면과 동일 (`PUT /cms-service/api/v1/noti/{notiId}/read`).
### 6-5. 검색 파라미터 (화면 미표시)
서버 `toNotiSearchDto()` 기본: `notiType = ADSS`. 화면 필터 없음.
| DashboardMainSearchDto | 화면 | 비고 |
| ---------------------- | ---- | --------------------------------- |
| `notiType` | 없음 | 서버 기본 `ADSS` |
| `notReadYn` | 없음 | 통계 칩 클릭 필터 시 후보 (TBD) |
| `startDt` / `endDt` | 없음 | 대시보드 기본 미전달 |
| `docSeComCd` | 없음 | 알림만 필터. 진행상황 카드와 무관 |
---
## 7. 영역 D — 공지사항
`getMainDashboard`에 공지 필드는 없다. **board-service** `getUserPostList`로 별도 조회한다.
- Base: `GET /board-service/api/v1/posts/user/{AS_DASHBOARD_BBS_ID}`
- `AS_DASHBOARD_BBS_ID` = `NEXT_PUBLIC_AS_DASHBOARD_BBS_ID` (로컬 예: `7`)
- 클라이언트: `@/entities/board` — `postApi.getUserPostList` / `useUserPostList`
페이지네이션 컨트롤은 없다. 요약 목록만 표시한다 (`page=0`, `size=3`).
### 7-1. 헤더·버튼
| 버튼명 | 동작 |
| ------ | ------------------------------------------------------------------------------------------- |
| 더보기 | `/cms/bbsnotice/{AS_DASHBOARD_BBS_ID}` 로 이동. as 앱에 공지 목록 라우트는 없음(st/cm 관례) |
### 7-2. 그리드 컬럼
스크린샷 헤더 순서. `UserPost`.
| 표시명 | Field | 비고 |
| ------ | --------- | ---------------------------------------------------- |
| 번호 | `rowNo` | `upndFixYn` = `Y` 이면 `[공지]` Badge, 그 외 `rowNo` |
| 제목 | `pstTtl` | truncate |
| 작성자 | `wrtrNm` | |
| 작성일 | `regDt` | 표시 `YYYY-MM-DD` (응답 datetime) |
| 조회수 | `inqNocs` | 숫자 |
조회 파라미터 (`UserPostSearchParams`):
| 필드 | 값 | 비고 |
| ------------- | ----------- | ------------------------------------------ |
| `page` | `0` | Spring 0-based |
| `size` | `3` | 대시보드 요약. 스크린샷 3행 |
| `keywordType` | `bbsTypeCd` | st 대시보드 공지 패널과 동일 |
| `keyword` | `BBSNOTICE` | 게시판 유형. path의 bbsId와 함께 공지 한정 |
응답에 있으나 그리드에 **표시하지 않음**: `bbsId`, `pstNo`(행 키·상세 이동), `pstCn`, `upndFixYn`(번호 열 판정만), `rgtrNm`, `atflCd` 등.
### 7-3. 행 상호작용
| 동작 | 설명 |
| ------- | ------------------------------------------------------------------------------ |
| 행 클릭 | `/cms/bbsnotice/{AS_DASHBOARD_BBS_ID}/{pstNo}` (as 앱 공지 상세 라우트 미구현) |
---
## 8. API/DTO 명세
### 8-1. 공통
| 항목 | 값 |
| --------------- | ---------------------------------------------------------------------------------------------- |
| Base prefix | `/as-service/api/v1/dashboard` (`ENDPOINT.AS_SERVICE.DASHBOARD`) |
| 인증 | `Authorization` 헤더. 프론트는 `client` 인터셉터. 로그인 기관 권한은 서버가 사용자 캐시로 적용 |
| 클라이언트 모듈 | `@/entities/dashboard` — `dashboardService` / `useGetMainDashboard` |
| 알림 읽음 | `@/entities/noti` — `notiService.readNoti` / `useReadNoti` |
| 공지사항 | `@/entities/board` — `postApi.getUserPostList` / `useUserPostList` |
영역 A(지도)는 본 컨트롤러·board-service 범위 밖 → **TBD**.
### 8-2. 대시보드 메인 조회 — `getMainDashboard`
| 기능 | Method | URL | Request DTO | Response DTO | API |
| -------------------------------------------- | ------ | ------------- | ------------------------------- | -------------------------- | ----------------------------------------------------------- |
| 대시보드 메인 통합진행상태 및 알림 정보 조회 | GET | `{BASE}/main` | Query: `DashboardMainSearchDto` | `DashboardMainResponseDto` | `dashboardService.getMainDashboard` / `useGetMainDashboard` |
**Request (`DashboardMainSearchDto`)**
| 필드 | 필수 | 설명 |
| ------------ | ---- | ---------------------------------------------------------------------- |
| `searchYear` | N | 검색 연도. 미전달 시 서버 당해 연도. 영역 B 집계 기간 |
| `notiType` | N | 알림 종류. 미전달 시 서버 `ADSS` |
| `notReadYn` | N | 알림 확인 여부 `Y`/`N`. 본 화면 미사용 |
| `startDt` | N | 알림 시작일 `YYYY-MM-DD`. 본 화면 미사용 |
| `endDt` | N | 알림 종료일 `YYYY-MM-DD`. 본 화면 미사용 |
| `docSeComCd` | N | 알림 문서구분 (`INSTL`/`FNRS`/`SEWER`/`CNFM` 등). 진행상황 집계와 무관 |
`Authorization`은 service 시그니처에 넣지 않는다.
**Response (`DashboardMainResponseDto`)**
| 필드 | 타입 | 설명 |
| ------------------ | --------------------------- | --------------------------- |
| `docPrgrsSttsList` | `DocPrgrsSttsResponseDto[]` | 문서별 진행상태 + TOTAL 행 |
| `notiList` | `NotiAsUserResponseDto[]` | 최신 최대 5건 |
| `notiStats` | `NotiAsStatResponseDto` | 오늘 / 지연 / 완료대기 건수 |
**`DocPrgrsSttsResponseDto`**
| 필드 | 타입 | 설명 |
| ------------ | ------ | --------------------------------------------- |
| `docSeNm` | string | 문서 구분 명칭 |
| `docSeComCd` | string | `INSTL` / `FNRS` / `SEWER` / `CNFM` / `TOTAL` |
| `reqCnt` | number | 신청 건수 |
| `prgrsCnt` | number | 진행 건수 |
| `cmptnCnt` | number | 완료 건수 |
| `totCnt` | number | 총 건수 (화면 미표시) |
**`NotiAsStatResponseDto`**
| 필드 | 타입 | 설명 |
| -------------- | ------ | --------- |
| `todayCount` | number | 오늘 알림 |
| `delayCount` | number | 지연 알림 |
| `pendingCount` | number | 완료대기 |
**`NotiAsUserResponseDto`** — 필드 상세는 `docs/spec/nti-hstry` §6-2와 동일 (`notiId`, `notiTtl`, `regDt`, `notiUrl`, `notReadYn`, `notiPrcsCmptnYn` 등).
### 8-3. 알림 읽음 처리 — `readNoti`
대시보드 컨트롤러가 아니라 cms-service. 영역 C [처리하기]에서 호출.
| 기능 | Method | URL | Request DTO | Response DTO | API |
| -------------- | ------ | ---------------------------------------- | ----------------------- | ---------------- | -------------------------------------- |
| 알림 읽음 처리 | PUT | `/cms-service/api/v1/noti/{notiId}/read` | Path: `notiId` (`Long`) | `Long` (알림 ID) | `notiService.readNoti` / `useReadNoti` |
Body 없음. 성공 시 `dashboardBaseKey`(및 필요 시 `notiBaseKey`) invalidate.
### 8-4. 전국 행정지원 지도 — TBD
| 기능 | Method | URL | Request DTO | Response DTO | API |
| ------------------- | ------ | --- | ----------- | ------------ | --- |
| 지도 마커·음영 조회 | TBD | TBD | TBD | TBD | TBD |
### 8-5. 공지사항 — `getUserPostList`
board-service. bbsId는 환경변수 `AS_DASHBOARD_BBS_ID`로 path에 고정한다.
| 기능 | Method | URL | Request DTO | Response DTO | API |
| ----------------------- | ------ | ------------------------------------------ | ----------------------------- | ------------------------ | --------------------------------------------- |
| 대시보드 공지 요약 목록 | GET | `/board-service/api/v1/posts/user/{bbsId}` | Query: `UserPostSearchParams` | `PageResponse<UserPost>` | `postApi.getUserPostList` / `useUserPostList` |
**Request (`UserPostSearchParams`)** — 본 화면 고정값
| 필드 | 필수 | 설명 |
| ------------- | ---- | -------------- |
| `page` | N | `0` |
| `size` | N | `3` |
| `keywordType` | N | `bbsTypeCd` |
| `keyword` | N | `BBSNOTICE` |
| `popupYn` 등 | N | 본 화면 미사용 |
**Response (`UserPost`) — 화면 매핑 필드**
| 필드 | 타입 | 설명 |
| ----------- | ------ | --------------------------- |
| `rowNo` | number | 번호. 고정글이 아니면 표시 |
| `pstNo` | number | 게시 번호. 상세 이동 키 |
| `bbsId` | number | 게시판 ID |
| `pstTtl` | string | 제목 |
| `wrtrNm` | string | 작성자 |
| `regDt` | string | 작성일 |
| `inqNocs` | number | 조회수 |
| `upndFixYn` | string | 상단고정 `Y`/`N` → `[공지]` |
---
## 9. UI 구현 가이드
### 9-1. 기술 스택·원칙
- 라우트: 기존 홈 `app/(main)/(with-base-layout)/page.tsx` → `pages/home` (현재 `MainPage` 플레이스홀더)
- UI: `@common/components` — Card, Badge, Button, Table(공지). 지도 라이브러리 TBD
- 데이터: React Query `useGetMainDashboard()`. 처리하기는 `useReadNoti()`. 공지는 `useUserPostList()`
- 진행상황 카드: `docSeComCd`로 4슬롯 매핑. `TOTAL` 제외
- 알림 목록: 테이블이 아닌 행 리스트(뱃지 + 일자 + 제목 + 버튼)
- 공지: `@tanstack/react-table` + `useDataTable`, 페이지네이션 없음
- 검색 조건 URL 동기화: 본 화면 필터 없음. `searchYear` UI가 생기면 그때 검토
### 9-2. 모듈·파일 배치
| 구분 | 경로 |
| ------------- | ------------------------------------------------------------ |
| 라우트 | `app/(main)/(with-base-layout)/page.tsx` |
| 페이지 | `pages/dashboard/ui/dashboard-page.tsx` |
| 지도 | `pages/dashboard/ui/national-as-map.tsx` (API TBD, Skeleton) |
| 진행상황 카드 | `pages/dashboard/ui/doc-prgrs-stts-cards.tsx` |
| 알림이력 | `pages/dashboard/ui/dashboard-noti-list.tsx` |
| 공지사항 | `pages/dashboard/ui/dashboard-notice-table.tsx` |
| 공지 컬럼 | `pages/dashboard/model/dashboard-notice-table-columns.tsx` |
| Entity API | `entities/dashboard/api/dashboard-service.ts` |
| Entity Hook | `entities/dashboard/api/queries.ts` → `useGetMainDashboard` |
| Entity Model | `entities/dashboard/model/dashboard-interface.ts` |
| 알림 읽음 | `entities/noti` → `useReadNoti` |
| 공지 Entity | `entities/board` → `useUserPostList` |
Entity(`getMainDashboard`, `getUserPostList`) 구현됨. 영역 B·C·D 화면 구현. 영역 A는 Skeleton.
### 9-3. 영역별 UI 매핑
| 영역 | 컴포넌트 | 데이터 |
| ---- | ------------------------ | ---------------------------------------------------------------------------- |
| A | `national-as-map` | Skeleton (지도 API TBD) |
| B | `doc-prgrs-stts-cards` | `useGetMainDashboard` → `docPrgrsSttsList` (`TOTAL` 제외, 코드 매핑) |
| C | `dashboard-noti-list` | 동 훅 → `notiStats`, `notiList`. 처리 → `useReadNoti`. 전체보기 `/nti-hstry` |
| D | `dashboard-notice-table` | `useUserPostList` → `content[]`. [더보기]·행 클릭 → `/cms/bbsnotice/...` |
### 9-4. 다이얼로그·라우팅
| 항목 | 내용 |
| -------------- | -------------------------------------------------------------------------------------- |
| 다이얼로그 | 없음 |
| 본 화면 라우트 | `/` (홈) |
| 알림 처리 이동 | [처리하기] → `readNoti` 후 `notiUrl` (알림이력과 동일, SEWER URL은 상세 세그먼트 제거) |
| 알림 전체보기 | `/nti-hstry` |
| 공지 더보기 | `/cms/bbsnotice/{AS_DASHBOARD_BBS_ID}` (as 앱 공지 라우트 미구현, st/cm 관례) |
| 공지 행 클릭 | `/cms/bbsnotice/{AS_DASHBOARD_BBS_ID}/{pstNo}` |
| 진행상황 카드 | §5-3 목록 라우트 (`/fnrs-cnsltn`, `/instl-sncn`, `/design-cnstn`, `/sewer-bsc-plan`) |
### 9-5. 데이터 흐름
```
BEGIN
// 최초 진입 (홈)
CALL getMainDashboard({})
DISPLAY 영역 B // docPrgrsSttsList — FNRS/INSTL/CNFM/SEWER 슬롯
DISPLAY 영역 C // notiStats + notiList(≤5)
CALL getUserPostList({ page: 0, size: 3, keywordType: "bbsTypeCd", keyword: "BBSNOTICE" })
DISPLAY 영역 D
CALL 지도 데이터 // TBD → 영역 A
ON 영역 C [전체보기] CLICK DO
NAVIGATE /nti-hstry
END ON
ON 영역 C [처리하기] CLICK DO
CALL readNoti(notiId)
IF notiUrl THEN
NAVIGATE notiUrl
END IF
CALL getMainDashboard // invalidate dashboardBaseKey
END ON
ON 영역 B 카드 CLICK DO
NAVIGATE 문서 목록 // §5-3
END ON
ON 영역 D [더보기] CLICK DO
NAVIGATE /cms/bbsnotice/{AS_DASHBOARD_BBS_ID}
END ON
ON 영역 D 행 CLICK DO
NAVIGATE /cms/bbsnotice/{AS_DASHBOARD_BBS_ID}/{pstNo}
END ON
END
```
@@ -33,10 +33,10 @@
├──────────────────────────────────────────────────────────────────────────────────────────────────────┤ ├──────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ [영역 B] 필수첨부파일 목록 │ │ [영역 B] 필수첨부파일 목록 │
│ ┌────────────────────────────────────────────────────────────────────────────────────────────────┐ │ │ ┌────────────────────────────────────────────────────────────────────────────────────────────────┐ │
│ │ 필수첨부파일아이디 | 문서구분 | 필수첨부파일명 | 필수첨부여부 | 비고 | 등록일시 | 수정일시 | 관리 │ │ │ 필수첨부파일아이디 | 문서구분 | 필수첨부파일명 | 필수첨부여부 | 필수파일검토내용 | 비고 | 관리 │ │
│ ├────────────────────────────────────────────────────────────────────────────────────────────────┤ │ │ ├────────────────────────────────────────────────────────────────────────────────────────────────┤ │
│ │ EAF-0001 | 설치인가 | 사업계획서 | 예 | 서명본 첨부 | 2026-04-22 10:10:00 | — | [수정][삭제] │ │ │ │ EAF-0001 | 설치인가 | 사업계획서 | 예 | 사업비 내역 검토 | 서명본 첨부 | [수정][삭제] │ │
│ │ EAF-0002 | 기본계획 | 변경사유서 | 예 | — | 2026-04-22 11:00:00 | — | [수정][삭제] │ │ │ │ EAF-0002 | 기본계획 | 변경사유서 | 예 | 변경 전후 비교표 | — | [수정][삭제] │ │
│ └────────────────────────────────────────────────────────────────────────────────────────────────┘ │ │ └────────────────────────────────────────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────────────────────────────────────┘ └──────────────────────────────────────────────────────────────────────────────────────────────────────┘
@@ -47,7 +47,9 @@
│ 필수첨부파일아이디(자동발급) [EAF-0004] │ │ 필수첨부파일아이디(자동발급) [EAF-0004] │
│ 문서구분 [설치인가 ▼] │ │ 문서구분 [설치인가 ▼] │
│ 필수첨부파일명 [예: 사업계획서] │ │ 필수첨부파일명 [예: 사업계획서] │
│ 양식파일 [파일 선택] │
│ 필수첨부여부 [ ] 예 [ ] 아니오 │ │ 필수첨부여부 [ ] 예 [ ] 아니오 │
│ 필수파일검토내용 [검토내용을 입력하세요]│
│ 비고 [비고를 입력하세요] │ │ 비고 [비고를 입력하세요] │
├──────────────────────────────────────────────────────┤ ├──────────────────────────────────────────────────────┤
│ [취소] [저장] │ │ [취소] [저장] │
@@ -139,16 +141,16 @@ END
### 5-2. 그리드 컬럼 ### 5-2. 그리드 컬럼
| 컬럼명 | Field | 타입 | 비고 | | 컬럼명 | Field | 타입 | 비고 |
| ------------------ | ------------------- | ------ | ------------------------------------- | | ------------------ | -------------------- | ------ | ------------------------------------- |
| 필수첨부파일아이디 | `esntlAtchFileId` | string | 예: `EAF-0001`, 클라이언트 생성/입력 | | 필수첨부파일아이디 | `esntlAtchFileId` | string | 예: `EAF-0001`, 클라이언트 생성/입력 |
| 문서구분 | `docSeComCd` | string | 코드명 표시 — 공통코드 (TBD 상위코드) | | 문서구분 | `docSeComCd` | string | 코드명 표시 — 공통코드 (TBD 상위코드) |
| 필수첨부파일명 | `esntlAtchFileNm` | string | | | 필수첨부파일명 | `esntlAtchFileNm` | string | |
| 필수첨부여부 | `esntlAtchYn` | string | `Y`→예, `N`→아니오 | | 필수첨부여부 | `esntlAtchYn` | string | `Y`→예, `N`→아니오 |
| 비고 | `esntlAtchFileRmrk` | string | 없으면 `—` 표시 | | 필수파일검토내용 | `esntlAtchFileRvwCn` | string | 없으면 `—` 표시, 최대 2줄 clamp |
| 등록일시 | `regDt` | string | `yyyy-MM-dd HH:mm:ss` | | 비고 | `esntlAtchFileRmrk` | string | 없으면 `—` 표시 |
| 수정일시 | `mdfcnDt` | string | 미수정 시 빈 값 또는 `—` | | 양식 내려받기 | `atchFileGroupId` | string | 양식파일 다운로드 버튼 |
| 관리 | — | — | 행별 **[수정] · [삭제]** | | 관리 | — | — | 행별 **[수정] · [삭제]** |
- 행 클릭 시 별도 상세 화면 없음. 편집은 **[수정]** 버튼으로 다이얼로그 C를 연다. - 행 클릭 시 별도 상세 화면 없음. 편집은 **[수정]** 버튼으로 다이얼로그 C를 연다.
@@ -167,13 +169,15 @@ END
### 6-1. 폼 필드 ### 6-1. 폼 필드
| 표시명 | 입력 유형 | 필수 | 기본값 | API 파라미터 매핑 | 비고 | | 표시명 | 입력 유형 | 필수 | 기본값 | API 파라미터 매핑 | 비고 |
| ------------------ | ---------- | ---- | ------ | ------------------- | -------------------------------- | | ------------------ | ------------------- | ---- | ------ | -------------------- | ---------------------------------- |
| 필수첨부파일아이디 | (Readonly) | Y | — | `esntlAtchFileId` | 등록 시 미노출 | | 필수첨부파일아이디 | (Readonly) | Y | — | `esntlAtchFileId` | 등록 시 미노출 |
| 문서구분 | CodeSelect | Y | — | `docSeComCd` | 예: 설치인가, 기본계획 | | 문서구분 | CodeSelect | Y | — | `docSeComCd` | 예: 설치인가, 기본계획 |
| 필수첨부파일명 | Text | Y | — | `esntlAtchFileNm` | placeholder: "예: 사업계획서" | | 필수첨부파일명 | Text | Y | — | `esntlAtchFileNm` | placeholder: "예: 사업계획서" |
| 필수첨부여부 | RadioGroup | Y | Y | `esntlAtchYn` | Y/N, UI 표시: 예/아니오 | | 양식파일 | AttachmentFileField | N | — | `atchFileGroupId` | TUS 파일 업로드 |
| 비고 | Textarea | N | — | `esntlAtchFileRmrk` | placeholder: "비고를 입력하세요" | | 필수첨부여부 | RadioGroup | Y | Y | `esntlAtchYn` | Y/N, UI 표시: 예/아니오 |
| 필수파일검토내용 | Textarea | N | — | `esntlAtchFileRvwCn` | placeholder: "검토내용을 입력하세요"|
| 비고 | Textarea | N | — | `esntlAtchFileRmrk` | placeholder: "비고를 입력하세요" |
### 6-2. 버튼 ### 6-2. 버튼
@@ -210,28 +214,32 @@ END
**응답 DTO** **응답 DTO**
| Field | 타입 | 설명 | | Field | 타입 | 설명 |
| ------------------- | ------- | -------------------------------- | | -------------------- | -------------------------- | -------------------------------- |
| `esntlAtchFileId` | string | 필수첨부파일아이디 | | `esntlAtchFileId` | string | 필수첨부파일아이디 |
| `docSeComCd` | string | 문서구분공통코드 | | `docSeComCd` | string | 문서구분공통코드 |
| `esntlAtchFileNm` | string | 필수첨부파일명 | | `esntlAtchFileNm` | string | 필수첨부파일명 |
| `esntlAtchYn` | string | 필수첨부여부 (Y/N) | | `esntlAtchYn` | string | 필수첨부여부 (Y/N) |
| `esntlAtchFileRmrk` | string? | 필수첨부파일비고 | | `esntlAtchFileRvwCn` | string? | 필수첨부파일검토내용 |
| `rgtr` | string? | 등록자 UUID | | `esntlAtchFileRmrk` | string? | 필수첨부파일비고 |
| `rgtrNm` | string? | 등록자명 | | `atchFileGroupId` | string? | 첨부파일그룹아이디 (양식파일) |
| `mdfr` | string? | 수정자 UUID | | `atchFileList` | AtflMngClientResponseDto[]?| 첨부파일 목록 |
| `regDt` | string? | 등록일시 (`yyyy-MM-dd HH:mm:ss`) | | `rgtr` | string? | 등록자 UUID |
| `mdfcnDt` | string? | 수정일시 | | `rgtrNm` | string? | 등록자명 |
| `mdfr` | string? | 수정자 UUID |
| `regDt` | string? | 등록일시 (`yyyy-MM-dd HH:mm:ss`) |
| `mdfcnDt` | string? | 수정일시 |
**저장 Request** **저장 Request**
| Field | 필수 | 설명 | | Field | 필수 | 설명 |
| ------------------- | ---- | ------------------ | | -------------------- | ---- | -------------------- |
| `esntlAtchFileId` | Y | 필수첨부파일아이디 | | `docSeComCd` | Y | 문서구분 |
| `docSeComCd` | N | 문서구분 | | `esntlAtchFileNm` | N | 필수첨부파일명 |
| `esntlAtchFileNm` | N | 필수첨부파일명 | | `esntlAtchYn` | N | 필수첨부여부 |
| `esntlAtchYn` | N | 필수첨부여부 | | `esntlAtchFileRvwCn` | N | 필수첨부파일검토내용 |
| `esntlAtchFileRmrk` | N | 비고 | | `esntlAtchFileRmrk` | N | 비고 |
| `atchFileGroupId` | N | 첨부파일그룹아이디 |
**검색 Query** **검색 Query**
+285
View File
@@ -0,0 +1,285 @@
# 화면설계서: 내 행정지원
## 1. 기본 정보
| 항목 | 내용 |
| ---------- | -------------------------------------------- |
| 버전 | 1.0 |
| 프로세스명 | UI설계 |
| 화면명 | 내 행정지원 |
| 화면 ID | AS_SC0204011 |
| 화면 유형 | [v] 조회 [ ] 입력 [ ] 수정 [ ] 삭제 [ ] 출력 |
| 메뉴 경로 | 메인 > 행정지원서비스 > 내 행정지원 |
---
## 2. 화면 목적
로그인한 사용자가 **본인이 신청한 행정지원 문서**를 기간·구분·진행상태로 조회한다. 목록에서 행을 선택하면 해당 문서 상세 화면으로 이동한다.
---
## 3. 화면 구성
### 3-1. 전체 레이아웃
```
┌─────────────────────────────────────────────────────────────────────────────────┐
│ 브레드크럼: 메인 > 행정지원서비스 > 내 행정지원 │
│ 페이지 타이틀: 내 행정지원 │
├─────────────────────────────────────────────────────────────────────────────────┤
│ [영역 A] 검색 필터 │
│ 기간 [시작일] ~ [종료일] 구분 ▼ 진행상태 ▼ [검색] │
├─────────────────────────────────────────────────────────────────────────────────┤
│ [영역 B] 내 신청 목록 │
│ 타이틀: 내 신청 목록 N개 │
│ ┌────┬──────┬────────┬──────────────┬────────────┬────────┬──────────────┐ │
│ │ No │ 구분 │ 신청일 │ 진행상태 │ 완료(예정)일│ 담당자 │ 담당자연락처 │ │
│ ├────┼──────┼────────┼──────────────┼────────────┼────────┼──────────────┤ │
│ │ … │ … │ … │ prgrsSttsComNm│ … │ … │ … │ │
│ └────┴──────┴────────┴──────────────┴────────────┴────────┴──────────────┘ │
│ [페이지네이션] │
└─────────────────────────────────────────────────────────────────────────────────┘
```
### 3-2. 화면 데이터 흐름
```
BEGIN
// 최초 진입
// 기간 기본값: 오늘 기준 최근 1개월 (startDate = today-1M, endDate = today)
CALL findMyElaperDocList(ElaprMyDocSearchDto + pageable)
DISPLAY 내 신청 목록 그리드 (영역 B)
ON [검색] 버튼 CLICK DO
CALL findMyElaperDocList(영역 A 조건 + page=0)
DISPLAY 내 신청 목록 그리드 (영역 B)
END ON
ON 페이지네이션 CHANGE DO
CALL findMyElaperDocList(영역 A 조건 + page/size)
DISPLAY 내 신청 목록 그리드 (영역 B)
END ON
ON 영역 B 행 CLICK DO
// docSeComCd·prgrsSttsComCd·rgtr → view/edit
NAVIGATE 문서 상세 또는 수정 화면
END ON
END
```
---
## 4. 영역 A — 검색 필터
구성: **기간 · 구분 · 진행상태** (+ 검색).
> UI설계 상세설명의 `docSecs` / `docStatusCd`는 레거시·설계 문서 명칭이다.
> **API SoT**는 `ElaprMyDocSearchDto`의 `docSeComCd` / `prgrsSttsComCd`를 사용한다.
### 4-1. 필드 정의
| 표시명 | 입력 유형 | 필수 | 기본값 | API 파라미터 매핑 | 비고 |
| ---------- | ---------------- | ---- | --------------------------- | ----------------- | --------------------------------------------------------------------- |
| 기간(시작) | DatePicker | N | 오늘 − 1개월 (`YYYY-MM-DD`) | `startDate` | 신청일(`reqDt`) 기준 검색으로 가정 |
| 기간(종료) | DatePicker | N | 오늘 (`YYYY-MM-DD`) | `endDate` | |
| 구분 | Select(공통코드) | N | 전체(미선택) | `docSeComCd` | 상위코드 `ASDOCSE`. UI설계 `docSecs` → API `docSeComCd` |
| 진행상태 | Select(공통코드) | N | 전체(미선택) | `prgrsSttsComCd` | 상위코드 = `{docSeComCd}STTS` (예: `INSTL` → `INSTLSTTS`). UI설계 `docStatusCd` → API 필드명 |
### 4-2. 버튼
| 버튼명 | 동작 |
| ------ | ----------------------------------------------------------------- |
| 검색 | 영역 A 조건으로 `findMyElaperDocList` 호출 → 영역 B 갱신 (page=0) |
### 4-3. 콤보 의존
| 트리거 | 동작 |
| ----------- | ------------------------------------------------------------------- |
| 구분 변경 | 진행상태 옵션을 `upCd = docSeComCd + "STTS"`로 재조회. 진행상태 선택값 RESET |
| 구분 미선택 | 진행상태 비활성(옵션 비움). `upCd` 조합 불가이므로 조회하지 않음 |
---
## 5. 영역 B — 내 신청 목록
### 5-1. 그리드 컬럼
| 표시명 | Field | 비고 |
| ------------ | ---------------- | ------------------------------------------------- |
| No | (row index) | 페이지 오프셋 반영 |
| 구분 | `docSeComNm` | 코드 `docSeComCd` (`ASDOCSE`) |
| 신청일 | `reqDt` | 표시 `YYYY-MM-DD` (응답은 `yyyy-MM-dd HH:mm:ss`) |
| 진행상태 | `prgrsSttsComNm` | 텍스트 렌더링 (단계형 프로그레스 미사용) |
| 완료(예정)일 | `cmptnExpctDt` | 표시 `YYYY-MM-DD` |
| 담당자 | `picNm` | |
| 담당자연락처 | `picTelno` | |
응답에 있으나 그리드에 **표시하지 않음**:
| Field | 비고 |
| ---------------- | ----------------------------------------------------------------- |
| `docNo` | 문서번호 — 행 클릭 라우팅 키 |
| `docTtl` | 문서명/사업명 — 미표시 |
| `chgCycl` | 변경차수 — 그리드 미표시. SEWER 이동 시 query 파라미터로 사용 |
| `prgrsSttsComCd` | 진행상태 코드 — 컬럼 표시는 `prgrsSttsComNm` |
| `rgtr` | 등록자 UUID — edit 판정용 (`rgtr === userUnqId`) |
### 5-2. 목록 메타·페이지네이션
| 항목 | 매핑 |
| ------------- | ------------------------------------------- |
| 건수 `N개` | `PageResponse.totalElements` |
| page / size | `ElaprMyDocSearchDto`의 `PageableParams` |
| 페이지 컨트롤 | Spring `Page` 표준 (`number`, `totalPages`) |
### 5-3. 행 상호작용
| 동작 | 설명 |
| ------- | -------------------------------------------------------------------------------------------- |
| 행 클릭 | `docSeComCd`·`prgrsSttsComCd`·`rgtr`로 view/edit 분기 후 이동 (`resolveMyPbadmsSprtDetailPath`) |
**edit 조건** (기존 도메인 목록과 동일):
| `docSeComCd` | edit 조건 |
| ------------------------ | ---------------------------------------------------------------------- |
| `INSTL` / `FNRS` / `SEWER` | `(prgrsSttsComCd === "SV" \|\| "SL") && rgtr === loggedInUserUnqId` |
| `CNFM` | `prgrsSttsComCd === "CV" && rgtr === loggedInUserUnqId` |
| `docSeComCd` | edit 경로 | view 경로 |
| ------------ | -------------------------------------------- | --------------------------------------- |
| `FNRS` | `/fnrs-cnsltn/{docNo}/edit` | `/fnrs-cnsltn/{docNo}` |
| `INSTL` | `/instl-sncn/{docNo}/edit` | `/instl-sncn/{docNo}` |
| `CNFM` | `/design-cnstn/{docNo}/edit` | `/design-cnstn/{docNo}` |
| `SEWER` | `/sewer-bsc-plan/{docNo}/edit?chgCycl={n}` | `/sewer-bsc-plan/{docNo}?chgCycl={n}` |
| 그 외 | 이동하지 않음 | |
- SEWER `chgCycl`: `row.chgCycl ?? 1`
- 조건 불충족 시 항상 view
---
## 6. API/DTO 명세
### 6-1. 공통
| 항목 | 값 |
| --------------- | ------------------------------------------------------------------------------------------ |
| Base prefix | `/as-elapr-service/api/v1/elapr-prgrs-stts` (`ENDPOINT.AS_ELAPR_SERVICE.ELAPR_PRGRS_STTS`) |
| 인증 | 로그인 사용자 UUID — 서버 `SecurityUtils.getLoggedInUserUnqIdUUID()` (클라이언트 미전달) |
| 클라이언트 모듈 | `@/entities/elapr` — `elaprPrgrsSttsService` / `useElaprPrgrsSttsMyDocPage` |
### 6-2. 내 행정지원 목록 — `findMyElaperDocList`
| 기능 | Method | URL | Request DTO | Response DTO | API |
| ----------------- | ------ | --------------- | --------------------------------------- | ------------------------------------ | -------------------------------------------------------------------------- |
| 내 신청 목록 조회 | GET | `{BASE}/my/doc` | Query: `ElaprMyDocSearchDto` + Pageable | `PageResponse<MyAdssDocResponseDto>` | `elaprPrgrsSttsService.findMyElaperDocList` / `useElaprPrgrsSttsMyDocPage` |
**Request (`ElaprMyDocSearchDto` + Pageable)**
| 필드 | 필수 | 설명 |
| ------------------------ | ---- | ------------------------------------------------------------ |
| `startDate` | N | 검색 시작일 `YYYY-MM-DD` |
| `endDate` | N | 검색 종료일 `YYYY-MM-DD` |
| `docSeComCd` | N | 문서구분 (`INSTL` / `FNRS` / `DESIGN` / `SEWER` / `PWRK` 등) |
| `prgrsSttsComCd` | N | 문서진행상태공통코드 |
| `page` / `size` / `sort` | N | Spring Pageable |
**Response (`MyAdssDocResponseDto`)**
| 필드 | 타입 | 설명 |
| ---------------- | ------- | ------------------------------------------- |
| `docNo` | string | 문서번호 |
| `docTtl` | string? | 문서명/사업명 |
| `docSeComCd` | string? | 문서구분 코드 |
| `docSeComNm` | string? | 문서구분 코드명 (`ASDOCSE`) |
| `reqDt` | string? | 신청일시 `yyyy-MM-dd HH:mm:ss` |
| `prgrsSttsComCd` | string? | 문서진행상태 코드 |
| `prgrsSttsComNm` | string? | 문서진행상태 코드명 |
| `cmptnExpctDt` | string? | 완료(예정)일 `yyyy-MM-dd` |
| `picNm` | string? | 담당자 성명 |
| `picTelno` | string? | 담당자 연락처 |
| `chgCycl` | number? | 변경차수 |
| `rgtr` | string? | 등록자 UUID |
### 6-3. 공통코드 (콤보)
| 용도 | 상위코드 | API | 비고 |
| -------- | ------------ | --------------------------- | ----------------------------- |
| 구분 | `ASDOCSE` | `useCodeList("ASDOCSE")` 등 | 기존 관리 화면과 동일 |
| 진행상태 | `{docSeComCd}STTS` | `useCodeList(docSeComCd + "STTS")` | 예: `INSTLSTTS`, `FNRSSTTS`, `SEWERSTTS`. 구분 미선택 시 미호출 |
---
## 7. UI 구현 가이드
### 7-1. 기술 스택·원칙
- UI: `@common/components` — DatePicker, Select/`CodeSelect`, Button, DataTable, Pagination
- 목록: `@tanstack/react-table` + `useDataTable` (기존 `instl-list` / `fnrs-list` 패턴)
- 데이터: React Query `useElaprPrgrsSttsMyDocPage(params)`
- 검색 조건·페이지: URL `searchParams` 동기화 권장 (`useSearchParamsState` 스킬)
- 진행상태 컬럼: `prgrsSttsComNm` 텍스트 렌더링 (단계형 프로그레스 미사용)
- 행 클릭: `docSeComCd` + `prgrsSttsComCd` + `rgtr`로 view/edit 분기 (`SEWER`는 `chgCycl` query 포함)
### 7-2. 앱·파일 배치
| 구분 | 경로 |
| ------------ | -------------------------------------------------------------- |
| 라우트 | `app/(main)/(with-base-layout)/my-pbadms-sprt/page.tsx` |
| 페이지 | `pages/my-pbadms-sprt/ui/my-pbadms-sprt-list-page.tsx` |
| 목록 그리드 | `pages/my-pbadms-sprt/ui/my-pbadms-sprt-list.tsx` |
| 경로 해석 | `pages/my-pbadms-sprt/lib/resolve-detail-path.ts` |
| 테이블 컬럼 | `pages/my-pbadms-sprt/model/my-pbadms-sprt-table-columns.tsx` |
| Entity API | `entities/elapr/api/elapr-prgrs-stts-service.ts` |
| Entity Hook | `entities/elapr/api/queries.ts` → `useElaprPrgrsSttsMyDocPage` |
| Entity Model | `entities/elapr/model/elapr-prgrs-stts-interface.ts` |
### 7-3. 영역별 UI 매핑
| 영역 | 컴포넌트 | 데이터 |
| ---- | -------------------------- | ------------------------------------------ |
| A | `my-pbadms-sprt-list-page` | 로컬 state → `ElaprMyDocSearchDto` |
| B | DataTable + columns | `useElaprPrgrsSttsMyDocPage` → `content[]` |
### 7-4. 다이얼로그·라우팅
| 항목 | 내용 |
| ----------- | -------------------------------------------------------------------- |
| 다이얼로그 | 없음 (목록 전용) |
| 목록 라우트 | `/my-pbadms-sprt` |
| 상세 이동 | §5-3 표와 동일 (view/edit + `rgtr` 소유자 검사, SEWER `chgCycl`) |
### 7-5. 데이터 흐름
```
BEGIN
// 최초 진입
SET 기간 = 최근 1개월
CALL findMyElaperDocList
DISPLAY 영역 B
ON [검색] CLICK DO
CALL findMyElaperDocList // → 영역 B 갱신
END ON
ON 페이지 CHANGE DO
CALL findMyElaperDocList
END ON
ON 영역 B 행 CLICK DO
IF NOT docNo OR unsupported docSeComCd THEN
// no-op
ELSE IF canEdit(prgrsSttsComCd, rgtr === userUnqId) THEN
NAVIGATE edit 경로 // SEWER면 ?chgCycl=
ELSE
NAVIGATE view 경로
END IF
END ON
END
```
+352
View File
@@ -0,0 +1,352 @@
# 화면설계서: 알림이력
## 1. 기본 정보
| 항목 | 내용 |
| ----------- | -------------------------------------------- |
| 버전 | 1.4 |
| 프로세스명 | UI설계 |
| 화면명 | 알림이력 |
| 화면 ID | AS_SC0205011 |
| 요구사항 ID | NSUR-001-001 |
| 화면 유형 | [v] 조회 [ ] 입력 [ ] 수정 [ ] 삭제 [ ] 출력 |
| 메뉴 경로 | 메인 > 행정지원서비스 > 알림이력 |
---
## 2. 화면 목적
로그인한 사용자가 **본인에게 발송된 행정지원(ADSS) 알림**을 기간·구분으로 조회한다. 목록의 처리 버튼으로 해당 알림을 읽음 처리한 뒤, 연계된 업무 화면으로 이동한다.
---
## 3. 화면 구성
### 3-1. 전체 레이아웃
```
┌─────────────────────────────────────────────────────────────────────────────────┐
│ 브레드크럼: 메인 > 행정지원서비스 > 알림이력 │
│ 페이지 타이틀: 알림이력 │
├─────────────────────────────────────────────────────────────────────────────────┤
│ [영역 A] 검색 필터 │
│ 기간 [시작일] ~ [종료일] 구분 ▼ [검색] │
├─────────────────────────────────────────────────────────────────────────────────┤
│ [영역 B] 알림 이력 목록 │
│ 타이틀: 알림 이력 목록 N개 ▼ │
│ ┌────┬──────────────┬────────────────┬──────────┬────────┬────────┐ │
│ │ No │ 알림일시 │ 알림내용 │ 연계업무 │ 상태 │ 처리 │ │
│ ├────┼──────────────┼────────────────┼──────────┼────────┼────────┤ │
│ │ … │ regDt │ notiTtl │ docSeComNm│ Badge │ 버튼 │ │
│ └────┴──────────────┴────────────────┴──────────┴────────┴────────┘ │
│ [페이지네이션] │
└─────────────────────────────────────────────────────────────────────────────────┘
```
### 3-2. 화면 데이터 흐름
```
BEGIN
// 최초 진입
// 기간 기본값: 오늘 기준 최근 1개월 (시작일 = today-1M, 종료일 = today)
CALL findMyAsNotiList(NotiSearchDto + pageable)
DISPLAY 알림 이력 목록 그리드 (영역 B)
ON [검색] 버튼 CLICK DO
CALL findMyAsNotiList(영역 A 조건 + page=0)
DISPLAY 알림 이력 목록 그리드 (영역 B)
END ON
ON 페이지네이션 CHANGE DO
CALL findMyAsNotiList(영역 A 조건 + page/size)
DISPLAY 알림 이력 목록 그리드 (영역 B)
END ON
ON 영역 B [처리] 버튼 CLICK DO
CALL readNoti(notiId)
// UI설계: 해당 알림에 연계된 URL로 이동
NAVIGATE notiUrl
END ON
END
```
---
## 4. 영역 A — 검색 필터
구성: **기간 · 구분** (+ 검색).
서버 `findMyAsNotiList` 고정 정책 (클라이언트 미전달):
- `recvUnqId` = 로그인 사용자 UUID
- `delYn` = `N`
- `notiType` = `ADSS` (행정지원 전용 API)
### 4-1. 필드 정의
| 표시명 | 입력 유형 | 필수 | 기본값 | API 파라미터 매핑 | 비고 |
| ---------- | ---------- | ---- | --------------------------- | ----------------- | ------------------------------------------------------------------------------------------------ |
| 기간(시작) | DatePicker | N | 오늘 − 1개월 (`YYYY-MM-DD`) | `startDt` | `NotiSearchDto.startDt` |
| 기간(종료) | DatePicker | N | 오늘 (`YYYY-MM-DD`) | `endDt` | `NotiSearchDto.endDt` |
| 구분 | Select | N | 전체(미선택) | `docSeComCd` | `INSTL` / `FNRS` / `SEWER` / `CNFM` / `PWRK` (또는 단축 `I`/`F`/`S`/`C`/`P`). 공통코드 `ASDOCSE` |
### 4-2. 버튼
| 버튼명 | 동작 |
| ------ | -------------------------------------------------------------- |
| 검색 | 영역 A 조건으로 `findMyAsNotiList` 호출 → 영역 B 갱신 (page=0) |
### 4-3. 콤보·기본값 주의
| 항목 | 내용 |
| -------------- | ---------------------------------------------------------- |
| 구분 = 전체 | `docSeComCd` 미전달. `notiType`은 서버가 `ADSS`로 고정 |
| 구분 옵션 소스 | 공통코드 `ASDOCSE` (`docSeComCd` ↔ `docSeComNm`) |
| 기간 | UI설계 상세설명: 기본 최근 1개월. `startDt`/`endDt`로 전달 |
---
## 5. 영역 B — 알림 이력 목록
### 5-1. 그리드 컬럼
| 표시명 | Field | 비고 |
| -------- | ------------ | --------------------------------------------------------------- |
| No | (row index) | 페이지 오프셋 반영 |
| 알림일시 | `regDt` | 표시 `YYYY-MM-DD HH:mm` (응답 `yyyy-MM-dd HH:mm:ss`) |
| 알림내용 | `notiTtl` | 제목 문자열. UI 샘플은 `[YYYY-MM-DD] …` 접두 |
| 연계업무 | `docSeComNm` | 문서구분 공통코드명 (`docSeComCd` + `@CodeName` `ASDOCSE`) |
| 상태 | (derived) | `Badge` 렌더링. 판정 규칙은 §5-1-1 |
| 처리 | (action) | 상태별 버튼 라벨. 클릭 시 `readNoti` 후 `notiUrl`로 이동 (§5-3) |
#### 5-1-1. 상태 UI 케이스 (`@common/components` `Badge`)
판정에 사용: `notiPrcsCmptnYn`, `notReadYn`, `regDt` (오늘 기준 경과일).
| 조건 | 라벨 | Badge |
| ------------------------------------------------------------------------------ | -------- | ----------------------------- |
| `notiPrcsCmptnYn` = `Y` | 처리완료 | `variant="default"` (primary) |
| `notiPrcsCmptnYn` = `N` 이고 `notReadYn` = `Y` 이고 `regDt`로부터 **7일 초과** | 처리지연 | `color="red"` |
| `notiPrcsCmptnYn` = `N` 이고 `notReadYn` = `Y` 이고 `regDt`로부터 **7일 이내** | 처리중 | `color="green"` |
| `notiPrcsCmptnYn` = `N` 이고 `notReadYn` = `N` 이고 `regDt`로부터 **3일 초과** | 지연 | `color="orange"` |
| `notiPrcsCmptnYn` = `N` 이고 `notReadYn` = `N` 이고 `regDt`로부터 **3일 이내** | 미조회 | `color="yellow"` |
```
IF notiPrcsCmptnYn = Y THEN
DISPLAY Badge "처리완료" (primary / variant=default)
ELSE IF notReadYn = Y THEN
IF daysSince(regDt) > 7 THEN
DISPLAY Badge "처리지연" (red)
ELSE
DISPLAY Badge "처리중" (green)
END IF
ELSE // notReadYn = N
IF daysSince(regDt) > 3 THEN
DISPLAY Badge "지연" (orange)
ELSE
DISPLAY Badge "미조회" (yellow)
END IF
END IF
```
- `daysSince(regDt)`: 오늘 날짜 − `regDt` 일자 (일 단위)
- `regDt`가 없으면 상태 Badge 표시 전략 TBD (미표시 또는 기본값)
응답에 있으나 그리드에 **표시하지 않음**:
| Field | 비고 |
| ----------------- | ---------------------------------------------------------------------- |
| `notiId` | 알림 ID — 행 키 |
| `recvUnqId` | 수신 회원 UUID (서버가 로그인 사용자로 고정) |
| `senderUnqId` | 발신 회원 UUID |
| `notiType` | 알림 종류 (`ADSS`) |
| `notiCn` | 알림 내용 (목록에서는 서버가 제외할 수 있음) |
| `notReadYn` | 확인 여부 `Y`/`N`. 상태 Badge 판정에 사용 (§5-1-1) |
| `notiUrl` | 처리 버튼 이동 URL. 컬럼 미표시 |
| `notiLnkgUnqNo` | 알림연결고유번호 |
| `docSeComCd` | 문서구분 공통코드. 연계업무 표시는 `docSeComNm` 사용 |
| `notiIdntyDt` | 알림 확인 일시 |
| `notiIdntyYn` | 알림 확인 여부 (`notReadYn`과 동일 의미). 상태 판정은 `notReadYn` 사용 |
| `notiPrcsCmptnYn` | 알림 처리 완료 여부. 상태 Badge 판정에 사용 (§5-1-1) |
| `notiPrcsCmptnDt` | 알림 처리 완료 일시 |
### 5-2. 목록 메타·페이지네이션
| 항목 | 매핑 |
| ------------- | ------------------------------------------- |
| 건수 / size ▼ | 상단 우측 `N개` 셀렉트 → `Pageable.size` |
| page / size | `NotiSearchDto`의 `PageableParams` |
| 페이지 컨트롤 | Spring `Page` 표준 (`number`, `totalPages`) |
### 5-3. 행 상호작용
| 동작 | 설명 |
| -------------- | --------------------------------------------------------------------------- |
| 처리 버튼 클릭 | `readNoti(notiId)`로 읽음 처리 후, 해당 알림에 연계된 URL(`notiUrl`)로 이동 |
| 행 클릭 | UI설계에 없음. 처리 버튼만 명시 |
**처리 버튼 라벨** (상태 Badge §5-1-1 기준):
| 상태 | 버튼명 |
| -------- | -------- |
| 미조회 | 처리하기 |
| 지연 | 처리하기 |
| 처리중 | 처리하기 |
| 처리지연 | 처리하기 |
| 처리완료 | 상세보기 |
- `notiUrl`이 비어 있으면 이동하지 않음 (no-op). 읽음 처리는 `notiUrl` 유무와 관계없이 호출
- 읽음 처리 성공 후 목록 쿼리(`notiBaseKey`)를 invalidate 하여 영역 B 상태를 갱신
---
## 6. API/DTO 명세
### 6-1. 공통
| 항목 | 값 |
| --------------- | ---------------------------------------------------------------------------------------- |
| Base prefix | `/cms-service/api/v1/noti` (`ENDPOINT.CMS_SERVICE.NOTI`) |
| 인증 | 로그인 사용자 UUID — 서버 `SecurityUtils.getLoggedInUserUnqIdUUID()` (클라이언트 미전달) |
| 클라이언트 모듈 | `@/entities/noti` — `notiService` / `useFindMyAsNotiList` / `useReadNoti` |
### 6-2. 로그인한 사용자 행정지원 알림 목록 — `findMyAsNotiList`
| 기능 | Method | URL | Request DTO | Response DTO | API |
| ---------------------------------- | ------ | ------------------- | --------------------------------- | ------------------------------------- | ------------------------------------------------------ |
| 로그인한 사용자 행정지원 알림 목록 | GET | `{BASE}/as/my/list` | Query: `NotiSearchDto` + Pageable | `PageResponse<NotiAsUserResponseDto>` | `notiService.findMyAsNotiList` / `useFindMyAsNotiList` |
**Request (`NotiSearchDto` + Pageable)**
| 필드 | 필수 | 설명 |
| ------------------------ | ---- | --------------------------------------------------------------------------- |
| `recvUnqId` | N | 수신 회원 UUID. 서버가 로그인 사용자로 덮어씀 |
| `senderUnqId` | N | 발신 회원 UUID. 본 화면 필터에 없음 |
| `notiType` | N | 알림 종류. 본 API는 서버가 `ADSS`로 고정 |
| `notiTtlKeyword` | N | 알림 제목 키워드. 본 화면 필터에 없음 |
| `notiCnKeyword` | N | 알림 내용 키워드. 본 화면 필터에 없음 |
| `notReadYn` | N | 알림 확인 여부 (`Y`/`N`). 본 화면 검색 필터에 없음 |
| `delYn` | N | 삭제 여부. 서버가 `N`으로 고정 |
| `startDt` | N | 시작일자 `YYYY-MM-DD` — 영역 A 기간(시작) |
| `endDt` | N | 종료일자 `YYYY-MM-DD` — 영역 A 기간(종료) |
| `docSeComCd` | N | 문서 구분 공통 코드 (`INSTL`/`FNRS`/`SEWER`/`CNFM`/`PWRK` 등) — 영역 A 구분 |
| `page` / `size` / `sort` | N | Spring Pageable |
**Response (`NotiAsUserResponseDto`)**
| 필드 | 타입 | 설명 |
| ----------------- | ------- | ------------------------------------------------------- |
| `notiId` | number | 알림 ID |
| `recvUnqId` | string | 수신 회원 UUID |
| `senderUnqId` | string | 발신 회원 UUID |
| `notiType` | string | 알림 종류 (`ADSS`) |
| `notiTtl` | string | 알림 제목 |
| `notiCn` | string? | 알림 내용 (목록에서 제외될 수 있음) |
| `notReadYn` | string? | 알림 확인 여부 (`Y`/`N`). 상태 Badge 판정 |
| `regDt` | string? | 등록 일시 `yyyy-MM-dd HH:mm:ss`. 상태 Badge 경과일 기준 |
| `notiUrl` | string? | 알림 URL 주소 |
| `notiLnkgUnqNo` | string? | 알림연결고유번호 |
| `docSeComCd` | string? | 문서구분 공통코드 |
| `docSeComNm` | string? | 문서구분 공통코드명 (`ASDOCSE`) |
| `notiIdntyDt` | string? | 알림 확인 일시 `yyyy-MM-dd HH:mm:ss` |
| `notiIdntyYn` | string? | 알림 확인 여부 (`Y`/`N`) |
| `notiPrcsCmptnYn` | string? | 알림 처리 완료 여부 (`Y`/`N`). 상태 Badge 판정 |
| `notiPrcsCmptnDt` | string? | 알림 처리 완료 일시 `yyyy-MM-dd HH:mm:ss` |
### 6-3. 알림 읽음 처리 — `readNoti`
| 기능 | Method | URL | Request DTO | Response DTO | API |
| -------------- | ------ | ---------------------- | ----------------------- | ---------------- | -------------------------------------- |
| 알림 읽음 처리 | PUT | `{BASE}/{notiId}/read` | Path: `notiId` (`Long`) | `Long` (알림 ID) | `notiService.readNoti` / `useReadNoti` |
**Request**
| 필드 | 위치 | 필수 | 설명 |
| -------- | ---- | ---- | ------- |
| `notiId` | Path | Y | 알림 ID |
Body 없음.
**Response**
| 타입 | 설명 |
| ------ | ------------------------------ |
| number | 읽음 처리된 알림 ID (`notiId`) |
화면 매핑: 영역 B [처리] 버튼 클릭 시 호출. 성공 시 `notiBaseKey` invalidate → `findMyAsNotiList` 재조회.
---
## 7. UI 구현 가이드
### 7-1. 기술 스택·원칙
- UI: `@common/components` — DatePicker, Select, Button, DataTable, Pagination, **Badge**
- 목록: `@tanstack/react-table` + `useDataTable` (기존 `my-pbadms-sprt-list` 패턴)
- 데이터: React Query `useFindMyAsNotiList(params)`, `useReadNoti()`
- 검색 조건·페이지: URL `searchParams` 동기화 권장 (`useSearchParamsState` 스킬)
- 처리 컬럼: 행 클릭이 아니라 **버튼 클릭**으로 `readNoti` 후 `notiUrl` 이동
- 상태 컬럼: §5-1-1 규칙으로 `Badge` 렌더링 (`notiPrcsCmptnYn` / `notReadYn` / `regDt`)
- 연계업무: `docSeComNm` 표시
### 7-2. 앱·파일 배치
| 구분 | 경로 |
| ------------ | --------------------------------------------------------------------- |
| 라우트 | `app/(main)/(with-base-layout)/noti/page.tsx` |
| 페이지 | `pages/noti/ui/noti-list-page.tsx` |
| 목록 그리드 | `pages/noti/ui/noti-list.tsx` |
| 테이블 컬럼 | `pages/noti/model/noti-table-columns.tsx` |
| 상태 판정 | `pages/noti/lib/resolve-noti-status.ts` |
| Entity API | `entities/noti/api/noti-service.ts` |
| Entity Hook | `entities/noti/api/queries.ts` → `useFindMyAsNotiList`, `useReadNoti` |
| Entity Model | `entities/noti/model/noti-interface.ts` |
화면 레이어(`pages/noti`)·Entity(`findMyAsNotiList`, `readNoti`) 구현 완료.
### 7-3. 영역별 UI 매핑
| 영역 | 컴포넌트 | 데이터 |
| ---- | ------------------- | -------------------------------------------------------------- |
| A | `noti-list-page` | 로컬 state → `NotiSearchDto` |
| B | DataTable + columns | `useFindMyAsNotiList` → `content[]`. 처리 버튼 → `useReadNoti` |
### 7-4. 다이얼로그·라우팅
| 항목 | 내용 |
| ----------- | --------------------------------------------------------------------- |
| 다이얼로그 | 없음 (목록 전용) |
| 목록 라우트 | `/noti` |
| 상세 이동 | 처리 버튼 → `readNoti(notiId)` 후 `notiUrl` (절대/상대 경로 형식 TBD) |
### 7-5. 데이터 흐름
```
BEGIN
// 최초 진입
SET 기간 = 최근 1개월 // startDt / endDt 전달
CALL findMyAsNotiList
DISPLAY 영역 B
ON [검색] CLICK DO
CALL findMyAsNotiList // → 영역 B 갱신
END ON
ON 페이지 CHANGE DO
CALL findMyAsNotiList
END ON
ON 영역 B [처리] 버튼 CLICK DO
CALL readNoti(notiId) // → 영역 B 상태 갱신 (invalidate)
IF NOT notiUrl THEN
// no-op (읽음 처리는 수행됨)
ELSE
NAVIGATE notiUrl
END IF
END ON
END
```
@@ -0,0 +1,441 @@
# 화면설계서: 이전기본계획조회 (기본 계획 찾기)
## 1. 기본 정보
| 항목 | 내용 |
| ---------- | --------------------------------------------------------------- |
| 버전 | 0.2 |
| 프로세스명 | UI설계 |
| 화면명 | 이전기본계획조회 (기본 계획 찾기) |
| 화면 유형 | [v] 조회 [ ] 입력 [ ] 수정 [ ] 삭제 [ ] 출력 |
| 메뉴 경로 | 하수도기본계획 등록·수정 > 사업개요 > [이전기본계획조회] Dialog |
---
## 2. 화면 목적
하수도기본계획 등록·수정 화면에서 **이전(기수립) 기본계획** 수치와 관련 보고서를 **조회·다운로드**한다. 시도·구군·연도·처리시설을 선택한 뒤 총설·처리구역별 현황을 표시하고, 분류 트리에서 기본계획 파일을 내려받는다. **부모 사업개요 폼에는 수치를 반영하지 않는다**(레거시 `bscPlnPopup.jsp`와 동일).
---
## 3. 화면 구성
### 3-1. 전체 레이아웃
부모: 하수도기본계획 등록/수정 폼 → 사업개요 섹션의 `[이전기본계획조회]` 버튼으로 Dialog 오픈.
```
┌─────────────────────────────────────────────────────────────────────────────────┐
│ Dialog 타이틀: 기본 계획 찾기 [닫기] │
├─────────────────────────────────────────────────────────────────────────────────┤
│ [영역 A] 검색 필터 │
│ 시도 ▼ 구군 ▼ 연도 ▼ 처리시설 ▼ [조회] │
├─────────────────────────────────────────────────────────────────────────────────┤
│ [영역 B] 기본계획파일 │
│ ┌──────────────────────┬────────────────────────────────────────────────────┐ │
│ │ [영역 B-1] 분류 트리 │ [영역 B-2] 파일 목록 │ │
│ │ (약 40%) │ (약 60%) │ │
│ │ └ 노드… │ 파일명 / 다운로드 │ │
│ └──────────────────────┴────────────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────────────────────────────────┤
│ [영역 C] 총설 │
│ ┌────────┬──────────────당초──────────────┬──기준──┬──────────────변경──────────────┐ │
│ │ 구분 │ 1단계 │ 2단계 │ 3단계 │ 4단계 │ 기준 │ 1단계 │ 2단계 │ 3단계 │ 4단계 │ │
│ ├────────┼───────┼───────┼───────┼───────┼────────┼───────┼───────┼───────┼───────┤ │
│ │계획목표│ 년 │ 년 │ 년 │ 년 │ 년 │ 년 │ 년 │ 년 │ 년 │ │
│ │년도 │ │ │ │ │ │ │ │ │ │ │
│ └────────┴───────┴───────┴───────┴───────┴────────┴───────┴───────┴───────┴───────┘ │
├─────────────────────────────────────────────────────────────────────────────────┤
│ [영역 D] 처리구역별 현황 │
│ ┌────────┬──────────────당초──────────────┬──기준──┬──────────────변경──────────────┐ │
│ │ 구분 │ 1~4단계(+연도헤더) │ 기준 │ 1~4단계(+연도헤더) │ │
│ ├────────┼────────────────────────────────┼────────┼────────────────────────────────┤ │
│ │ 하수처리인구 … 배제방식 (읽기 전용 행) │ │
│ └────────┴────────────────────────────────┴────────┴────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────────┘
```
### 3-2. 화면 데이터 흐름
```
BEGIN
// 부모 폼 — 사업개요
ON [이전기본계획조회] 버튼 CLICK DO
OPEN PreviousPlanLookupDialog
// 프리필: 부모 폼 sewerBscPlan.sido / gugun / admdstCd
CALL findSidoCombo()
DISPLAY 시도 옵션 (영역 A)
IF 시도 프리필 THEN
CALL findGugunCombo(sidoCd)
DISPLAY 구군 옵션 (영역 A)
CALL getLvNowDmgMtncGnsvy({ level: "1", ctpvNm })
// TBD: useLvNowDmgMtncGnsvy enabled — level별 필수 파라미터만 검사하도록 조정
IF 구군 프리필 THEN
CALL getLvNowDmgMtncGnsvy({ level: "2", ctpvNm, sggNm })
DISPLAY 연도 옵션 (영역 A) // children 중 level=3 노드의 gnsvyYr
CALL getClsfSystm({ clsfSystmNm: gugunNm })
DISPLAY 분류 트리 (영역 B-1)
END IF
END IF
END ON
// 영역 A: 시도 변경
ON 영역 A 시도 CHANGE DO
RESET 구군 / 연도 / 처리시설
CALL findGugunCombo(sidoCd)
CALL getLvNowDmgMtncGnsvy({ level: "1", ctpvNm })
// 구군 미선택 → 연도·처리시설 옵션 비움, 트리 빈 안내
END ON
// 영역 A: 구군 변경
ON 영역 A 구군 CHANGE DO
RESET 연도 / 처리시설
CALL getLvNowDmgMtncGnsvy({ level: "2", ctpvNm, sggNm })
DISPLAY 연도 옵션 (영역 A) // level=3 노드 → gnsvyYr
CALL getClsfSystm({ clsfSystmNm: sggNm })
DISPLAY 분류 트리 (영역 B-1)
END ON
// 영역 A: 연도 변경
ON 영역 A 연도 CHANGE DO
RESET 처리시설
CALL getLvNowDmgMtncGnsvy({ level: "3", ctpvNm, sggNm, gnsvyYr })
DISPLAY 처리시설 옵션 (영역 A) // level=3 노드 children → swtrmtFcltCd / swtrmtFcltNm
END ON
// 영역 A: 조회
ON [조회] 버튼 CLICK DO
VALIDATE 시도, 구군, 연도(targetYear), 처리시설(swtrmtFcltCd)
CALL getNowDmgMtncGnsvy({ swtrmtFcltCd, ctpvNm, sggNm, targetYear })
DISPLAY 총설 (영역 C) // 응답[0]의 planYr / planYrDt* / planYrDc*
DISPLAY 처리구역별 현황 (영역 D) // 응답[0]의 지표 Dt1~4 / base / Dc1~4
END ON
// 영역 B: 트리 노드 선택
ON 영역 B-1 노드 CLICK DO
CALL getFile({ clsfSystmSn, sort, vetPrcsYn }) // Response DTO TBD
DISPLAY 파일 목록 (영역 B-2)
END ON
// 영역 B: 파일 다운로드
ON 영역 B-2 파일 CLICK DO
CALL getFilePrcs(fileSn) // 파일 메타(경로·제목) — Response TBD
CALL fileDownload({ filePath, fileName })
// 브라우저 다운로드 트리거
END ON
ON [닫기] DO
CLOSE PreviousPlanLookupDialog
// 부모 폼 값 변경 없음
END ON
END
```
---
## 4. 영역 A — 검색 필터
구성: **시도 · 구군 · 연도 · 처리시설** (+ 조회).
- 시도·구군: `adstdstr` 콤보 (기존 스펙 유지)
- 연도·처리시설: 각각 `useLvNowDmgMtncGnsvy`를 **별도 호출**. `level`에 따라 반환 계층이 달라진다.
### 4-1. 필드 정의
| 표시명 | 입력 유형 | 필수 | 기본값 | API 파라미터 매핑 | 비고 |
| -------- | --------- | ---- | ------------------------- | ------------------------------------------------ | -------------------------------------------------------------- |
| 시도 | Select | Y | 부모 `sewerBscPlan.sido` | `ctpvNm`(표시명), 코드는 `adstDstrCd` | `useFindSidoCombo` |
| 구군 | Select | Y | 부모 `sewerBscPlan.gugun` | `sggNm`(표시명), 코드는 `adstDstrCd` | `useFindGugunCombo(sidoCd)` |
| 연도 | Select | Y | 없음 | `gnsvyYr` / 조회 시 `targetYear` | `useLvNowDmgMtncGnsvy({ level: "2", ctpvNm, sggNm })` |
| 처리시설 | Select | Y | 없음 | `swtrmtFcltCd` ← 옵션 value, 라벨 `swtrmtFcltNm` | `useLvNowDmgMtncGnsvy({ level: "3", ctpvNm, sggNm, gnsvyYr })` |
> 처리시설은 `bns-if` `useFacilityList`를 **사용하지 않는다**.
### 4-2. 버튼
| 버튼명 | 동작 |
| ------ | -------------------------------------------------------- |
| 조회 | 필수값 검증 후 `getNowDmgMtncGnsvy` 호출 → 영역 C·D 갱신 |
| 닫기 | Dialog 종료 (부모 미반영) |
### 4-3. `lvNowDmgMtncGnsvy` level 캐스케이드
| 트리거 | Request `level` | Request 파라미터 | UI에 쓰는 응답 | 옵션 추출 |
| --------- | --------------- | ---------------------------- | --------------- | ------------------------------------------------------------------------------ |
| 시도 선택 | `"1"` | `ctpvNm` | (하위 초기화용) | 연도·처리시설 Select 리셋. 구군은 `findGugunCombo` |
| 구군 선택 | `"2"` | `ctpvNm`, `sggNm` | 연도 Select | 트리에서 `level === 3` 노드의 `gnsvyYr` |
| 연도 선택 | `"3"` | `ctpvNm`, `sggNm`, `gnsvyYr` | 처리시설 Select | 선택 연도(`level === 3`) 노드의 `children[]` → `swtrmtFcltCd` / `swtrmtFcltNm` |
상위 변경 시 하위 선택값·옵션을 RESET한다 (시도→구군·연도·시설 / 구군→연도·시설 / 연도→시설).
---
## 5. 영역 B — 기본계획파일
### 5-1. 트리 (B-1)
| 표시명 | Field | 비고 |
| ------ | --------------- | ------------------------ |
| 분류명 | `clsfSystmNm` | `ClsfSystmResponseDto` |
| 노드키 | `clsfSystmSn` | 선택 시 파일 목록 조회 |
| 상위키 | `upClsfSystmSn` | 클라이언트에서 트리 조립 |
| 정렬 | `sortSeq` | |
| 경로 | `path` | |
- 조회 파라미터: `clsfSystmNm` = 선택 구군명(레거시와 동일). `upClsfSystmSn`은 하위 전개 시 TBD.
- 시도·구군 미선택 시: 「시도와 구군을 선택하세요.」 안내.
### 5-2. 파일 목록 (B-2)
| 표시명 | Field | 비고 |
| ------ | ----- | ------------------------------------------------------------------- |
| 파일명 | TBD | `getFile` Response DTO가 `unknown` — 백엔드 스키마 확정 후 매핑 |
| (기타) | TBD | 레거시: 트리 선택 후 목록 표시, 클릭 시 `fileprcs` → `fileDownload` |
### 5-3. 버튼·상호작용
| 동작 | 설명 |
| -------------- | -------------------------------------------------------------- |
| 트리 노드 클릭 | `getFile({ clsfSystmSn, … })` → B-2 목록 |
| 파일 클릭 | `getFilePrcs(fileSn)` → `fileDownload({ filePath, fileName })` |
| 빈 목록 | 「트리에서 항목을 선택하세요.」 / 「파일이 없습니다.」 |
---
## 6. 영역 C — 총설
### 6-1. 테이블 구조
| 행(구분) | 열 매핑 (당초1~~4 / 기준 / 변경1~~4) |
| ------------ | ------------------------------------------------------------------------------ |
| 계획목표년도 | `planYrDt1planYrDt4` ~~/~~ `planYr` ~~/~~ `planYrDc1planYrDc4` (+ `년` 접미사) |
- 헤더: 당초(1~~4단계) · 기준 · 변경(1~~4단계)
- 데이터 소스: `getNowDmgMtncGnsvy` 응답 배열의 **첫 번째** 요소
- 읽기 전용
---
## 7. 영역 D — 처리구역별 현황
### 7-1. 행 정의 (표시명 → Field 접두사)
각 행은 `Dt1Dt4` ~~/ (접미사 없음=기준) /~~ `Dc1Dc4` 셀로 표시한다. 숫자는 천단위 콤마, 없으면 `-`.
| 표시명 | Field 접두사 | 비고 |
| ------------------------ | -------------------- | ---------------------------------------------- |
| 하수처리인구(인) | `swtrmtPpltnCnt` | |
| 자연적하수처리인구수(인) | `natrSwtrmtPpltnCnt` | |
| 사회적하수처리인구수(인) | `scilSwtrmtPpltnCnt` | |
| 하수처리구역면적 | `swtrmtZoneArea` | |
| 물사용량원단위 | `wtrUsqtyOrgnUnit` | |
| 오수전환율(%) | `drtwSwtcRt` | |
| 변동부하율(%) | `flctnLodRt` | |
| 오수원단위 | `drtwOrgnUnit` | |
| 계획하수량합계(㎥/일) | `planSwageQntSum` | |
| 생활오수(㎥/일) | `lvngDrtwSum` | |
| 지하수(㎥/일) | `ugwtSum` | |
| 공장폐수(㎥/일) | `fctryWwtSum` | |
| 관광오수(㎥/일) | `toursmDrtwSum` | |
| 군부대(㎥/일) | `mtbsSum` | |
| 연계처리수(㎥/일) | `linkPrcsCntSum` | |
| 기타오수(㎥/일) | `etcDrtwSum` | |
| 지하수사용량(㎥/일) | `ugwtUseSum` | |
| 시설용량(㎥/일) | `fcltCpctSum` | |
| 배제방식 | `exclsMethExpln` | 문자열; 단계별 `*Dt*`/`*Dc*` 존재 시 동일 규칙 |
헤더 3행째 연도: 영역 C와 동일하게 `planYrDt*` / `planYr` / `planYrDc*` 표시.
---
## 8. API/DTO 명세
### 8-1. 공통
| 항목 | 값 |
| --------------- | ---------------------------------------------------- |
| DC Base | `/dc-service/api/v1` (`ENDPOINT.DC_SERVICE`) |
| AS adstdstr | `/as-service/api/v1/adstdstr` |
| 클라이언트 모듈 | `@/entities/dc` (`dcService`), `@/entities/adstdstr` |
> 영역 A 처리시설은 `bns-if`를 사용하지 않음.
### 8-2. 시도·구군 콤보 (non-DC)
| 기능 | Method | URL | Request DTO | Response DTO | API |
| -------- | ------ | ----------------------------------- | ------------------------ | -------------------------------- | ---------------------------------------------------------- |
| 시도목록 | GET | `/as-service/api/v1/adstdstr/sido` | — | `CmCdAdstDstrComboResponseDto[]` | `cmCdAdstDstrService.findSidoCombo` / `useFindSidoCombo` |
| 구군목록 | GET | `/as-service/api/v1/adstdstr/gugun` | Query: `sido` (시도코드) | `CmCdAdstDstrComboResponseDto[]` | `cmCdAdstDstrService.findGugunCombo` / `useFindGugunCombo` |
### 8-3. 연도·처리시설 계층 조회 — DC
| 기능 | Method | URL | Request DTO | Response DTO | API |
| --------------- | ------ | -------------------------------------- | ---------------------------- | -------------------------------- | --------------------------------------------------------- |
| 계층형 계획목록 | GET | `/dc-service/api/v1/lvNowDmgMtncGnsvy` | `LvNowDmgMtncGnsvySearchDto` | `LvNowDmgMtncGnsvyResponseDto[]` | `dcService.getLvNowDmgMtncGnsvy` / `useLvNowDmgMtncGnsvy` |
연도 Select·처리시설 Select는 **각각** `useLvNowDmgMtncGnsvy`를 호출한다 (`level`만 다르게).
**Request (**`LvNowDmgMtncGnsvySearchDto`**)**
| 필드 | 시도(`level=1`) | 구군→연도(`level=2`) | 연도→시설(`level=3`) |
| --------- | --------------- | -------------------- | -------------------- |
| `level` | `"1"` | `"2"` | `"3"` |
| `ctpvNm` | 필수 | 필수 | 필수 |
| `sggNm` | — | 필수 | 필수 |
| `gnsvyYr` | — | — | 필수 |
**Response 노드 필드 (**`LvNowDmgMtncGnsvyResponseDto`**, 부분 optional)**
| 필드 | 등장 level | 설명 |
| -------------- | ---------- | ------------------------ |
| `level` | 1~3 | 계층 깊이 |
| `ctpvNm` | 1 | 시도명 |
| `sggNm` | 2 | 구군명 |
| `gnsvyYr` | 3 | 연도 → 연도 Select value |
| `swtrmtFcltCd` | 시설(리프) | 처리시설 코드 |
| `swtrmtFcltNm` | 시설(리프) | 처리시설명 |
| `children` | 1~3 | 하위 노드 배열 |
> **TBD**: 현재 `useLvNowDmgMtncGnsvy` `enabled`가 `level && ctpvNm && sggNm && gnsvyYr`로 고정되어 `level=1`/`2` 호출이 막힘. level별 필수 파라미터만 검사하도록 수정 필요.
>
> 예: `level=1` → `ctpvNm` / `level=2` → `ctpvNm && sggNm` / `level=3` → `ctpvNm && sggNm && gnsvyYr`
### 8-4. 기본계획 수치 조회 — DC
| 기능 | Method | URL | Request DTO | Response DTO | API |
| ---------------- | ------ | ------------------------------------ | -------------------------- | ------------------------------ | ----------------------------------------------------- |
| 현재·단계별 수치 | GET | `/dc-service/api/v1/nowDmgMtncGnsvy` | `NowDmgMtncGnsvySearchDto` | `NowDmgMtncGnsvyResponseDto[]` | `dcService.getNowDmgMtncGnsvy` / `useNowDmgMtncGnsvy` |
**Request (**`NowDmgMtncGnsvySearchDto`**)**
| 필드 | 매핑 |
| -------------- | ----------------------- |
| `swtrmtFcltCd` | 영역 A 처리시설 |
| `ctpvNm` | 영역 A 시도 표시명 |
| `sggNm` | 영역 A 구군 표시명 |
| `targetYear` | 영역 A 연도 (`gnsvyYr`) |
**Response**: 지표 필드 + `Dt1Dt4` ~~/~~ `Dc1Dc4` 접미사 스테이지 필드 (`NowDmgMtncGnsvyResponseDto`). UI는 `data[0]` 사용.
### 8-5. 분류체계 — DC
| 기능 | Method | URL | Request DTO | Response DTO | API |
| ------------ | ------ | ------------------------------ | -------------------- | ------------------------ | ----------------------------------------- |
| 분류트리조회 | GET | `/dc-service/api/v1/clsfSystm` | `ClsfSystmParamsDto` | `ClsfSystmResponseDto[]` | `dcService.getClsfSystm` / `useClsfSystm` |
**Request**: `clsfSystmNm?`, `upClsfSystmSn?`
**Response**: `clsfSystmSn`, `upClsfSystmSn`, `clsfSystmNm`, `sortSeq`, `droppAble`, `path`
> 레거시 경로 `/clsf` → 현재 클라이언트는 `/clsfSystm`.
### 8-6. 파일 목록·처리·다운로드 — DC
| 기능 | Method | URL | Request DTO / Params | Response DTO | API |
| ------------ | ------ | --------------------------------- | ------------------------------------------------------- | ----------------- | -------------------------------------------- |
| 파일목록 | GET | `/dc-service/api/v1/file` | `FileParamsDto` (`sort?`, `clsfSystmSn?`, `vetPrcsYn?`) | `unknown` **TBD** | `dcService.getFile` / `useFile` |
| 파일처리정보 | GET | `/dc-service/api/v1/fileprcs` | Query: `fileSn` | `unknown` **TBD** | `dcService.getFilePrcs` / `useFilePrcs` |
| 파일다운로드 | POST | `/dc-service/api/v1/fileDownload` | `FileDownloadParamsDto` (`filePath?`, `fileName?`) | `unknown` **TBD** | `dcService.fileDownload` / `useFileDownload` |
> **TBD**: 파일 관련 Response 스키마·다운로드 blob/헤더 처리·`fileDownload` POST body vs query 전달 방식은 백엔드/게이트웨이 확인 후 확정.
---
## 9. UI 구현 가이드
### 9-1. 기술 스택·원칙
- UI: `@common/components` — `Dialog`, `Select`, `Button`, `Table`, `Skeleton`
- 상태: Dialog 로컬 state + React Query (`entities/dc`, `adstdstr`)
- 영역 A 연도·처리시설: `useLvNowDmgMtncGnsvy`를 level별 **두 번**(또는 시도 포함 시 세 번) 호출
- 테이블: 읽기 전용 고정 행 → `@tanstack/react-table` / `DataTable` **불필요**
- 트리: jsTree 미사용 — 재귀 들여쓰기 트리
- 번들: `PreviousPlanLookupDialog`는 outline에서 `next/dynamic` 로드 권장
- 부모 폼: **수치 미반영** (조회·다운로드 only)
- 1차 구현 시 DC 미연동이면 `@faker-js/faker` mock + TODO → 이후 `dcService` 훅으로 교체
### 9-2. 앱·파일 배치
| 구분 | 경로 |
| ------------ | ------------------------------------------------------------------------ |
| 진입 버튼 | `pages/sewer-bsc-plan/ui/sections/outline-form-section.tsx` |
| Dialog | `pages/sewer-bsc-plan/ui/previous-plan-lookup-dialog.tsx` |
| 필터 | `pages/sewer-bsc-plan/ui/previous-plan/previous-plan-search-filters.tsx` |
| 파일 | `pages/sewer-bsc-plan/ui/previous-plan/previous-plan-file-section.tsx` |
| 총설 | `pages/sewer-bsc-plan/ui/previous-plan/previous-plan-summary-table.tsx` |
| 처리구역 | `pages/sewer-bsc-plan/ui/previous-plan/previous-plan-area-table.tsx` |
| 셀 매핑 유틸 | `pages/sewer-bsc-plan/lib/previous-plan-table-utils.ts` |
| mock(선택) | `pages/sewer-bsc-plan/lib/previous-plan-mock.ts` |
| DC entity | `entities/dc/api/dc-service.ts`, `queries.ts`, `model/dc-interface.ts` |
| 레거시 참조 | `adssNewOld/.../cmmn/bscPlnPopup.jsp` |
### 9-3. 영역별 UI 매핑
| 영역 | 컴포넌트 | 데이터 |
| ---- | ------------------------------ | -------------------------------------------------------------- |
| A | `previous-plan-search-filters` | adstdstr + `useLvNowDmgMtncGnsvy`(level 1·2·3) |
| B | `previous-plan-file-section` | `useClsfSystm` + `useFile` + `useFilePrcs` + `useFileDownload` |
| C | `previous-plan-summary-table` | `useNowDmgMtncGnsvy` / 조회 결과 state |
| D | `previous-plan-area-table` | 동일 |
### 9-4. 다이얼로그·라우팅
| 항목 | 내용 |
| ------------ | ------------------------------------------------------------------------ |
| 다이얼로그명 | `PreviousPlanLookupDialog` / 타이틀 `기본 계획 찾기` |
| 오픈 | 사업개요 `[이전기본계획조회]` |
| 라우트 | 없음 (모달). 부모: `/sewer-bsc-plan/regist`, `/sewer-bsc-plan/[id]/edit` |
| 선택 반영 | 없음 |
### 9-5. 데이터 흐름 (구현 요약)
```
BEGIN
ON [이전기본계획조회] CLICK DO
OPEN PreviousPlanLookupDialog (sido/gugun 프리필)
END ON
ON 시도 CHANGE DO
CALL findGugunCombo
CALL getLvNowDmgMtncGnsvy(level=1)
RESET 연도·처리시설
END ON
ON 구군 CHANGE DO
CALL getLvNowDmgMtncGnsvy(level=2) // → 연도 옵션
CALL getClsfSystm
RESET 처리시설
END ON
ON 연도 CHANGE DO
CALL getLvNowDmgMtncGnsvy(level=3) // → 처리시설 옵션
END ON
ON [조회] CLICK DO
VALIDATE
CALL getNowDmgMtncGnsvy
DISPLAY 영역 C · 영역 D
END ON
ON 트리 노드 / 파일 CLICK DO
CALL getFile → getFilePrcs → fileDownload // Response TBD
END ON
END
```
---
## 10. 미확정 (TBD) 추적
| ID | 항목 | 비고 |
| --- | -------------------------------------------------------- | -------------------------------------------------------------------- |
| T1 | `useLvNowDmgMtncGnsvy` enabled을 level별 필수값으로 분기 | `1`: ctpvNm / `2`: +sggNm / `3`: +gnsvyYr |
| T2 | `getFile` / `getFilePrcs` / `fileDownload` Response DTO | 현재 `unknown` |
| T3 | 파일 다운로드 UX (blob, Content-Disposition, 게이트웨이) | 레거시 apigateway 직접 호출과 차이 |
| T4 | `clsfSystm`의 `upClsfSystmSn` 점진 로드 여부 | flat 목록 vs 계층 API |
| T5 | 1차 mock vs 즉시 `dcService` 실연동 | 마이그레이션 플랜은 mock 우선, entity는 준비됨 |
| T6 | 부모 폼 수치 반영 | 요구 없음(레거시 동일). 요청 시 별도 스펙 |
| T7 | `level=1` 응답 UI 활용 여부 | 시도 선택 시 호출하나 구군은 adstdstr — 응답은 캐시·검증용일 수 있음 |
+22 -7
View File
@@ -1,8 +1,16 @@
import { NextRequest, NextResponse } from "next/server"; import { NextRequest, NextResponse } from "next/server";
import { AUTH_USER_ID, CLAIM_NAME, PORT, PROXY_HOST, SERVER_API_URL, SITE_ID } from "@/shared/config"; import {
AUTH_USER_ID,
CLAIM_NAME,
PORT,
PROXY_HOST,
SERVER_API_URL,
SITE_ID,
} from "@/shared/config";
const serverApiUrl = process.env.SERVER_API_URL; const serverApiUrl = process.env.SERVER_API_URL;
const dcServiceApiUrl = process.env.DC_SERVICE_API_URL;
// CSP 헤더 설정 함수 (img-src만) // CSP 헤더 설정 함수 (img-src만)
function addSecurityHeaders(response: NextResponse): NextResponse { function addSecurityHeaders(response: NextResponse): NextResponse {
@@ -22,7 +30,15 @@ export function middleware(req: NextRequest) {
if (pathname.startsWith("/server")) { if (pathname.startsWith("/server")) {
const queryParams = searchParams.toString(); const queryParams = searchParams.toString();
const newUrl = `${serverApiUrl}${pathname.replace("/server", "")}${
let serverUrl: string | undefined;
if (pathname.includes("/dc-service")) {
serverUrl = dcServiceApiUrl;
} else {
serverUrl = serverApiUrl;
}
const newUrl = `${serverUrl}${pathname.replace("/server", "")}${
queryParams ? `?${queryParams}` : "" queryParams ? `?${queryParams}` : ""
}`; }`;
@@ -34,7 +50,6 @@ export function middleware(req: NextRequest) {
return addSecurityHeaders(response); return addSecurityHeaders(response);
} }
// tus-js-client PATCH 요청: Location 헤더로 /server 없이 반환된 경로 직접 처리 // tus-js-client PATCH 요청: Location 헤더로 /server 없이 반환된 경로 직접 처리
// (예: /attach-service/api/v1/tus/upload/{id}) // (예: /attach-service/api/v1/tus/upload/{id})
if (pathname.startsWith("/attach-service")) { if (pathname.startsWith("/attach-service")) {
@@ -56,10 +71,10 @@ export function middleware(req: NextRequest) {
return addSecurityHeaders(response); return addSecurityHeaders(response);
} }
// if (pathname === "/") { if (pathname === "/") {
// const response = NextResponse.redirect(new URL("/dashboard", req.url)); const response = NextResponse.redirect(new URL("/dashboard", req.url));
// return addSecurityHeaders(response); return response;
// } }
logMessages.push("\n--------------------------------\n"); logMessages.push("\n--------------------------------\n");
console.log(logMessages.join("\n")); console.log(logMessages.join("\n"));
+44
View File
@@ -1,9 +1,53 @@
import type { NextConfig } from "next"; import type { NextConfig } from "next";
const subDomain = process.env.NEXT_PUBLIC_SUB_DOMAIN || "http://localhost:13000";
const webhwpCtrlBase = (
process.env.NEXT_PUBLIC_WEBHYP_CTRL_URL || "https://hwp.onasys.co.kr/webhwpctrl"
).replace(/\/$/, "");
const webhwpAssetPrefixes = ["js", "hwpsdk", "dist", "commonFrame", "wasm"] as const;
const nextConfig: NextConfig = { const nextConfig: NextConfig = {
output: "standalone", output: "standalone",
reactStrictMode: true, reactStrictMode: true,
transpilePackages: ["@common/assets"], transpilePackages: ["@common/assets"],
experimental: {
globalNotFound: true,
},
async rewrites() {
return {
beforeFiles: [
{
source: "/_next/static/:path*",
destination: `${subDomain}/_next/static/:path*`,
has: [
{
type: "header",
key: "referer",
value: "(.*)/cms(.*)",
},
],
},
],
afterFiles: [
{
source: "/cms/:path*",
destination: `${subDomain}/cms/:path*`,
},
...webhwpAssetPrefixes.map((prefix) => ({
source: `/webhwpctrl/${prefix}/:path*`,
destination: `${webhwpCtrlBase}/${prefix}/:path*`,
})),
{
source: "/webhwpctrl/file/:path*",
destination: `${webhwpCtrlBase}/file/:path*`,
},
],
fallback: [],
};
},
}; };
export default nextConfig; export default nextConfig;
+1 -1
View File
@@ -32,11 +32,11 @@
"next": "15.5.18", "next": "15.5.18",
"react-hook-form": "^7.54.2", "react-hook-form": "^7.54.2",
"tus-js-client": "^4.3.1", "tus-js-client": "^4.3.1",
"uuid": "^14.0.1",
"zod": "^3.24.2" "zod": "^3.24.2"
}, },
"devDependencies": { "devDependencies": {
"@eslint/eslintrc": "^3", "@eslint/eslintrc": "^3",
"@hookform/devtools": "^4.4.0",
"@types/js-cookie": "^3.0.6", "@types/js-cookie": "^3.0.6",
"eslint": "^9.0.0", "eslint": "^9.0.0",
"eslint-config-next": "15.3.8", "eslint-config-next": "15.3.8",
Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

Before

Width:  |  Height:  |  Size: 6.5 KiB

After

Width:  |  Height:  |  Size: 6.5 KiB

Before

Width:  |  Height:  |  Size: 6.5 KiB

After

Width:  |  Height:  |  Size: 6.5 KiB

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 483 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 483 KiB

+63
View File
@@ -0,0 +1,63 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 538 755">
<defs>
<style>
.cls-1 {
fill: #fff;
}
</style>
</defs>
<!-- Generator: Adobe Illustrator 28.7.9, SVG Export Plug-In . SVG Version: 1.2.0 Build 218) -->
<g>
<g id="Layer_1">
<g id="han" data-label-shift="dx,dy">
<path class="cls-1" d="M159.2,197l-5,5.8,5.1,3.7,13.2,9.7-7.3,3.5-8.2-1.2-9.4,1.4,1.4,4.9-3.5,7.3,3.8,2.6-.9,4.7,8.9-2.3,4.2-3.7,5.2,4.2-6.1,2.3.9,12,3,7-.9,3.3-.5,1.6,5.4,6.8,5.2,13.4h-3.6c0-.1,6.4,3.8,6.4,3.8l4.8,6h1.2l3.6-5.4,3.6-.6,13.8,5.9,19.7-1.1,4.6-6.5,3-5.2,8.2-1.7,11.7-17.8,7.1,1.9,4.9,10,13.2,9.6,3.8,13-1.4,12.2-.5,9.5,1.6,1.1-.6,1.3v1.1c.1,0-2.2,2.4-2.2,2.4l-17.5,7.4,7.6,19.2,16.3,1.1,8.4,10.8,12.6.6,4.2-5.2-4.6-7.9-9.2-6.2,12.1-7.1,6.4,5.2,1.3-5.6-6.6-6.5,17.5-5.8,4.8,4.4,4.8-2.1-4.7-6.5,4.5-8.8,6.3-.4,4.7-3,8,3,24.1,4.5,11.5-7.8-2.8-6.4,6.5-11.7,10.5-5.5,7.6-7.9,10.2-2.3,13.4,4.6,4.4-8,12.4,8.2,3.4-3.1.2-15.2.4-11.3,13.4-3.2,4.6-.4-1.9,9.9,9.8,4.6,1.6,12.8,12.2,9.2,9.8-8.4,15-8.9-3.7-19-21.8-40.6-5.7-10.6,1.2-6.8-4.9-6.1-4-4.9,1.2-5.2-26.3-33.1-5.8-12.1-7.1-14.9-11.9-20.9-2.9-9.4-2.1-6.7-1.8-5.2-8.7-25.5-8.6-14.4-.9-11-5.2-11-3-6.3-6,6.5-5.2,22.8-9.8,11-5.5,2.8-7.4,3.7-17.1.5-12.7-1.6-8.2,2.3-5.2-3.4-3.4-2.2-18.8-2.3-21.8,4.5-19-3.7-3.5,4.7-11.2-7.3-5.2,4.2-6.1-.7-11.5,3.5-14.1,10.8-3.7,10.1-12.9,9.8-4,7.3-8.5,6.8-2.3,1.9-9.1,4.5-4,18.3-9.2,11.5-14.3,1.2-2.1,30,5.6,1.4,3.3,9.2-1.4,2.7.9,2.6,2.9-.7,1.8,3.2-4.5,2.2,1.7,8.6,3.8,6.9,8.9,3.4Z"/>
</g>
<g id="yeongsan" data-label-shift="dx,dy">
<path class="cls-1" d="M57.4,578.4s.2,1.4.2,2.3.7,1.6.7,1.9.5,1.4.7,2.3c.2.9,1.2,1.2,1.2,1.2h0s.5,1.4.5,1.4c0,0-.5.7-.9,1.4-.5.7,1.6.9,1.6.9l2.8-.7s1.4-.7,1.4-1.2,0,0-.2-.7c-.2-.7-.5-.7-.9-1.2-.5-.5.5-.2.5-.2l.5.2,2.6-.5s.9-.5,1.4-.7c.5-.2-.2-.5-.2-.9s-.2-1.2-.2-1.2l.2-.9v-1.2c0-.5.7-.7.9-1.2.2-.5.5-.7.9-1.2.5-.5,0-.7,0-1.6s-.5-.5-.5-.9-.5,0-.5,0l-.9.5-.9.7s-.2.9-.5,1.2c-.2.2-1.4.2-1.4.2,0,0-.2-.5-1.2-.9-.9-.5,0,0-.5,0s-1.2-.2-1.2-.2c0,0-1.2-.7-1.6-.9-.5-.2-.5-.9-.5-.9h-1.2s-1.9,1.4-2.8,1.9c-.9.5,0,1.2,0,1.2Z"/>
<path class="cls-1" d="M45.2,585.7c.2.2,0,.2.7.9.7.7.5,1.6.9,1.6s.2-.5,1.2,0c.9.5,1.4.5,1.9.7.5.2,1.6.9,1.9.9s1.4.7,1.4.9-.2.2,0,.5c.2.2,0,.5.5,1.4.5.9,1.9,2.6,1.9,2.6,0,0-.7.7.5.5,1.2-.2,1.9-.2,1.9-.9s.2-1.2.2-1.4-.2-.2-.5-1.2c-.2-.9-.7-1.6-.7-1.6,0,0,1.2-.2,0-1.2-1.2-.9-.2-1.2-1.2-.9-.9.2-1.9.2-1.9.2-.2-1.2-.2-1.9-.2-1.9l-.5-.5.7-1.4h0c.5,0,.9-.2,1.2-.2s.9-.7.9-.7c0,0,.2-.2,0-.7-.2-.5-.2-.2-.9-.7s-1.6-.7-1.6-.7l-2.1-1.2-1.4-.9c-.7-.2-2.6.9-2.6.9,0,0,.9-.5,0,.7-.9,1.2-2.3,1.9-2.3,1.9,0,0-.2.5,0,.9.2.5,0,1.2.2,1.4Z"/>
<path class="cls-1" d="M28.6,585.5s0,.7,1.2.7h2.1c.5,0,.9.7.9.7v1.9l1.2.7s1.4,0,1.9-.5c.5-.5.5-.7.5-.7l-.5-1.4,2.3-.7s.5-.5.7-1.2c.2-.7-.2-1.2,0-2.3.2-1.2,0-2.8,0-2.8,0,0,.9-1.4.9-2.1s1.9-2.6,1.9-2.8h0v-.7h-1.4c-.2,0-1.7.5-2.1.7-.5.2,0,0-.9,1.2s-.2,1.6-.2,1.6l-1.9.5-1.4.7-.9.2s-1.2-.2-2.1-.2-.5.9-.5.9c0,0-1.2,1.4-1.9,1.6-.7.2-.7.7-.7.7,0,0,.9,2.3.9,2.8v.5Z"/>
<path class="cls-1" d="M41.7,591.4c-.5.2-.7-1.2-.5,0,.2,1.2,1.2,2.1,1.2,2.1l1.4.9s.9-.2,1.4,0c.5.2.9,0,.9,0l1.9.7v.2s.7-.2.5-1.6c-.2-1.4,0-1.4,0-1.4l-.7-.9-2.6-1.4h0s-2.8-.7-3-.5c-.2.2-.5-.2-.5.2s.5,1.4,0,1.6Z"/>
<path class="cls-1" d="M34.2,597.7s0,0,0,0c0,0,0,0,0,0Z"/>
<path class="cls-1" d="M45,598.9l-.7-.7-.5-.9s.2-1.4-.2-1.4-1.9-.5-1.9-.5l-1.4-.7h0s-1.4,0-1.4,0c0,0-.9,0-1.2.7-.2.7-.9.5-.9.9s-2.1,1.2-2.5,1.4c0,0,.2.2.4.5.5.7.5,1.4,1.2,1.4s2.1.2,2.3,0c.2-.2.9-.7,1.2-.5.2.2.5.5.5.9s-.5,1.2-.5,1.6.5,0,0,.5c-.5.5.2,1.4.5,1.6.2.2-.7,2.1-.7,2.1,0,0-.5.5-.5.7s1.4.7,1.6,0c.2-.7.7-1.2.9-1.4.2-.2.9-.5.9-.5,0,0-.5.2.7.2s2.1.2,2.1-.5v-1.9s-.2-1.2,0-1.4c.2-.2,0-2.3,0-2.3Z"/>
<path class="cls-1" d="M18.5,619.7c.7,0,1.9-.9,2.3,0,.5.9,1.9,2.6,1.9,2.6,0,0,.2-.2,1.4,0,1.2.2,1.2.5,2.1,0,.9-.5,1.4-1.4,1.4-1.4l1.2-1.2,3.3-1.4s-.2-.7-.7-2.1c-.5-1.4-.7-2.1-.7-2.1,0,0,1.2.2.5-.9-.7-1.2-.9-1.9-.9-1.9l-.5-1.6h0s-.7-.9-.7-.9l-2.1.2s-.2,1.2,0,1.9c.2.7.2.9,0,1.4-.2.5-.7-.5-1.2,0-.5.5-2.3.2-2.3.2h-1.2l-.9,1.4-1.2.9-2.3.9-2.1,1.4s-.5,0,0,.9c.5.9,2.1,1.6,2.8,1.6Z"/>
<path class="cls-1" d="M33.1,624h-.9l-1.4-.7-2.1.7s-.9,1.2-.9,1.6.7,1.9,1.4,2.3c.7.5,1.6.7,1.9,1.6.2.9.2,1.9.2,1.9,0,0,1.9.7,2.3-.7.5-1.4.2-2.3.7-2.6.5-.2,1.9-1.6,1.9-1.6,0,0-.5-.5.7-.5s1.6-.5,1.6-.7v-1.9l-.9-.9h.9l.7-.9s.2-.7,0-1.2c-.2-.5-.5-1.9-.5-1.9l-2.6.5h0l-1.4,1.4-.5.9s.2.9,0,1.2c-.2.2-1.2,1.4-1.2,1.4Z"/>
<path class="cls-1" d="M65.6,620.2l1.9-.7-2.1-2.6-3.5-1.9h0s-3.3.7-3.3.7l1.6,2.1.9,1.4s-.5.5-.9.9c-.5.5-1.6.7-1.6.7,0,0-.5.5-.5.9s-.9,0-2.1.7c-1.2.7-4.5,1.9-4.5,1.9h-.9c.9,0,1.6,2.6,2.3,2.6h3.1s-.2-.5.9-.7c1.2-.2,2.6-1.2,2.6-1.2,1.4.2,2.3-.7,3,.5.7,1.2,1.4.9,1.6,1.4.2.5,1.2,2.1,1.2,2.1l-.7,2.6s1.2.9,1.6.7c.5-.2,1.9-1.4,1.9-1.4l.9-1.9s-.5-1.4,0-1.9c.5-.5-1.6-1.4-1.6-1.4l-2.8-.7-1.9-2.6v-1.2l2.8-1.2Z"/>
<path class="cls-1" d="M64.2,635.5v-1.6s-3.3,1.4-3.3,1.6.9,1.6.9,1.6h1.6l.7-1.6Z"/>
<path class="cls-1" d="M59.8,639.2c0,1.2.5,2.8,1.2,3,.7.2,1.6,0,1.6,0v-3l-1.6-.9s-1.2-.2-1.2.9Z"/>
<path class="cls-1" d="M38.4,638.3c1.2,0,2.8-4.5,2.8-4.5v-1.4h0s-3.5,0-3.5,0l-2.1,1.4-3.5.7s-.2,1.9,1.4,1.9.7-.1.6-.2c0,0,.3,0,.6.2,1.6.5,3.3.7,3.3,1.2s-.7.7.5.7Z"/>
<path class="cls-1" d="M15.4,630.1h0s0,0,0,0h0Z"/>
<path class="cls-1" d="M7,642.7c.2-.5,1.2-.7,1.2-1.4s-1.6-2.6-.5-3.1c1.2-.5,2.6-.9,2.8-1.2.2-.2,1.2-.7,1.6-.5s.9.9.9.9l1.4-.5.5-.9s.2-.9.5-1.4c.2-.5.7-1.9.7-1.9v-1.2l-.7-1.6c-.3.2-2.6,2.1-2.8,2.3-.2.2-3,1.2-3.3,1.2s-3.1.2-3.1.2l-3.8.5-2.6,1.9,1.9,2.1s-.2,1.9,0,2.1c.2.2-.5,1.2.5,1.6.9.5,1.6.9,1.6.9,0,0,2.8.2,3-.2Z"/>
<path class="cls-1" d="M16.6,649.1c0-.7-1.2-1.6-1.2-1.6l-.7-.7-.9-.9s-.2-1.2-.2-1.4-.7-.9-.7-.9c0,0-2.3.2-2.3.5s-.7.7-.7.7c0,0-.2.7-1.6.7s-2.8-.5-2.6,0c.2.5-.2.9-.2,1.6s-.5,1.9-.5,1.9v1.2s.2,1.2.7,1.4c.5.2,1.2.5,1.4.7.2.2,1.4.9,1.2,1.4-.2.5,2.3.2,2.3.2l.5-.5c.2-.2,1.9-1.2,1.9-1.2,0,0,0-.9.5-.9s3.3-.5,3.3-.5v-1.6Z"/>
<polygon class="cls-1" points="12.9 643.4 12.9 643.4 12.9 643.4 12.9 643.4"/>
<path class="cls-1" d="M27.7,639.9c.5.5,1.4,1.6,2.1,1.9.7.2,1.2.5,1.4,1.2.2.7.2,1.4.5,2.1.2.7,1.9.7,1.9,1.2s.5-.2,1.4,0c.9.2.2-.2,1.6,1.2,1.4,1.4,2.3,2.1,2.6,2.6.2.5.9,0,.9,0l1.2-.9s-.2-1.6,0-1.6.2-2.1.2-2.3-.5-2.3-.5-2.3c0,0-1.2-1.4-1.4-1.4s-2.1-.7-2.1-.7l-1.2-1.2-.9-1.2-1.4-.5h0l-3.7-.7h-2.1l-1.2,1.6s.2.7.7,1.2Z"/>
<path class="cls-1" d="M32.6,663.4c.5.2,2.3.5,2.8.5s1.6-.2,1.9-.5c.2-.2.9-.2,1.6-.2s3.5.2,3.5.2c0,0,.5-.7.2-1.4-.2-.7-.9-1.4-.9-1.4l-.2-.7-.7-.5.7-1.2v-.9s-.7-.5-1.4-.5-1.4.2-1.4-.2-.9-1.9-.9-1.9l-1.4.7h0c-.9.5-1.9.7-2.1.9-.2.2-3.3.2-3.1.7.2.5.7,1.2.9,1.4.2.2.9,1.6.9,1.6,0,0-.5,1.6-.5,1.9s-.5,1.2,0,1.4Z"/>
<path class="cls-1" d="M21.3,670.4c0-.2,0-1.4.5-1.6.5-.2.7-.5.7-.7s-.7-1.2-.7-1.4-1.2-1.2-1.2-1.2l-.5-1.2-.2-1.2-.2-1.9h0s-.9-1.9-1.2-1.9-1.4-.2-1.4,0-.5.9-.5.9v.7c0,.2-.2.7,0,1.2.2.5-.2,1.4,0,1.6.2.2.2.7.5,1.4.2.7.7,1.2.7,1.4s-.2,1.2-.2,1.2c0,0-.5-.7-.5.5s-1.9,1.9-.5,2.1c1.4.2,2.8.5,3,.5s1.6-.2,1.6-.5Z"/>
<path class="cls-1" d="M27.4,673s-.5-.9,0-1.6c.3-.5.5-.9.7-1.2.2-.5.9-.5.9-.9s.5-2.3,0-2.8c-.5-.5-.9-1.6-1.4-2.3-.5-.7-.2-.9-1.2-1.6-.9-.7-1.9-1.2-1.9-1.2l-1.2.2h0s-.7,2.1-.7,2.3.2,2.3.7,2.6c.5.2,1.6,1.4,1.9,1.9.2.5.2,1.2.2,1.6s-.7,2.1-.9,2.1,0,0-1.2.5c-1.2.5-2.6.2-3,.9-.5.7-1.2,1.4-1.2,1.4v1.4c0,.2-.2.7.5.7s2.3-.2,2.8-.5c.5-.2,1.4-.7,1.4-.7l2.3-2.1,1.2-.7Z"/>
<path class="cls-1" d="M63.7,683.1c-.2-.5-1.4-2.1-1.4-2.1l-.5-.9s0-1.6-.9-1.9c-.9-.2-3-.2-3-.2l-1.6-2.3s-1.4-2.6-1.6-2.6-1.4,0-2.1-.2c-.7-.2-2.6-1.2-2.6-1.2l-.9.7c.2.7,1.6,2.8,1.6,2.8l1.2,2.3-.5,1.9s-1.9.5-2.3.5h-3s-.5-.7-.7.7c-.2,1.4-2.6,2.6-2.6,2.6h-3s-.9,1.6-.9,2.1-6.1,3.1-6.1,3.1l-1.9,2.6-1.9.5s-.5.7-.5.9-1.2,1.6-1.2,1.6h-.7s-2.3.5-2.3.9.5,1.6.5,2.1v1.6s0,1.6.2,1.6,1.4.7,1.4.7l.7,1.2s.5.2.5.5.7,1.2.7,1.2l1.2,1.2s-.5,1.6,0,2.1c.5.5.9.5,1.4.7.5.2,4.2.7,4.2.7,0,0,1.9.2,2.1.5.2.2,1.4.5,2.1,0,.7-.5,7-1.6,7-1.6,0,0-.7-.9,0-1.2.7-.2,1.9-.5,3-1.4,1.2-.9,3.7-1.2,3.7-1.2,0,0-.5-1.2.2-1.6.7-.5,2.6-2.1,2.8-2.1s2.1,1.4,2.1,1.4c0,0-.7.7.5.7s1.4-.5,1.9-2.1c.5-1.6,1.9-2.3,1.9-2.3l.5-.7s-.5-.5,0-1.6c.5-1.2.9-2.6.9-2.6v-3.5s-.5,0,.5-.5c.9-.5,2.1-2.3,1.4-3.1-.7-.7-1.6-1.9-1.9-2.3Z"/>
<polygon class="cls-1" points="49 672.3 49 672.3 49 672.3 49 672.3"/>
<path class="cls-1" d="M93.5,737.5c-2.3-1.2-4.2-2.3-4.2-2.3h-1.9l-1.2,3.5s1.4,0,1.2.9c-.2.9-1.6.5-1.2,1.6.5,1.2-.5.7,1.6,1.6,2.1.9,2.8,1.9,3.7.9.9-.9,1.9-2.6,1.9-2.6h0l.7-1.2s1.6-1.4-.7-2.6Z"/>
<path class="cls-1" d="M86.2,745l-2.8-1.9s-2.8,2.6-2.8,3.1-2.1,3.3-1.6,4,1.2,1.6,2.8,1.6,3.3-1.6,5.4-2.6c2.1-.9,2.8-2.3,3.7-1.9.9.5,2.1-.5,2.1-.5h0l-1.9-1.2-4.9-.7Z"/>
<path class="cls-1" d="M101.7,742h0l-3.5,1.2s-.9,1.2-.9,1.6.5,2.6.5,2.6c0,0-.5.5-.5,1.6v2.3s1.2,3.1,2.1,3.5c.9.5,2.6-.5,3.1-1.9.5-1.4-.9-5.6-.9-5.6,0,0-2.8-1.2-2.1-1.4.7-.2,2.8-.5,3.1-.5s.9-.7.9-.7l-.7-2.8h-.9Z"/>
<path class="cls-1" d="M115.1,720.4l-1.2-1.6-.5-1.6h0v-2.6l-2.1-3.3s-1.6-1.2-1.9-1.6c-.2-.5-3-2.3-3-2.1s-2.3,2.3-2.3,2.6-3.7,2.8-3.7,2.8c0,0,1.2,4,1.2,4.2s-1.9.2-.5,1.9c1.4,1.6,5.9,2.1,5.9,2.1,0,0,.2-1.4.5.5.2,1.9-.5,2.8-.5,3.1s1.2,1.2,2.6,1.2,3.3-.2,3.5,0c.2.2,3.5.5,3.5.5,0,0,.2,0,0-2.1-.2-2.1-1.4-3.8-1.4-3.8Z"/>
<path class="cls-1" d="M125.6,711.9c.5,0,1.4-1.9,1.4-1.9,0,0-.5-.7.9-.5,1.4.2.9,0,1.6.5.7.5,1.2.5,1.4.5s1.2-.7.5-1.4c-.7-.7-1.4-.9-1.9-1.6-.5-.7-.5-2.6-.5-2.6,0,0-.5-1.4-.7-1.4h-2.8l-.9.9h0c-.7,0-3.5-.7-3.5-.7l-.5,2.8-2.1,1.4s-.9.5-.5,1.2c.5.7.9,1.2.7,1.6-.2.5-1.6,0-1.4.7.2.7,1.6,1.9,1.6,1.9,0,0-.7,1.2-.7,1.6s.9,1.4,1.4,1.4,1.9-2.1,1.9-2.1c0,0,.5-.9.9-1.2.5-.2,2.1-1.2,2.1-1.2h.9Z"/>
<path class="cls-1" d="M141.5,712.4c0-.2-.9-.5-1.2-.7-.2-.2-1.6-.7-1.6-.7l-3-.9h-1.7s-.7,2.1-.7,2.3-.5,1.2,0,1.4c.5.2,2.1.7,2.1.7v1.2c0,.5-.7.7.5,1.2,1.2.5,4.2-.5,4.2-.5h2.6s.9-1.4.5-1.9c-.5-.5-1.6-1.9-1.6-2.1Z"/>
<path class="cls-1" d="M128.6,725.1s.2-.2.5,0c.2.2,1.4-.5,1.4-.5,0,0-.2-.5.5-.5s1.9.5,1.9.5c0,0,.7-.2.9-.7.2-.5-.2-.9,0-1.4.2-.5,0-1.6,0-1.6l-.7-1.6s-.9-.2-1.4-.2-1.2-.2-1.2-.2l-.5-.2-.9-.7h0s-2.3-.2-2.6.5c-.2.7-.7,1.6-.9,1.9-.2.2-1.9.9-1.9.9l1.4,1.4s.2.7,1.2,1.2c.9.5,2.3,1.4,2.3,1.4Z"/>
<path class="cls-1" d="M121.1,719.9c-.3-.3-.7-.5-1.2-.5h0c-.6,0-1.1.6-1.2,1.2,0,.4.2.8.5,1.2.1.2.3.5.7.7.5.3,1.4.6,1.6.2.2-.2,0-.5-.2-1.6,0-.7,0-.9-.2-1.2Z"/>
<path class="cls-1" d="M137.3,747.6c-.9-.5-1.9-.9-1.9-.9l-.5-1.2.9-1.2.2-2.3-4.2-.5-2.1.5h0s-1.2,2.1-1.4,2.6c-.2.5-2.1,1.9-2.1,2.1s0,.7-.2,1.4c-.2.7-.9,1.9,0,2.3.9.5,2.1.7,2.1.7,0,0,2.6-1.4,2.8-.7.2.7.2,1.2,1.2,1.9.9.7,2.1.9,2.3.9s.2-1.2.9-1.4c.7-.2,2.6-1.9,2.6-1.9,0,0,.2-1.9-.7-2.3Z"/>
<path class="cls-1" d="M267.1,679.6s.5-.5,0-1.6c-.5-1.2-.7-2.1-.7-2.1l-.7-1.2-.5-1.4s-.5-.5-.2-1.2c.2-.7,0-2.6,0-2.6l-.2-.7.7-.9h0c.2-.2.5-2.1.5-2.1l-3.3-2.3s-1.4.9-1.2,1.9c.2.9.9,2.1.9,2.6s.2,1.6,0,2.1c-.2.5-.9,1.2-.9,1.4s-.7,1.4-.9,1.9c-.2.5-2.8,1.9-2.8,1.9,0,0-1.9,1.9-1.9,2.1s-1.2.2-.7,1.6c.5,1.4.5,3.3.5,3.3,0,0-1.2.9-.5,1.6.7.7,1.4,1.6,2.8,2.1,1.4.5,4.2,1.6,4.2,1.6l3.1.5s2.3-.9,1.9-1.4c-.5-.5-1.6-1.4-1.6-1.4l-.5-1.2s0-1.2.5-1.9,1.6-2.6,1.6-2.6Z"/>
<polygon class="cls-1" points="279.4 600.4 279.4 586.1 272 579.9 262.7 571.1 261.3 561.5 254.5 559.9 251.7 557.3 244.7 562.9 243.5 566.9 235.1 563.3 234.4 553 235.1 535.7 240.9 533.6 245.9 532.1 247.1 523.8 256.9 510.3 248.3 507 239.9 515.4 232.7 509.4 238.7 493.8 231.6 486.6 229.2 475.8 217.2 472.2 206.4 484.2 205.2 493.8 195.7 486.6 186.1 495 186.1 502.2 176.5 510.6 159.8 515.4 157.4 523.8 140.6 521.4 127.4 517.8 124.5 512.7 119.4 508.3 115.3 512.5 110.2 512.4 96.4 517.8 89.3 530 84.4 535 85.6 538.3 88.3 540.1 87.2 541.1 82.3 539.9 76.8 551.3 72.3 553.6 67.3 564.8 76.6 566.5 73.6 571.4 75.3 574.4 80.6 583.9 85.4 585.9 85.4 589.7 80 596 78.4 589.4 69.9 588.5 68.8 590 71.3 593.4 75.4 595.9 74.3 598.4 69.7 600 68.7 603.3 63 605.4 61.9 611.5 67.8 615.5 70.4 613.1 73.2 605.9 74.3 613.6 75.3 626.5 69.7 634.5 69.7 639.2 68.1 643.4 67.8 644.3 80.4 650.4 92.7 661.4 89.3 661.9 86.5 659.8 85.6 664 79 654.2 69.4 650 67.8 656.3 70.6 661.9 74.3 663.3 77.1 670.4 72.7 669.9 66.1 661.5 62.9 648.1 58.5 642.1 51.9 654.2 55.1 668.3 59.6 674.6 61.9 673.9 65.8 676.6 67.1 680.5 69.9 680.2 72.7 677.9 79 686.6 77.7 698.2 82.8 699.7 79.7 706.7 76.6 709.9 77.4 712.1 82.5 712.8 82.7 721.2 93.1 719.9 97.3 713.3 97.1 709.8 112.1 701.6 110.7 695.9 117.2 694.3 120.8 688.2 120.2 673.4 122.8 671.1 124.7 675.1 124.5 684.5 122.4 694.3 125.7 699.9 130.4 702.1 134.8 704.2 136.9 700.4 143.5 702.3 145.8 697.6 149.1 698.3 148.3 688.6 152.1 687 152.9 684.2 151.7 682 155 673 162 671.1 167.4 666.2 174.9 662.4 179.6 664 183.8 662.6 187.1 655.8 192.4 654.7 194.6 657.2 202.1 657.2 202.7 663.8 200.6 669.2 196 669.8 194.8 663.8 189.6 664 187.5 671.6 189.1 675.6 185.9 681.9 181.9 681.2 183.1 676 181.4 674.3 176 692.2 189.2 694.5 191.7 701.4 195.5 706.3 203.9 704.4 207.4 697.8 213.3 694.1 214.2 689.9 209.3 687.1 210 684 218 684.7 221 686.6 224.2 683.9 222.7 674.6 209.2 661.4 209.5 657 212.8 653.7 210.5 650.9 215.4 646.2 211.9 643.7 213.5 642.5 216.5 642.5 222 642.5 227.6 640.4 228.1 635.9 230.9 638 231.2 641.7 231.8 647.4 236.2 650.7 238.4 657 234.6 663.8 232.6 672.3 234.9 676.9 238.7 676.9 243.5 679.8 244.5 675.1 243.3 671.1 245.9 662.9 249.9 659.4 255 664.1 261.6 661 262.3 655.8 265.1 651.9 267 645.5 264.6 642 261.1 642.5 257.9 642 248 646 246.3 642 239.9 634.6 244.2 626.8 246.3 631 251.6 632.8 262.5 623.5 278.8 628.6 285.4 617.2 285.4 607.6 279.4 600.4"/>
</g>
<g id="nakdong" data-label-shift="dx,dy">
<path class="cls-1" d="M405.4,649.3l-2.1-.2-1.6,1.2-.9,1.6-1.2.2-.9-.5v-1.2l.2-.9,1.4-.9.9-.7v-.9l.9-.9.7-1.2-.2-1.6-.2-1.4.5-3v-1.9l-1.6-1.6-.9-.9.2-1.9.7-2.8h0c0,0,1.6-.5,1.6-.5,0,0,1.2-.9,1.2-1.2s.2-.7.2-.7l-.2-.9-1.4-.9-1.9-.7-1.6.2-1.2.5-.9,1.2-.7,1.2-.2,1.2.5,1.9v.9l-.5.7-.7.2-1.2.2-.9.5-.5.5-.2,1.2-.2.5h-1.4l-1.4.5s-.9.2-1.2.2-.7.7-.7.7l-1.2.5h-1.4c-.2,0-.7.2-.7.2l-.5.5v.5l.2.7s.7.7.9.7,0,.7,0,.7l.2.9v.7l.2.9.5.5.7.5s.5.5.7.5.2.7.2.7c0,0,.5.7.5.9v2.1h-.2s-.7-.2-.9-.2-.5-.5-.5-.5l-.9-.7h-.9l-.9-.5-.5-.7-.7-1.2-.7-.5-.5-.2-.5.2-.7.7-.7.2h-.9l-1.4-.9-1.4-.9-1.4-.7h-1.2l-.5.2-.7.9-.5.7-1.2.7s-.2,0-.5.2c-.2.2-.7.5-.7.5l-.5.7-.7,1.2-.5,1.2-.9.9v3.3l1.2,1.4s.7,1.4.7,1.6.2,1.4.2,1.4l.7,1.2.7,1.6,1.6.2,1.4-.9.7-1.2.7-.7h2.6l1.4-1.2.9-1.6.7-.9.7-.5.5.5.5.7v2.6s.5.5.7.5,0,.7,0,.7l-.7,1.2-1.2,1.9-.5.7-.2.5-.2.5-.9.5-1.2.2-1.4.2-.7.5-.5.5-.2.7,1.4.5.5.5v.7l.5.5.7.2h1.2l2.1.2.7.5.2.7-.2.5-.7.5s-.9.7-1.2.7-.5.9-.5.9l-.2.7.2.9v.7l.9.2.9.7,1.2.2v.9l-.2.2-.9.5-1.4.5-.7.2-.5.7v.9l.5.7s.5.5.7.5,1.4.2,1.4.2c0,0,1.4.2,1.6.2s.9-.5.9-.5l1.2-.5,1.2-.7.5-.7v-.7l.2-1.2s.5-.9.5-1.2.9-.5.9-.5l1.4-.2.2-.7-.9-.9-.7-1.4.5-1.6v-1.2l.7-.7,1.6-.2,1.6-.2.7-.9.2-1.2v-2.1l.7-.7,1.9.2,1.6-.2.5,2.1,1.2,1.4,2.1.9,1.6-.7.2-1.2-.2-1.2-1.6-1.2-.7-1.4.2-1.2-1.2-.7-.9-.9.5-1.4h1.9c.2,0,.7-.5.7-.5l.7-.9v-1.6h.9c.2,0,.7-.5.7-.5l.2-1.4s.2-.5.7-.7c.5-.2,0-1.4,0-1.4v-1.4Z"/>
<path class="cls-1" d="M330.3,650.8v-1.2l-.9-.5h0s-4.2-1.2-4.2-.9-2.8,0-2.8,0l-1.4.9s-.2.7-.2.9.2,1.2.2,1.2l.5.2.9.7,2.8.5h1.9l.9-.7.9-.7.9-.2.5-.2Z"/>
<polygon class="cls-1" points="333.8 654.8 332.6 654.1 332.2 652.9 331.7 652.7 331 652.9 329.8 653.9 329.6 654.8 329.1 656 329.1 657.6 329.4 659 329.8 659.7 330.8 659.7 331.9 659.5 332.4 659 333.8 659 335 658.8 335.2 657.8 335.2 656.9 334.7 655.7 333.8 654.8"/>
<polygon class="cls-1" points="308 649.2 307.6 648.4 307.6 647.5 308 646.3 308.7 645.2 308.7 644.2 308.3 644 307.3 643.5 306.4 642.8 305.5 641.9 304.7 641.2 303.1 641.4 302.2 642.8 302.2 644.7 301.2 645.2 300.8 644.2 300.5 642.6 301 641.4 301.7 639.8 302.4 638.4 303.6 637.2 303.6 636.5 303.3 635.8 302.6 635.8 301 636 299.8 637 298.4 638.1 298.2 639.5 297.3 640 295.6 640.9 294.7 642.4 293.5 643.5 293.3 644.9 293.3 645.9 293.5 647 294.2 648.2 295.4 648.4 297 649.2 299.4 649.2 301.2 648.7 303.1 649.2 304.1 649.9 306.2 651 307.8 652 308.3 651.7 309.4 651.7 309.7 651 309.2 650.3 308 649.2"/>
<path class="cls-1" d="M305.2,653.4l-1.2-.5-.9-.7-.7-.2h-2.1l-1.6-.2-.9-.7h-1.6l-1.2,1.2-1.6,1.2-.9.7v.7l-1.4-.2-.5-1.2-.9-1.4-.2-.7-.5-.9-1.2-.5-1.2-.5-.5-.5s-.2-.5-.2-.7-.2-1.2-.2-1.2v-.5c0-.2-.7-.7-.7-.7l-.2-.7-.5-1.2-.2-1.4.7-.9.2-.9,1.6-.2.7-.5.5-.2.2-.7.2-1.2.5-.5.7-.9v-.7l-.2-1.2-.5-1.4.2-1.4-2.1-1.4h-3.1l-1.9.2-1.4,1.2-.9,1.4-.5,1.2-.5,1.4v.7l-1.6.5-.9.5-.7.9s.2.5,0,.7c-.2.2-1.2.9-1.2.9v1.4l-.5,1.2-.7.5-.9.5-.2.9v1.4l.2,1.2-.2,1.4.5,1.6.5,1.9,1.2,1.9h0v1.6l.2,1.4.7.9.9.7.2.7-.2.7-.9.9-.9.7-.5.7-.2.7,1.2.7.7.5,1.4.7v.7l.2,1.2.5.9.9.5.9-.2h1.9l1.2-.2.9-.5.9-.9v-1.4l-.5-.9-.2-1.4.2-.7.7-.5,1.9-.2,1.2-.9h2.3l.5.9v.7s-.5.7-.5.9-.2,1.2-.2,1.2l-.5,1.4v.5l-.2.9.2.5.2.5.2,1.2v1.2l.5.9,2.1.2,2.3.7,1.6-.2,1.2.2.9-.7s.5.5.7.5.5.9.5.9l1.6.7,1.6.2h.7l.2-1.2-.2-1.4-.7-.7-.2-.9v-1.2l.5-.5.9-.9.7-.2.5-.5v-2.3s-.5-2.1-.5-2.3v-.9l.2-1.2.5-1.4s-.2-.9,0-.9.7-.7.7-.7l.7-.5.2-.9-.2-.9-1.2-.5Z"/>
<polygon class="cls-1" points="533.7 451.9 529.9 457.3 516.8 465.7 509.4 457.5 516.6 447.7 513.8 417.3 513.1 408.8 521.3 392.4 522.9 355.1 523.6 353.3 531.1 333.9 526.2 315.4 526.2 282.1 519.8 263.8 504.8 272.7 495 281.2 482.8 272 481.1 259.2 471.4 254.5 473.3 244.6 468.7 245 455.3 248.2 454.9 259.5 454.7 274.8 451.4 277.9 438.9 269.7 434.5 277.6 421.1 273 410.9 275.3 403.3 283.3 392.8 288.8 386.2 300.5 389 306.9 377.6 314.7 353.5 310.3 345.4 307.2 340.7 310.3 334.5 310.7 330 319.5 334.6 326 329.8 328.1 325 323.7 307.5 329.5 314.2 336 312.9 341.5 306.5 336.4 294.4 343.5 303.6 349.6 308.1 357.5 304 362.7 314.1 371.6 316.5 383.6 314.1 388.4 326.1 392 323.3 402.6 325.2 414.4 315.5 413.8 315 423.6 307.5 439.1 298 443 298.5 447.6 301.2 454.4 296.8 462.5 286.2 472.3 276.7 473.4 269.4 480.7 267.1 483.1 264.5 485.8 256.9 505.2 256.9 510.3 247.1 523.8 245.9 532.1 240.9 533.6 235.1 535.7 234.4 553 235.1 563.3 243.5 566.9 244.7 562.9 251.7 557.3 254.5 559.9 261.3 561.5 262.7 571.1 272 579.9 279.4 586.1 279.4 600.4 285.4 607.6 285.4 617.2 278.8 628.6 282.6 628.4 288.8 622.6 289.3 620.3 291.9 621.5 292.3 624.3 296.3 623 298.7 624.3 301.6 622.6 298.9 615.9 301.2 614.9 303.4 615.5 304.7 611.2 309.9 609.7 307.1 614.2 306.9 625.2 305.5 632.3 314.8 637.2 317 641.5 318.3 642.1 322.4 640.3 325.1 641.7 326.8 641.4 329.4 634.9 335.5 635.5 338.5 640.5 342.8 638.4 346.2 633 347.4 636.3 348.6 639.8 343.1 642.3 344.4 645.9 349.4 647.4 351.4 645.9 353.5 647.7 353.3 655.5 347.7 654.1 346.2 655.7 351 659.7 353.7 666.6 356.3 667.8 358.7 665.9 359.9 659.7 358.5 655.6 361.6 654.2 365.6 646.5 363.1 643.5 360.7 647.8 358.5 646.8 361.3 632.3 361.1 630.7 361 629.5 368.3 628 371.4 624.3 365.9 619.1 357.2 620.8 355.4 623.6 351.9 624.1 354.8 618.5 361.5 616.3 367.3 614.4 366.1 610.6 379 609.8 381.7 611.6 383.5 619.4 389.9 616.8 383.5 596.4 387.2 591.5 388.9 592.4 387.7 599.2 390.1 604.4 394.7 605 398 608.8 399.5 604.8 401.5 606.5 400.5 612.2 406.2 614.7 414 613.3 414.9 615.6 420.1 616.2 424.6 617.3 428.8 614.1 432.5 616.6 438.5 610.1 436.6 622.3 438 626.7 441.3 625.8 442.5 620.6 445.7 622.8 453.5 612.9 457 616.4 461.6 616.6 461.7 613.8 459.8 610.5 463.1 606.6 469.9 608.2 480.9 594.4 480.7 591.3 485.5 581.8 491 579.8 494.6 576.1 498.5 573.7 497.1 571.8 499.6 567.6 499.9 561.3 498 558.2 504.1 554 504 546.1 505.5 548.8 506.9 554.5 511.6 551.2 514.6 544.4 514.4 537.4 515.8 532.7 513.9 528 514 525.4 524.7 493.4 527.1 486.2 527.8 477.8 535.1 466.5 538 455.6 533.7 451.9"/>
</g>
<g id="geum" data-label-shift="dx,dy">
<path class="cls-1" d="M298,443l9.5-3.9,7.5-15.6.5-9.8,9.7.6-1.9-11.8,2.8-10.6-12-3.6,2.4-4.8-2.4-12-10.1-8.9-12.6-.6-8.4-10.8-16.3-1.1-7.6-19.2,17.5-7.4,2.3-2.4v-1.1c-.1,0,.4-1.3.4-1.3l-1.6-1.1.5-9.5,1.4-12.2-3.8-13-13.2-9.6-4.9-10-7.1-1.9-11.7,17.8-8.2,1.7-3,5.2-4.6,6.5-19.7,1.1-13.8-5.9-3.6.6-3.6,5.4h-1.2l-4.8-6-6.4-3.9-5-.2-9.1-18.5-5.4,3-2.4-5.7-11.4-2.8-11.7-9.2h-3.9c0,.1-3.4,4.8-3.4,4.8l1,6.2,2.1,5.4-1.2.8-4.4-7.5-6.7-3.2-6.3-.3-5.2,3.3,5.9,3.4-3.6,2.2,5.3,4.4,2.5.8.4,3-6.5,6.1-8.7,11.2-4-1.9,6.4-13.5-1.6-1.9,1.1-7.9-2.1-3-4.7,14.5-3,1.8-3.8-5.7-4.4,2.7.5,8.4-1.5,1.7-3.3-.8-4.2,3.2-5.1,13.8.5,1.5,3.9-.2.2-6.5,5.6,2.5,2.8,5.3-7.4,2.4v3c0,0,5.9,1.2,5.9,1.2l4.3-1.5,2.1-3.3,4-.3-1.4,6,4.1,3.7-2.4,9.4,4.3,6.1,1.1,27,11.6,2.1-6.6-26.7,2.3-4.1-6.3-4.9,1.7-1.9,1-6.9,2-1.5,2.2,9,4.4,3.2,3.1-1.9,3.1-6.1,1.8,1.5-1.9,8.6,2,14.4,1.9,13.7-2.8,6.5,8.2,7.8v2.1l-6.9,2.9,2.9,21.8-1.6,1.9-5.9,6.7,1.3,2.7,3.3-2.7,8.2,2.8,6.2,7.7-1.2,2.8,5.1,13.1,5.6,3.1-19.1.5-1.8,3.3,6.4,3.1-.6,10.7,7.3,2.8,3-3.3,8.9,1.9,9.4-3.4.3,1.4.2.9-3.1,5.4-5.1-.9-9.4,3.5,6.8,6.4,4.3,1.4-.2,5.6-20.6-5.8-6.4,13.7-18.1,10.4-1.9,6.6,5.9,4.7h19.9s3.8,4.2,3.8,4.2l-.5,3.4,3,5.1,13.2,3.6,16.8,2.4,2.4-8.4,16.8-4.8,9.6-8.4v-7.2l9.6-8.4,9.6,7.2,1.2-9.6,10.8-12,12,3.6,2.4,10.8,7.2,7.2-6,15.6,7.2,6,8.4-8.4,8.6,3.3v-5.1s7.6-19.4,7.6-19.4l12.2-12.4,9.6-1.1,10.5-9.8,4.5-8.1-2.7-6.8-.6-4.6Z"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 219 KiB

File diff suppressed because one or more lines are too long
+91
View File
@@ -0,0 +1,91 @@
/**
* Copyright (c) Tiny Technologies, Inc. All rights reserved.
* Licensed under the LGPL or a commercial license.
* For LGPL see License.txt in the project root for license information.
* For commercial licenses see https://www.tiny.cloud/
*/
/**
* Jquery integration plugin.
*
* @class tinymce.core.JqueryIntegration
* @private
*/
!function(){function f(){
// Reference to tinymce needs to be lazily evaluated since tinymce
// might be loaded through the compressor or other means
return d.tinymce}var p,c,u,s=[],d="undefined"!=typeof global?global:window,m=d.jQuery;m.fn.tinymce=function(o){var e,t,i,n,l=this,r="";
// No match then just ignore the call
return l.length?
// Get editor instance
o?(l.css("visibility","hidden"),
// Load TinyMCE on demand, if we need to
d.tinymce||c||!(e=o.script_url)?
// Delay the init call until tinymce is loaded
1===c?s.push(a):a():(c=1,t=e.substring(0,e.lastIndexOf("/")),
// Check if it's a dev/src version they want to load then
// make sure that all plugins, themes etc are loaded in source mode as well
-1!=e.indexOf(".min")&&(r=".min"),
// Setup tinyMCEPreInit object this will later be used by the TinyMCE
// core script to locate other resources like CSS files, dialogs etc
// You can also predefined a tinyMCEPreInit object and then it will use that instead
d.tinymce=d.tinyMCEPreInit||{base:t,suffix:r},
// url contains gzip then we assume it's a compressor
-1!=e.indexOf("gzip")&&(i=o.language||"en",e=e+(/\?/.test(e)?"&":"?")+"js=true&core=true&suffix="+escape(r)+"&themes="+escape(o.theme||"modern")+"&plugins="+escape(o.plugins||"")+"&languages="+(i||""),
// Check if compressor script is already loaded otherwise setup a basic one
d.tinyMCE_GZ||(d.tinyMCE_GZ={start:function(){function n(e){f().ScriptLoader.markDone(f().baseURI.toAbsolute(e))}
// Add core languages
n("langs/"+i+".js"),
// Add themes with languages
n("themes/"+o.theme+"/theme"+r+".js"),n("themes/"+o.theme+"/langs/"+i+".js"),
// Add plugins with languages
m.each(o.plugins.split(","),function(e,t){t&&(n("plugins/"+t+"/plugin"+r+".js"),n("plugins/"+t+"/langs/"+i+".js"))})},end:function(){}})),(n=document.createElement("script")).type="text/javascript",n.onload=n.onreadystatechange=function(e){e=e||window.event,2===c||"load"!=e.type&&!/complete|loaded/.test(n.readyState)||(f().dom.Event.domLoaded=1,c=2,
// Execute callback after mainscript has been loaded and before the initialization occurs
o.script_loaded&&o.script_loaded(),a(),m.each(s,function(e,t){t()}))},n.src=e,document.body.appendChild(n)),l):f()?f().get(l[0].id):null:l;function a(){var a=[],c=0;
// Apply patches to the jQuery object, only once
u||(v(),u=!0),
// Create an editor instance for each matched node
l.each(function(e,t){var n,i=t.id,r=o.oninit;
// Generate unique id for target element if needed
i||(t.id=i=f().DOM.uniqueId()),
// Only init the editor once
f().get(i)||(
// Create editor instance and render it
n=f().createEditor(i,o),a.push(n),n.on("init",function(){var e,t=r;l.css("visibility",""),
// Run this if the oninit setting is defined
// this logic will fire the oninit callback ones each
// matched editor instance is initialized
r&&++c==a.length&&("string"==typeof t&&(e=-1===t.indexOf(".")?null:f().resolve(t.replace(/\.\w+$/,"")),t=f().resolve(t)),
// Call the oninit function with the object
t.apply(e||f(),a))}))}),
// Render the editor instances in a separate loop since we
// need to have the full editors array used in the onInit calls
m.each(a,function(e,t){t.render()})}},
// Add :tinymce pseudo selector this will select elements that has been converted into editor instances
// it's now possible to use things like $('*:tinymce') to get all TinyMCE bound elements.
m.extend(m.expr[":"],{tinymce:function(e){var t;return!!(e.id&&"tinymce"in d&&(t=f().get(e.id))&&t.editorManager===f())}});
// This function patches internal jQuery functions so that if
// you for example remove an div element containing an editor it's
// automatically destroyed by the TinyMCE API
var v=function(){function r(e){
// If the function is remove
"remove"===e&&this.each(function(e,t){var n=u(t);n&&n.remove()}),this.find("span.mceEditor,div.mceEditor").each(function(e,t){var n=f().get(t.id.replace(/_parent$/,""));n&&n.remove()})}function o(i){var e,t=this;
// Handle set value
/*jshint eqnull:true */if(null!=i)r.call(t),
// Saves the contents before get/set value of textarea/div
t.each(function(e,t){var n;(n=f().get(t.id))&&n.setContent(i)});else if(0<t.length&&(e=f().get(t[0].id)))return e.getContent()}function l(e){return e&&e.length&&d.tinymce&&e.is(":tinymce")}
// Removes any child editor instances by looking for editor wrapper elements
var u=function(e){var t=null;return e&&e.id&&d.tinymce?f().get(e.id):t},s={};
// Loads or saves contents from/to textarea if the value
// argument is defined it will set the TinyMCE internal contents
// Patch some setter/getter functions these will
// now be able to set/get the contents of editor instances for
// example $('#editorid').html('Content'); will update the TinyMCE iframe instance
m.each(["text","html","val"],function(e,t){var a=s[t]=m.fn[t],c="text"===t;m.fn[t]=function(e){var t=this;if(!l(t))return a.apply(t,arguments);if(e!==p)return o.call(t.filter(":tinymce"),e),a.apply(t.not(":tinymce"),arguments),t;// return original set for chaining
var i="",r=arguments;return(c?t:t.eq(0)).each(function(e,t){var n=u(t);i+=n?c?n.getContent().replace(/<(?:"[^"]*"|'[^']*'|[^'">])*>/g,""):n.getContent({save:!0}):a.apply(m(t),r)}),i}}),
// Makes it possible to use $('#id').append("content"); to append contents to the TinyMCE editor iframe
m.each(["append","prepend"],function(e,t){var n=s[t]=m.fn[t],r="prepend"===t;m.fn[t]=function(i){var e=this;return l(e)?i!==p?("string"==typeof i&&e.filter(":tinymce").each(function(e,t){var n=u(t);n&&n.setContent(r?i+n.getContent():n.getContent()+i)}),n.apply(e.not(":tinymce"),arguments),e):void 0:n.apply(e,arguments)}}),
// Makes sure that the editor instance gets properly destroyed when the parent element is removed
m.each(["remove","replaceWith","replaceAll","empty"],function(e,t){var n=s[t]=m.fn[t];m.fn[t]=function(){return r.call(this,t),n.apply(this,arguments)}}),s.attr=m.fn.attr,
// Makes sure that $('#tinymce_id').attr('value') gets the editors current HTML contents
m.fn.attr=function(e,t){var n=this,i=arguments;if(!e||"value"!==e||!l(n))return s.attr.apply(n,i);if(t!==p)return o.call(n.filter(":tinymce"),t),s.attr.apply(n.not(":tinymce"),i),n;// return original set for chaining
var r=n[0],a=u(r);return a?a.getContent({save:!0}):s.attr.apply(m(r),i)}}}();
+3
View File
@@ -0,0 +1,3 @@
This is where language files should be placed.
Please DO NOT translate these directly use this service: https://www.transifex.com/projects/p/tinymce/
File diff suppressed because one or more lines are too long
+504
View File
@@ -0,0 +1,504 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use,
not price. Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
<one line to give the library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!
+9
View File
@@ -0,0 +1,9 @@
/**
* Copyright (c) Tiny Technologies, Inc. All rights reserved.
* Licensed under the LGPL or a commercial license.
* For LGPL see License.txt in the project root for license information.
* For commercial licenses see https://www.tiny.cloud/
*
* Version: 5.10.9 (2023-11-15)
*/
!function(){"use strict";function f(t,e,n){t.execCommand("UL"===e?"InsertUnorderedList":"InsertOrderedList",!1,!1===n?null:{"list-style-type":n})}function i(t){return function(){return t}}function t(t){return t}function e(){return s}var n=tinymce.util.Tools.resolve("tinymce.PluginManager"),g=tinymce.util.Tools.resolve("tinymce.util.Tools"),u=i(!1),l=i(!0),s={fold:function(t,e){return t()},isSome:u,isNone:l,getOr:t,getOrThunk:r,getOrDie:function(t){throw new Error(t||"error: getOrDie called on none.")},getOrNull:i(null),getOrUndefined:i(void 0),or:t,orThunk:r,map:e,each:function(){},bind:e,exists:u,forall:l,filter:function(){return s},toArray:function(){return[]},toString:i("none()")};function r(t){return t()}function d(t){return t&&/^(TH|TD)$/.test(t.nodeName)}function m(c,a){return function(s){function t(t){var e,n,r,o,i,u,l;s.setActive((e=c,r=a,i=-1!==(o=function(t,e){for(var n=0;n<t.length;n++)if(e(t[n]))return n;return-1}((n=t).parents,d))?n.parents.slice(0,o):n.parents,0<(u=g.grep(i,(l=e,function(t){return t&&/^(OL|UL|DL)$/.test(t.nodeName)&&(e=t,l.$.contains(l.getBody(),e));var e}))).length&&u[0].nodeName===r))}return c.on("NodeChange",t),function(){return c.off("NodeChange",t)}}}function c(t,e,n,r,o,i){var u,l,s,c,a,d;1<i.length?(c=r,a=o,d=i,(s=t).ui.registry.addSplitButton(e,{tooltip:n,icon:"OL"===a?"ordered-list":"unordered-list",presets:"listpreview",columns:3,fetch:function(t){t(g.map(d,function(t){return{type:"choiceitem",value:"default"===t?"":t,icon:"list-"+("OL"===a?"num":"bull")+"-"+("disc"===t||"decimal"===t?"default":t),text:t.replace(/\-/g," ").replace(/\b\w/g,function(t){return t.toUpperCase()})}}))},onAction:function(){return s.execCommand(c)},onItemAction:function(t,e){f(s,a,e)},select:function(e){var t,n=(t=s).dom.getParent(t.selection.getNode(),"ol,ul"),r=t.dom.getStyle(n,"listStyleType");return p(r).map(function(t){return e===t}).getOr(!1)},onSetup:m(s,a)})):(l=r,(u=t).ui.registry.addToggleButton(e,{active:!1,tooltip:n,icon:"OL"===o?"ordered-list":"unordered-list",onSetup:m(u,o),onAction:function(){return u.execCommand(l)}}))}var a=function(n){function t(){return o}function e(t){return t(n)}var r=i(n),o={fold:function(t,e){return e(n)},isSome:l,isNone:u,getOr:r,getOrThunk:r,getOrDie:r,getOrNull:r,getOrUndefined:r,or:t,orThunk:t,map:function(t){return a(t(n))},each:function(t){t(n)},bind:e,exists:e,forall:e,filter:function(t){return t(n)?o:s},toArray:function(){return[n]},toString:function(){return"some("+n+")"}};return o},p=function(t){return null==t?s:a(t)};n.add("advlist",function(t){var n,e,r,o;t.hasPlugin("lists")?(c(e=t,"numlist","Numbered list","InsertOrderedList","OL",(r=e.getParam("advlist_number_styles","default,lower-alpha,lower-greek,lower-roman,upper-alpha,upper-roman"))?r.split(/[ ,]/):[]),c(e,"bullist","Bullet list","InsertUnorderedList","UL",(o=e.getParam("advlist_bullet_styles","default,circle,square"))?o.split(/[ ,]/):[]),(n=t).addCommand("ApplyUnorderedListStyle",function(t,e){f(n,"UL",e["list-style-type"])}),n.addCommand("ApplyOrderedListStyle",function(t,e){f(n,"OL",e["list-style-type"])})):console.error("Please use the Lists plugin together with the Advanced List plugin.")})}();
+9
View File
@@ -0,0 +1,9 @@
/**
* Copyright (c) Tiny Technologies, Inc. All rights reserved.
* Licensed under the LGPL or a commercial license.
* For LGPL see License.txt in the project root for license information.
* For commercial licenses see https://www.tiny.cloud/
*
* Version: 5.10.9 (2023-11-15)
*/
!function(){"use strict";function a(e){return e.getAttribute("id")||e.getAttribute("name")||""}function c(e){return e&&"a"===e.nodeName.toLowerCase()&&!e.getAttribute("href")&&""!==a(e)}function d(e){return e.dom.getParent(e.selection.getStart(),l)}function r(e,t){var o,a,n,r,i,l=d(e);l?(n=e,r=t,(i=l).removeAttribute("name"),i.id=r,n.addVisual(),n.undoManager.add()):(a=t,(o=e).undoManager.transact(function(){var e,n;o.getParam("allow_html_in_named_anchor",!1,"boolean")||o.selection.collapse(!0),o.selection.isCollapsed()?o.insertContent(o.dom.createHTML("a",{id:a})):(n=(e=o).dom,u(n).walk(e.selection.getRng(),function(e){s.each(e,function(e){var t;c(t=e)&&!t.firstChild&&n.remove(e,!1)})}),o.formatter.remove("namedAnchor",null,null,!0),o.formatter.apply("namedAnchor",{value:a}),o.addVisual())})),e.focus()}function i(r){return function(e){for(var t,n=0;n<e.length;n++){var o=e[n],a=void 0;!(a=t=o)||a.attr("href")||!a.attr("id")&&!a.attr("name")||t.firstChild||o.attr("contenteditable",r)}}}var e=tinymce.util.Tools.resolve("tinymce.PluginManager"),u=tinymce.util.Tools.resolve("tinymce.dom.RangeUtils"),s=tinymce.util.Tools.resolve("tinymce.util.Tools"),l="a:not([href])";e.add("anchor",function(e){var t,n,o;(t=e).on("PreInit",function(){t.parser.addNodeFilter("a",i("false")),t.serializer.addNodeFilter("a",i(null))}),(n=e).addCommand("mceAnchor",function(){var o,e,t;t=(e=d(o=n))?a(e):"",o.windowManager.open({title:"Anchor",size:"normal",body:{type:"panel",items:[{name:"id",type:"input",label:"ID",placeholder:"example"}]},buttons:[{type:"cancel",name:"cancel",text:"Cancel"},{type:"submit",name:"save",text:"Save",primary:!0}],initialData:{id:t},onSubmit:function(e){var t=o,n=e.getData().id;(/^[A-Za-z][A-Za-z0-9\-:._]*$/.test(n)?(r(t,n),0):(t.windowManager.alert("Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores."),1))||e.close()}})}),(o=e).ui.registry.addToggleButton("anchor",{icon:"bookmark",tooltip:"Anchor",onAction:function(){return o.execCommand("mceAnchor")},onSetup:function(e){return o.selection.selectorChangedWithUnbind("a:not([href])",e.setActive).unbind}}),o.ui.registry.addMenuItem("anchor",{icon:"bookmark",text:"Anchor...",onAction:function(){return o.execCommand("mceAnchor")}}),e.on("PreInit",function(){e.formatter.register("namedAnchor",{inline:"a",selector:l,remove:"all",split:!0,deep:!0,attributes:{id:"%value"},onmatch:c})})})}();
+9
View File
@@ -0,0 +1,9 @@
/**
* Copyright (c) Tiny Technologies, Inc. All rights reserved.
* Licensed under the LGPL or a commercial license.
* For LGPL see License.txt in the project root for license information.
* For commercial licenses see https://www.tiny.cloud/
*
* Version: 5.10.9 (2023-11-15)
*/
!function(){"use strict";function k(e){return/^[(\[{ \u00a0]$/.test(e)}function w(e){return 3===e.nodeType}function i(e){return 1===e.nodeType}function o(e,t){var n;return t<0&&(t=0),!w(e)||(n=e.data.length)<t&&(t=n),t}function y(e,t,n){!i(t)||t.hasChildNodes()?e.setStart(t,o(t,n)):e.setStartBefore(t)}function v(e,t,n){!i(t)||t.hasChildNodes()?e.setEnd(t,o(t,n)):e.setEndAfter(t)}function r(e,t){var n,i,o,r,a,f=e.getParam("autolink_pattern",A),s=e.getParam("default_link_target",!1);if(null===e.dom.getParent(e.selection.getNode(),"a[href]")){var d=e.selection.getRng().cloneRange();if(d.startOffset<5){if(!(r=d.endContainer.previousSibling)){if(!d.endContainer.firstChild||!d.endContainer.firstChild.nextSibling)return;r=d.endContainer.firstChild.nextSibling}if(y(d,r,a=r.length),v(d,r,a),d.endOffset<5)return;n=d.endOffset,i=r}else{if(!w(i=d.endContainer)&&i.firstChild){for(;!w(i)&&i.firstChild;)i=i.firstChild;w(i)&&(y(d,i,0),v(d,i,i.nodeValue.length))}n=1===d.endOffset?2:d.endOffset-1-t}for(var l=n;y(d,i,2<=n?n-2:0),v(d,i,1<=n?n-1:0),--n,!k(d.toString())&&0<=n-2;);k(d.toString())?(y(d,i,n),v(d,i,l),n+=1):(0===d.startOffset?y(d,i,0):y(d,i,n),v(d,i,l)),u=d.toString(),/[?!,.;:]/.test(u.charAt(u.length-1))&&v(d,i,l-1);var u,c,g,h,C=(u=d.toString().trim()).match(f),m=e.getParam("link_default_protocol","http","string");C&&((g=c=C[0]).length>=(h="www.").length&&g.substr(0,0+h.length)===h?c=m+"://"+c:-1===c.indexOf("@")||/^([A-Za-z][A-Za-z\d.+-]*:\/\/)|mailto:/.test(c)||(c="mailto:"+c),o=e.selection.getBookmark(),e.selection.setRng(d),e.execCommand("createlink",!1,c),!1!==s&&e.dom.setAttrib(e.selection.getNode(),"target",s),e.selection.moveToBookmark(o),e.nodeChanged())}}var e=tinymce.util.Tools.resolve("tinymce.PluginManager"),a=tinymce.util.Tools.resolve("tinymce.Env"),A=new RegExp("^"+/(?:[A-Za-z][A-Za-z\d.+-]{0,14}:\/\/(?:[-.~*+=!&;:'%@?^${}(),\w]+@)?|www\.|[-;:&=+$,.\w]+@)[A-Za-z\d-]+(?:\.[A-Za-z\d-]+)*(?::\d+)?(?:\/(?:[-.~*+=!;:'%@$(),\/\w]*[-~*+=%@$()\/\w])?)?(?:\?(?:[-.~*+=!&;:'%@?^${}(),\/\w]+))?(?:#(?:[-.~*+=!&;:'%@?^${}(),\/\w]+))?/g.source+"$","i");e.add("autolink",function(e){var t,n;(t=e).on("keydown",function(e){if(13===e.keyCode)return r(t,-1)}),a.browser.isIE()?t.on("focus",function(){if(!n){n=!0;try{t.execCommand("AutoUrlDetect",!1,!0)}catch(e){}}}):(t.on("keypress",function(e){if(41===e.keyCode||93===e.keyCode||125===e.keyCode)return r(t,-1)}),t.on("keyup",function(e){if(32===e.keyCode)return r(t,0)}))})}();
@@ -0,0 +1,9 @@
/**
* Copyright (c) Tiny Technologies, Inc. All rights reserved.
* Licensed under the LGPL or a commercial license.
* For LGPL see License.txt in the project root for license information.
* For commercial licenses see https://www.tiny.cloud/
*
* Version: 5.10.9 (2023-11-15)
*/
!function(){"use strict";function y(e){return e.getParam("min_height",e.getElement().offsetHeight,"number")}function p(e,t){var n=e.getBody();n&&(n.style.overflowY=t?"":"hidden",t||(n.scrollTop=0))}function v(e,t,n,i){var o=parseInt(e.getStyle(t,n,i),10);return isNaN(o)?0:o}var l=Object.hasOwnProperty,e=tinymce.util.Tools.resolve("tinymce.PluginManager"),b=tinymce.util.Tools.resolve("tinymce.Env"),r=tinymce.util.Tools.resolve("tinymce.util.Delay"),u=function(e,t,n,i,o){r.setEditorTimeout(e,function(){C(e,t),n--?u(e,t,n,i,o):o&&o()},i)},C=function(e,t,n){var i,o,r,s,a,l,u,g,c,m,f,d=e.dom,h=e.getDoc();h&&(e.plugins.fullscreen&&e.plugins.fullscreen.isFullscreen()?p(e,!0):(i=h.documentElement,o=e.getParam("autoresize_bottom_margin",50,"number"),r=y(e),s=v(d,i,"margin-top",!0),a=v(d,i,"margin-bottom",!0),(l=(l=i.offsetHeight+s+a+o)<0?0:l)+(u=e.getContainer().offsetHeight-e.getContentAreaContainer().offsetHeight)>y(e)&&(r=l+u),(g=e.getParam("max_height",0,"number"))&&g<r?(r=g,p(e,!0)):p(e,!1),r!==t.get()&&(c=r-t.get(),d.setStyle(e.getContainer(),"height",r+"px"),t.set(r),e.fire("ResizeEditor"),b.browser.isSafari()&&b.mac&&(m=e.getWin()).scrollTo(m.pageXOffset,m.pageYOffset),!e.hasFocus()||"setcontent"!==(null==(f=n)?void 0:f.type.toLowerCase())||!0!==f.selection&&!0!==f.paste||e.selection.scrollIntoView(),b.webkit&&c<0&&C(e,t,n))))};e.add("autoresize",function(e){var t,n,i,o,r,s,a=e.settings;l.call(a,"resize")||(e.settings.resize=!1),e.inline||(s=0,r=t={get:function(){return s},set:function(e){s=e}},(o=e).addCommand("mceAutoResize",function(){C(o,r)}),i=t,(n=e).on("init",function(){var e=n.getParam("autoresize_overflow_padding",1,"number"),t=n.dom;t.setStyles(n.getDoc().documentElement,{height:"auto"}),t.setStyles(n.getBody(),{paddingLeft:e,paddingRight:e,"min-height":0})}),n.on("NodeChange SetContent keyup FullscreenStateChanged ResizeContent",function(e){C(n,i,e)}),n.getParam("autoresize_on_init",!0,"boolean")&&n.on("init",function(){u(n,i,20,100,function(){u(n,i,5,1e3)})}))})}();
+9
View File
@@ -0,0 +1,9 @@
/**
* Copyright (c) Tiny Technologies, Inc. All rights reserved.
* Licensed under the LGPL or a commercial license.
* For LGPL see License.txt in the project root for license information.
* For commercial licenses see https://www.tiny.cloud/
*
* Version: 5.10.9 (2023-11-15)
*/
!function(){"use strict";function o(t,e){var r=t||e,n=/^(\d+)([ms]?)$/.exec(""+r);return(n[2]?{s:1e3,m:6e4}[n[2]]:1)*parseInt(r,10)}function n(t){var e=document.location;return t.getParam("autosave_prefix","tinymce-autosave-{path}{query}{hash}-{id}-").replace(/{path}/g,e.pathname).replace(/{query}/g,e.search).replace(/{hash}/g,e.hash).replace(/{id}/g,t.id)}function i(t,e){if(a(e))return t.dom.isEmpty(t.getBody());var r=d.trim(e);if(""===r)return!0;var n=(new DOMParser).parseFromString(r,"text/html");return t.dom.isEmpty(n)}function u(t){var e=parseInt(v.getItem(n(t)+"time"),10)||0;return!((new Date).getTime()-e>o(t.getParam("autosave_retention"),"20m")&&(g(t,!1),1))}function s(t){var e=n(t);!i(t)&&t.isDirty()&&(v.setItem(e+"draft",t.getContent({format:"raw",no_events:!0})),v.setItem(e+"time",(new Date).getTime().toString()),t.fire("StoreDraft"))}function f(t){var e=n(t);u(t)&&(t.setContent(v.getItem(e+"draft"),{format:"raw"}),t.fire("RestoreDraft"))}function c(t){t.undoManager.transact(function(){f(t),g(t)}),t.focus()}function m(r){return function(t){function e(){return t.setDisabled(!u(r))}return t.setDisabled(!u(r)),r.on("StoreDraft RestoreDraft RemoveDraft",e),function(){return r.off("StoreDraft RestoreDraft RemoveDraft",e)}}}var t=tinymce.util.Tools.resolve("tinymce.PluginManager"),a=function(t){return void 0===t},l=tinymce.util.Tools.resolve("tinymce.util.Delay"),v=tinymce.util.Tools.resolve("tinymce.util.LocalStorage"),d=tinymce.util.Tools.resolve("tinymce.util.Tools"),g=function(t,e){var r=n(t);v.removeItem(r+"draft"),v.removeItem(r+"time"),!1!==e&&t.fire("RemoveDraft")},y=tinymce.util.Tools.resolve("tinymce.EditorManager");t.add("autosave",function(t){var e,r,n,a;return t.editorManager.on("BeforeUnload",function(t){var e;d.each(y.get(),function(t){t.plugins.autosave&&t.plugins.autosave.storeDraft(),!e&&t.isDirty()&&t.getParam("autosave_ask_before_unload",!0)&&(e=t.translate("You have unsaved changes are you sure you want to navigate away?"))}),e&&(t.preventDefault(),t.returnValue=e)}),n=e=t,a=o(n.getParam("autosave_interval"),"30s"),l.setEditorInterval(n,function(){s(n)},a),e.ui.registry.addButton("restoredraft",{tooltip:"Restore last draft",icon:"restore-draft",onAction:function(){c(e)},onSetup:m(e)}),e.ui.registry.addMenuItem("restoredraft",{text:"Restore last draft",icon:"restore-draft",onAction:function(){c(e)},onSetup:m(e)}),t.on("init",function(){t.getParam("autosave_restore_when_empty",!1)&&t.dom.isEmpty(t.getBody())&&f(t)}),r=t,{hasDraft:function(){return u(r)},storeDraft:function(){return s(r)},restoreDraft:function(){return f(r)},removeDraft:function(t){return g(r,t)},isEmpty:function(t){return i(r,t)}}})}();
+9
View File
@@ -0,0 +1,9 @@
/**
* Copyright (c) Tiny Technologies, Inc. All rights reserved.
* Licensed under the LGPL or a commercial license.
* For LGPL see License.txt in the project root for license information.
* For commercial licenses see https://www.tiny.cloud/
*
* Version: 5.10.9 (2023-11-15)
*/
!function(){"use strict";function i(t){function o(o,e){t=t.replace(o,e)}return t=n.trim(t),o(/\n/gi,"<br />"),o(/\[b\]/gi,"<strong>"),o(/\[\/b\]/gi,"</strong>"),o(/\[i\]/gi,"<em>"),o(/\[\/i\]/gi,"</em>"),o(/\[u\]/gi,"<u>"),o(/\[\/u\]/gi,"</u>"),o(/\[url=([^\]]+)\](.*?)\[\/url\]/gi,'<a href="$1">$2</a>'),o(/\[url\](.*?)\[\/url\]/gi,'<a href="$1">$1</a>'),o(/\[img\](.*?)\[\/img\]/gi,'<img src="$1" />'),o(/\[color=(.*?)\](.*?)\[\/color\]/gi,'<font color="$1">$2</font>'),o(/\[code\](.*?)\[\/code\]/gi,'<span class="codeStyle">$1</span>&nbsp;'),o(/\[quote.*?\](.*?)\[\/quote\]/gi,'<span class="quoteStyle">$1</span>&nbsp;'),t}var o=tinymce.util.Tools.resolve("tinymce.PluginManager"),n=tinymce.util.Tools.resolve("tinymce.util.Tools");o.add("bbcode",function(o){o.on("BeforeSetContent",function(o){o.content=i(o.content)}),o.on("PostProcess",function(o){function e(o,e){t=t.replace(o,e)}var t;o.set&&(o.content=i(o.content)),o.get&&(o.content=(t=o.content,t=n.trim(t),e(/<a.*?href=\"(.*?)\".*?>(.*?)<\/a>/gi,"[url=$1]$2[/url]"),e(/<font.*?color=\"(.*?)\".*?class=\"codeStyle\".*?>(.*?)<\/font>/gi,"[code][color=$1]$2[/color][/code]"),e(/<font.*?color=\"(.*?)\".*?class=\"quoteStyle\".*?>(.*?)<\/font>/gi,"[quote][color=$1]$2[/color][/quote]"),e(/<font.*?class=\"codeStyle\".*?color=\"(.*?)\".*?>(.*?)<\/font>/gi,"[code][color=$1]$2[/color][/code]"),e(/<font.*?class=\"quoteStyle\".*?color=\"(.*?)\".*?>(.*?)<\/font>/gi,"[quote][color=$1]$2[/color][/quote]"),e(/<span style=\"color: ?(.*?);\">(.*?)<\/span>/gi,"[color=$1]$2[/color]"),e(/<font.*?color=\"(.*?)\".*?>(.*?)<\/font>/gi,"[color=$1]$2[/color]"),e(/<span style=\"font-size:(.*?);\">(.*?)<\/span>/gi,"[size=$1]$2[/size]"),e(/<font>(.*?)<\/font>/gi,"$1"),e(/<img.*?src=\"(.*?)\".*?\/>/gi,"[img]$1[/img]"),e(/<span class=\"codeStyle\">(.*?)<\/span>/gi,"[code]$1[/code]"),e(/<span class=\"quoteStyle\">(.*?)<\/span>/gi,"[quote]$1[/quote]"),e(/<strong class=\"codeStyle\">(.*?)<\/strong>/gi,"[code][b]$1[/b][/code]"),e(/<strong class=\"quoteStyle\">(.*?)<\/strong>/gi,"[quote][b]$1[/b][/quote]"),e(/<em class=\"codeStyle\">(.*?)<\/em>/gi,"[code][i]$1[/i][/code]"),e(/<em class=\"quoteStyle\">(.*?)<\/em>/gi,"[quote][i]$1[/i][/quote]"),e(/<u class=\"codeStyle\">(.*?)<\/u>/gi,"[code][u]$1[/u][/code]"),e(/<u class=\"quoteStyle\">(.*?)<\/u>/gi,"[quote][u]$1[/u][/quote]"),e(/<\/(strong|b)>/gi,"[/b]"),e(/<(strong|b)>/gi,"[b]"),e(/<\/(em|i)>/gi,"[/i]"),e(/<(em|i)>/gi,"[i]"),e(/<\/u>/gi,"[/u]"),e(/<span style=\"text-decoration: ?underline;\">(.*?)<\/span>/gi,"[u]$1[/u]"),e(/<u>/gi,"[u]"),e(/<blockquote[^>]*>/gi,"[quote]"),e(/<\/blockquote>/gi,"[/quote]"),e(/<br \/>/gi,"\n"),e(/<br\/>/gi,"\n"),e(/<br>/gi,"\n"),e(/<p>/gi,""),e(/<\/p>/gi,"\n"),e(/&nbsp;|\u00a0/gi," "),e(/&quot;/gi,'"'),e(/&lt;/gi,"<"),e(/&gt;/gi,">"),e(/&amp;/gi,"&"),t))})})}();
File diff suppressed because one or more lines are too long
+9
View File
@@ -0,0 +1,9 @@
/**
* Copyright (c) Tiny Technologies, Inc. All rights reserved.
* Licensed under the LGPL or a commercial license.
* For LGPL see License.txt in the project root for license information.
* For commercial licenses see https://www.tiny.cloud/
*
* Version: 5.10.9 (2023-11-15)
*/
!function(){"use strict";tinymce.util.Tools.resolve("tinymce.PluginManager").add("code",function(e){var t,o;function n(){return o.execCommand("mceCodeEditor")}return(t=e).addCommand("mceCodeEditor",function(){var n,e;e=(n=t).getContent({source_view:!0}),n.windowManager.open({title:"Source Code",size:"large",body:{type:"panel",items:[{type:"textarea",name:"code"}]},buttons:[{type:"cancel",name:"cancel",text:"Cancel"},{type:"submit",name:"save",text:"Save",primary:!0}],initialData:{code:e},onSubmit:function(e){var t=n,o=e.getData().code;t.focus(),t.undoManager.transact(function(){t.setContent(o)}),t.selection.setCursorLocation(),t.nodeChanged(),e.close()}})}),(o=e).ui.registry.addButton("code",{icon:"sourcecode",tooltip:"Source code",onAction:n}),o.ui.registry.addMenuItem("code",{icon:"sourcecode",text:"Source code",onAction:n}),{}})}();
File diff suppressed because one or more lines are too long
@@ -0,0 +1,9 @@
/**
* Copyright (c) Tiny Technologies, Inc. All rights reserved.
* Licensed under the LGPL or a commercial license.
* For LGPL see License.txt in the project root for license information.
* For commercial licenses see https://www.tiny.cloud/
*
* Version: 5.10.9 (2023-11-15)
*/
!function(){"use strict";tinymce.util.Tools.resolve("tinymce.PluginManager").add("colorpicker",function(){})}();
@@ -0,0 +1,9 @@
/**
* Copyright (c) Tiny Technologies, Inc. All rights reserved.
* Licensed under the LGPL or a commercial license.
* For LGPL see License.txt in the project root for license information.
* For commercial licenses see https://www.tiny.cloud/
*
* Version: 5.10.9 (2023-11-15)
*/
!function(){"use strict";tinymce.util.Tools.resolve("tinymce.PluginManager").add("contextmenu",function(){})}();
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long

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