[as] feat: 재원협의 서비스(fnrsCnsltnService) 수정
This commit is contained in:
@@ -17,7 +17,7 @@
|
||||
> - `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-갭) 참고.
|
||||
> **현재 프론트 상태**: 재원협의(`fnrs-cnsltn`)·전자결재(`elapr`)·DC RAG TO(`dcrag-to`)는 존재한다. 실무협의(`pwrk-cnsltn-`\*) 엔티티·화면은 **미구현**. 버튼 매트릭스·상태코드는 **v2.1 잔존**이며 v3.0(`FNRSSTTS`)과 불일치한다. [9절](#9-현재-코드-vs-to-be-갭) 참고.
|
||||
|
||||
---
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
재원협의 4대 주체(기초·광역·환경청·공단)의 **상태 전이·버튼·API**를 `apps/as`에 구현할 때 쓰는 프론트엔드 가이드이다.
|
||||
|
||||
v1.0은 공단 접수자의 **자료검증 → 초안생성 → 웹기안기**만 다뤘다. v2.0은 2026-09-03 완결 프로세스 기준으로 **신청부터 최종종결(`TA`)까지**, 실무협의·AI 3파이프라인·결재선·보완 루프를 포함한다.
|
||||
v1.0은 공단 접수자의 **자료검증 → 초안생성 → 웹기안기**만 다뤘다. v2.0은 2026-09-03 완결 프로세스 기준으로 **신청부터 최종종결(**`TA`**)까지**, 실무협의·AI 3파이프라인·결재선·보완 루프를 포함한다.
|
||||
|
||||
상세 표·시퀀스·DTO는 본문에 넣지 않고 레퍼런스로 분리한다. 구현 중에는 본문의 규칙만 보고, 표가 필요하면 해당 파일을 연다.
|
||||
|
||||
@@ -92,7 +92,7 @@ v1.0은 공단 접수자의 **자료검증 → 초안생성 → 웹기안기**
|
||||
|
||||
## 4. 상태코드 한눈에 보기
|
||||
|
||||
상위코드 **`FNRSSTTS`**. v1.0의 `PWRKSTTS`(`DV`, `AR`, `TR` 등)는 **폐기**한다.
|
||||
상위코드 `FNRSSTTS`. v1.0의 `PWRKSTTS`(`DV`, `AR`, `TR` 등)는 **폐기**한다.
|
||||
|
||||
정방향:
|
||||
|
||||
@@ -122,7 +122,7 @@ SV → ST → LE → FV → ER → AC → TE → TP → RM → RB → RC → RA
|
||||
| `RK` | 조치결과서검토 | 웹기안기·금액검증 재실행·보완. 결재선 2차 |
|
||||
| `RR` | 조치결과서검토 회신 | 환경청 → 광역 `EP` |
|
||||
| `EP` | 검토결과 회신 | 광역 → 기초 `TA` |
|
||||
| `TA` | 검토결과 회신(종료) | 기초 조회만. **고시(`TB`) 아님** |
|
||||
| `TA` | 검토결과 회신(종료) | 기초 조회만. **고시(**`TB`**) 아님** |
|
||||
|
||||
코드 의미·구버전 대조·보완 루프·AI Fallback은 [ref-process-status.md](./ref-process-status.md).
|
||||
|
||||
@@ -130,17 +130,19 @@ SV → ST → LE → FV → ER → AC → TE → TP → RM → RB → RC → RA
|
||||
|
||||
## 5. 프론트가 지켜야 할 핵심 규칙
|
||||
|
||||
1. **상태 SSOT는 `FNRSSTTS`**. 버튼 가시성은 `(dtlInstCd, prgrsSttsComCd, atrzSeComCd)`로만 계산한다. 공단은 결재선 구분(`ATRZ01` 접수자, `ATRZ02` 검토자, `ATRZ03` 승인자, `ATRZ99` 최종승인자)을 추가로 본다.
|
||||
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)` 직행은 폐기.
|
||||
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`)
|
||||
|
||||
- `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으로 처리한다.
|
||||
8. **결재 승인/회신은** `PUT /elapr-prgrs-stts/{id}/process` **하나**. 1차 최종승인자 → `TP`, 2차 → `RR`은 서버가 Feign으로 처리한다.
|
||||
9. **2차 결재선 복사는 백엔드**. `POST /elapr-apln/copy-line`은 `AV→RK` 콜백에서 호출한다. 프론트는 `RK`에서 2차 결재선을 **조회·표시**만 한다.
|
||||
10. **웹기안기는 URL을 직접 Open**. blob Object URL을 `HwpCtrl.Open`에 넘기지 않는다. `orgnlOtptDocCn`은 쓰지 않는다.
|
||||
|
||||
@@ -209,7 +211,7 @@ END
|
||||
프론트가 치는 것만. 콜백·스케줄러는 제외.
|
||||
|
||||
| 주체 | 버튼 | 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` |
|
||||
@@ -224,7 +226,7 @@ END
|
||||
| 공단 검토 | 저장 | `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` |
|
||||
| 기초 | 파일보완완료 / 보완완료 | `SL→ER | TE | RK` | `POST /fnrs-cnsltn-mng/{docNo}/submitsupplement` |
|
||||
| 환경청 | 재원협의 결과 회신 | `TP→RM` | `POST /fnrs-cnsltn-mng/{docNo}/tech-review` |
|
||||
| 광역 | 지자체회신 | `RM→RB` | `updatePrgrsStts` |
|
||||
| 기초 | 저장(조치) / 협의회신 | `RB` / `RB→RC` | **신규** `POST /pwrk-cnsltn-mng/{pwrkDocNo}/submit-actn-result` |
|
||||
@@ -239,125 +241,3 @@ END
|
||||
신규 4개: `request-examine`, `submit-actn-result`, `request-actn-review`, (백엔드) `elapr-apln/copy-line`.
|
||||
|
||||
---
|
||||
|
||||
## 8. 프론트엔드 구현 가이드
|
||||
|
||||
### 8-1. 기술 스택·원칙
|
||||
|
||||
- App Router + FSD (`app/` → `pages/` → `widgets/` → `entities/` → `shared/`)
|
||||
- React Query. ad-hoc `useQuery` + service 직접 호출 금지
|
||||
- `@common/components`, `useDataTable`, `@tanstack/react-table`
|
||||
- `'use client'`는 Web API(다운로드, 폴링, 웹기안기)가 있는 페이지만
|
||||
- 함수명 = Controller public 메서드명 (`delete` → `remove`)
|
||||
- 타입명 = 백엔드 DTO명
|
||||
- Save/Apply Request = zod SSOT. Response는 `interface`
|
||||
|
||||
참조 슬라이스: `entities/fnrs-cnsltn/`, `entities/elapr/`, `entities/dcrag-to/`, `entities/sewer-bsc-plan/`
|
||||
|
||||
### 8-2. 모듈 배치
|
||||
|
||||
| 구분 | 경로 |
|
||||
| ------------- | ------------------------------------------------------------------------------------------------- |
|
||||
| ENDPOINT | `apps/as/src/shared/config/api.ts` |
|
||||
| 재원협의 | `entities/fnrs-cnsltn/` — **확장** (`requestExamine`, `requestActnReview`, `findFileVrfcResults`) |
|
||||
| 실무협의 관리 | `entities/pwrk-cnsltn-mng/` — **신규** |
|
||||
| 생성결과 | `entities/pwrk-cnsltn-doc-crt-rslt/` — **신규** |
|
||||
| DC RAG TO | `entities/dcrag-to/` — 구현됨 |
|
||||
| 전자결재 | `entities/elapr/` — 구현됨. `copyLine`은 서버 전용 |
|
||||
| 재원협의 화면 | `pages/fnrs-cnsltn/` — 버튼 매트릭스 전면 개편 |
|
||||
| 실무협의 화면 | `pages/pwrk-cnsltn/` — 목록/상세 (선택, 공단 보조) |
|
||||
|
||||
파일 트리·Service 시그니처·폴링 훅은 [ref-frontend-entities.md](./ref-frontend-entities.md).
|
||||
|
||||
### 8-3. 버튼 매트릭스 구현
|
||||
|
||||
`pages/fnrs-cnsltn/lib/button-config.ts`를 v3.0 표로 교체한다.
|
||||
|
||||
- 키: `Record<Role, Record<FnrsStts, Action[]>>`
|
||||
- 공단은 `Record<FnrsStts, Record<AtrzSe, Action[]>>` (`ER`/`TE`/`RK`)
|
||||
- AI 상태(`FV`, `AC`, `AV`)는 버튼 대신 **진행 패널** (진행률·경과시간·30분 안내)
|
||||
- `SL`은 기초만 수정 버튼. 복귀 종류(파일/기술/금액)는 `sttsChgHstryList` 직전 코드로 라벨만 분기
|
||||
- `TA`는 조회 전용. **고시(`TB`) 액션 삭제**
|
||||
|
||||
### 8-4. AI 3파이프라인 UX
|
||||
|
||||
| 상태 | 진입 버튼 | 완료 상태 | 30분 Fallback | 수동 버튼 |
|
||||
| ---- | ------------------- | --------- | ----------------------- | ----------- |
|
||||
| `FV` | 기술검토 의뢰 | `ER` | `ER` + [파일검증] | 공단 접수자 |
|
||||
| `AC` | 검토자지정 | `TE` | `TE` + [실무협의서생성] | 공단 검토자 |
|
||||
| `AV` | 조치결과서 검토요청 | `RK` | `RK` + [금액검증] | 공단 검토자 |
|
||||
|
||||
폴링: 초안은 `GET /dcrag-to/documents/draft/status/{crtDmndUuid}` (5~10초). 파일검증·금액검증은 상세 `prgrsSttsComCd` refetch로도 충분하다. `COMPLETED`/`FAILED` 또는 상태 이탈 시 중단하고 상세 invalidate.
|
||||
|
||||
초안 `COMPLETED` 전에는 [실무협의서관리]·[다운로드] 비활성. Fallback으로 `TE`가 되었는데 초안이 없으면 [실무협의서생성]만 활성.
|
||||
|
||||
### 8-5. 보완 팝업 3종
|
||||
|
||||
같은 `reqsupplement` / `submitsupplement`를 쓰고 팝업 UI만 나눈다.
|
||||
|
||||
| 단계 | 팝업 | 요청 주체 | 복귀 |
|
||||
| ---- | ------------------------- | ----------- | ---------------------- |
|
||||
| `ER` | 보완 검토팝업(파일리스트) | 공단 접수자 | `SL→ER` [파일보완완료] |
|
||||
| `TE` | 보완 검토팝업() | 공단 검토자 | `SL→TE` [보완완료] |
|
||||
| `RK` | 보완 검토팝업(금액) | 공단 검토자 | `SL→RK` [보완완료] |
|
||||
|
||||
파일 팝업 데이터: `GET /fnrs-cnsltn-mng/{docNo}/file-vrfc-results`.
|
||||
|
||||
---
|
||||
|
||||
## 9. 현재 코드 vs To-Be 갭
|
||||
|
||||
`pages/fnrs-cnsltn` 기준.
|
||||
|
||||
| 현재 | v3.0 |
|
||||
| ------------------------------------------------------ | --------------------------------------------------------------------- |
|
||||
| 기술검토 의뢰 → `updatePrgrsStts("ER")` | `request-examine` → **`FV`** |
|
||||
| `ROLE_USER01` 결재 상태 `ER`, `TE`만 | `RK` 2차 결재선 추가. `ER`에 파일보완 |
|
||||
| `TA` [고시] → `TB` | `TA`는 종료 조회. `TB` 없음 |
|
||||
| `EP` [인가승인] → `TA` | `EP` [결과회신] → `TA` (광역) |
|
||||
| `TP` 환경청 [검토결과 회신] → `EP` | `TP` → **`RM`** (1차). 최종은 `RR→EP` |
|
||||
| `LE` 결과회신 → `EP` (자체종결만 맞음) | 자체종결 유지. 일반 건은 의뢰 |
|
||||
| 조치결과 `RB/RC/RA/AV` 버튼 없음 | 기초 `submit-actn-result`, 광역 `RC→RA`, 환경청 `request-actn-review` |
|
||||
| `SL` 수정화면 미연결 | 파일/기술/금액 3버튼 |
|
||||
| 초안생성을 `pwrk-cnsltn-mng.save`로 가정 (가이드 v1.0) | **검토자지정**이 1차 트리거 |
|
||||
| `pwrk-cnsltn-*` 엔티티 없음 | 신규 슬라이스 |
|
||||
| `dcrag-to` 구현됨 | 수동 재실행 3종에 연결 |
|
||||
| `elapr-dmnd.save` 구현됨 | `docPrgrsSttsComCd: "AC"` 로 지정 |
|
||||
|
||||
---
|
||||
|
||||
## 10. 구현 순서
|
||||
|
||||
```
|
||||
Task Progress:
|
||||
- [ ] 1. FNRSSTTS 상수·표시명. button-config / floating-action-buttons 를 38행 매트릭스로 교체
|
||||
- [ ] 2. fnrs-cnsltn 엔티티 확장: requestExamine, requestActnReview, findFileVrfcResults
|
||||
(기존 saveApply, updatePrgrsStts, techReview, req/submit supplement 재사용)
|
||||
- [ ] 3. entities/pwrk-cnsltn-mng (findPage, findById, findByUpDocNo, saveApply, submitActnResult)
|
||||
- [ ] 4. entities/pwrk-cnsltn-doc-crt-rslt (download, webhwp open URL, saveMultipart)
|
||||
- [ ] 5. AI 진행 패널: FV / AC / AV (draft status 폴링 + 상세 refetch, 30분 안내)
|
||||
- [ ] 6. 환경청: 기술검토 의뢰(FV), 자체종결(EP), TP→RM, RA→AV, RR→EP
|
||||
- [ ] 7. 공단 접수: 파일검증 결과 배지·파일보완 팝업, 검토자지정(AC)
|
||||
- [ ] 8. 공단 검토 TE/RK: 웹기안기, 수동 초안/금액검증, 보완 팝업, 결재 process
|
||||
- [ ] 9. 기초: SL 보완완료 3분기, RB 조치결과 submit-actn-result, TA 조회
|
||||
- [ ] 10. 광역: ST→LE, RM→RB, RC→RA, EP→TA
|
||||
- [ ] 11. (선택) 실무협의 목록·상세 라우트 /pwrk-cnsltn
|
||||
```
|
||||
|
||||
1~4는 엔티티·버튼 뼈대. 화면 전이는 5부터 역할 레인 단위로 닫는다.
|
||||
|
||||
---
|
||||
|
||||
## 11. 공통코드 (화면 분기용)
|
||||
|
||||
| 상위코드 | 필드 | 값 | 명칭 |
|
||||
| ----------------------- | ---------------- | ----------------------------------- | ------------------------------ |
|
||||
| `ASDOCSE` | `upDocSeComCd` | `FNRS` / `PWRK` | 재원협의 / 실무협의 |
|
||||
| `FNRSSTTS` | `prgrsSttsComCd` | 18종 | [4절](#4-상태코드-한눈에-보기) |
|
||||
| `AICRTSTTS` / `CRTSTTS` | `crtSttsComCd` | `R` `I` `C` `F` | 생성요청/중/완료/실패 |
|
||||
| (검증) | `vrfcSttsComCd` | `PASS` / `FAIL` / `WARN` | 파일별 검증 |
|
||||
| 결재선 구분 | `atrzSeComCd` | `ATRZ01` `ATRZ02` `ATRZ03` `ATRZ99` | 접수/검토/승인/최종 |
|
||||
|
||||
표시명은 응답 `*Nm` / `*CdNm`. 분기는 코드값.
|
||||
|
||||
초안 직후 `crtSttsComCd = R`, 폴링 `COMPLETED` 이후 `C`. 문서 상태는 `AC → TE`.
|
||||
|
||||
@@ -5,6 +5,7 @@ import type { PageResponse } from "@/shared/model";
|
||||
import type {
|
||||
DocPrgrsUpdateRequestDto,
|
||||
FnrsCnsltnApplySaveRequestDto,
|
||||
FnrsCnsltnFileVrfcRsltResponseDto,
|
||||
FnrsCnsltnMngDetResponseDto,
|
||||
FnrsCnsltnMngResponseDto,
|
||||
FnrsCnsltnMngSaveRequestDto,
|
||||
@@ -12,6 +13,10 @@ import type {
|
||||
FnrsCnsltnMngService,
|
||||
FnrsCnsltnMngUpdateRequestDto,
|
||||
} from "../model/fnrs-cnsltn-interface";
|
||||
import type {
|
||||
EnvoActnReviewReqDto,
|
||||
FnrsExamineRequestDto,
|
||||
} from "../model/fnrs-cnsltn-payload-schema";
|
||||
|
||||
const BASE = ENDPOINT.AS_SERVICE.FNRS_CNSLTN_MNG;
|
||||
|
||||
@@ -51,6 +56,16 @@ async function techReview(id: string, body: FnrsCnsltnApplySaveRequestDto): Prom
|
||||
await client.post<void>(`${BASE}/${id}/tech-review`, body);
|
||||
}
|
||||
|
||||
async function requestExamine(docNo: string, body: FnrsExamineRequestDto): Promise<string> {
|
||||
const response = await client.post<string>(`${BASE}/${docNo}/request-examine`, body);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async function requestActnReview(docNo: string, body: EnvoActnReviewReqDto): Promise<string> {
|
||||
const response = await client.post<string>(`${BASE}/${docNo}/request-actn-review`, body);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async function submitSupplement(id: string, body: FnrsCnsltnApplySaveRequestDto): Promise<string> {
|
||||
const response = await client.post<string>(`${BASE}/${id}/submitsupplement`, body);
|
||||
return response.data;
|
||||
@@ -68,6 +83,15 @@ async function remove(fnrsCnsltnDocNo: string): Promise<void> {
|
||||
await client.delete<void>(`${BASE}/${fnrsCnsltnDocNo}`);
|
||||
}
|
||||
|
||||
async function findFileVrfcResults(
|
||||
fnrsCnsltnDocNo: string,
|
||||
): Promise<FnrsCnsltnFileVrfcRsltResponseDto[]> {
|
||||
const response = await client.get<FnrsCnsltnFileVrfcRsltResponseDto[]>(
|
||||
`${BASE}/${fnrsCnsltnDocNo}/file-vrfc-results`,
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async function changePrgrsSttsComCd(body: DocPrgrsUpdateRequestDto): Promise<void> {
|
||||
await client.post<void>(`${BASE}/doc/updatePrgrsStts`, body);
|
||||
}
|
||||
@@ -79,8 +103,11 @@ export const fnrsCnsltnMngService = {
|
||||
saveApply,
|
||||
update,
|
||||
techReview,
|
||||
requestExamine,
|
||||
requestActnReview,
|
||||
submitSupplement,
|
||||
submitReqSupplement,
|
||||
remove,
|
||||
findFileVrfcResults,
|
||||
changePrgrsSttsComCd,
|
||||
} satisfies FnrsCnsltnMngService;
|
||||
|
||||
@@ -7,6 +7,10 @@ import type {
|
||||
FnrsCnsltnMngSearchDto,
|
||||
FnrsCnsltnMngUpdateRequestDto,
|
||||
} from "../model/fnrs-cnsltn-interface";
|
||||
import type {
|
||||
EnvoActnReviewReqDto,
|
||||
FnrsExamineRequestDto,
|
||||
} from "../model/fnrs-cnsltn-payload-schema";
|
||||
|
||||
import { fnrsCnsltnMngService } from "./fnrs-cnsltn-service";
|
||||
|
||||
@@ -14,6 +18,7 @@ export const fnrsCnsltnKeys = {
|
||||
all: ["fnrs-cnsltn"] as const,
|
||||
list: () => [...fnrsCnsltnKeys.all, "list"] as const,
|
||||
detail: (id: string) => [...fnrsCnsltnKeys.all, "detail", id] as const,
|
||||
fileVrfcResults: (id: string) => [...fnrsCnsltnKeys.all, "fileVrfcResults", id] as const,
|
||||
};
|
||||
|
||||
export function useFnrsCnsltnMngPage(params: FnrsCnsltnMngSearchDto) {
|
||||
@@ -31,6 +36,14 @@ export function useFnrsCnsltnMng(fnrsCnsltnDocNo: string | null | undefined) {
|
||||
});
|
||||
}
|
||||
|
||||
export function useFnrsCnsltnFileVrfcResults(fnrsCnsltnDocNo: string | null | undefined) {
|
||||
return useQuery({
|
||||
queryKey: fnrsCnsltnKeys.fileVrfcResults(fnrsCnsltnDocNo!),
|
||||
queryFn: () => fnrsCnsltnMngService.findFileVrfcResults(fnrsCnsltnDocNo!),
|
||||
enabled: Boolean(fnrsCnsltnDocNo),
|
||||
});
|
||||
}
|
||||
|
||||
export function useSaveFnrsCnsltnMng() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
@@ -84,6 +97,34 @@ export function useTechReviewFnrsCnsltnMng() {
|
||||
});
|
||||
}
|
||||
|
||||
export function useRequestExamineFnrsCnsltnMng() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ docNo, body }: { docNo: string; body: FnrsExamineRequestDto }) =>
|
||||
fnrsCnsltnMngService.requestExamine(docNo, body),
|
||||
onSuccess: (_data, { docNo }) => {
|
||||
queryClient.invalidateQueries({ queryKey: fnrsCnsltnKeys.all });
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: fnrsCnsltnKeys.detail(docNo),
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useRequestActnReviewFnrsCnsltnMng() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ docNo, body }: { docNo: string; body: EnvoActnReviewReqDto }) =>
|
||||
fnrsCnsltnMngService.requestActnReview(docNo, body),
|
||||
onSuccess: (_data, { docNo }) => {
|
||||
queryClient.invalidateQueries({ queryKey: fnrsCnsltnKeys.all });
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: fnrsCnsltnKeys.detail(docNo),
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useSubmitSupplementFnrsCnsltnMng() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
|
||||
@@ -3,8 +3,11 @@ export { fnrsCnsltnKeys } from "./api/queries";
|
||||
export {
|
||||
useChangePrgrsSttsComCd,
|
||||
useDeleteFnrsCnsltnMng,
|
||||
useFnrsCnsltnFileVrfcResults,
|
||||
useFnrsCnsltnMng,
|
||||
useFnrsCnsltnMngPage,
|
||||
useRequestActnReviewFnrsCnsltnMng,
|
||||
useRequestExamineFnrsCnsltnMng,
|
||||
useSaveApplyFnrsCnsltnMng,
|
||||
useSaveFnrsCnsltnMng,
|
||||
useSubmitReqSupplementFnrsCnsltnMng,
|
||||
@@ -25,6 +28,7 @@ export type {
|
||||
EsntlAtchFileItem,
|
||||
EsntlAtchFileMngTreeResponseDto,
|
||||
FnrsCnsltnApplySaveRequestDto,
|
||||
FnrsCnsltnFileVrfcRsltResponseDto,
|
||||
FnrsCnsltnMngDetResponseDto,
|
||||
FnrsCnsltnMngResponseDto,
|
||||
FnrsCnsltnMngSaveRequestDto,
|
||||
@@ -37,3 +41,11 @@ export type {
|
||||
FnrsCnsltnSplmntDmndResponseDto,
|
||||
FnrsSharItem,
|
||||
} from "./model/fnrs-cnsltn-interface";
|
||||
export type {
|
||||
EnvoActnReviewReqDto,
|
||||
FnrsExamineRequestDto,
|
||||
} from "./model/fnrs-cnsltn-payload-schema";
|
||||
export {
|
||||
envoActnReviewReqSchema,
|
||||
fnrsExamineRequestSchema,
|
||||
} from "./model/fnrs-cnsltn-payload-schema";
|
||||
|
||||
@@ -16,6 +16,11 @@ import type {
|
||||
} from "@/shared/model";
|
||||
import type { FnrsSharBizCstTreeDto } from "@/entities/fnrs-shar-se";
|
||||
|
||||
import type {
|
||||
EnvoActnReviewReqDto,
|
||||
FnrsExamineRequestDto,
|
||||
} from "./fnrs-cnsltn-payload-schema";
|
||||
|
||||
export type {
|
||||
AtflMngClientResponseDto,
|
||||
BizCstTreeDto,
|
||||
@@ -79,6 +84,8 @@ export interface FnrsCnsltnMngSearchDto extends PageableParams {
|
||||
atchFileGroupId?: string;
|
||||
sharAtchFileGroupId?: string;
|
||||
prgrsSttsComCd?: string;
|
||||
rtlztnFcltSeComCd?: string;
|
||||
rtlztnUsgSeComCd?: string;
|
||||
}
|
||||
|
||||
interface FnrsCnsltnMngResponseExtension {
|
||||
@@ -86,6 +93,10 @@ interface FnrsCnsltnMngResponseExtension {
|
||||
envoSeCdNm?: string;
|
||||
taskSeCdNm?: string;
|
||||
prgrsSttsCdNm?: string;
|
||||
beforePrgrsSttsComCd?: string;
|
||||
beforePrgrsSttsCdNm?: string;
|
||||
rtlztnFcltSeComNm?: string;
|
||||
rtlztnUsgSeComNm?: string;
|
||||
regDt?: string;
|
||||
rgtr?: string;
|
||||
rgtrNm?: string;
|
||||
@@ -163,6 +174,22 @@ export interface FnrsCnsltnApplySaveRequestDto extends PbadmsApplySaveHeader {
|
||||
fnrsSharList?: FnrsSharItem[];
|
||||
}
|
||||
|
||||
/** 백엔드 FnrsCnsltnFileVrfcRsltResponseDto */
|
||||
export interface FnrsCnsltnFileVrfcRsltResponseDto {
|
||||
fileSeComCd?: string;
|
||||
fileSeNm?: string;
|
||||
esntlAtchFileId?: string;
|
||||
atchFileGroupId?: string;
|
||||
atflId?: string;
|
||||
orgnlFileNm?: string;
|
||||
atflFlieSz?: number;
|
||||
vrfcSttsComCd?: string;
|
||||
vrfcSttsComNm?: string;
|
||||
vrfcRsltCn?: string;
|
||||
vrfcDt?: string;
|
||||
regDt?: string;
|
||||
}
|
||||
|
||||
/** Controller public 메서드명·시그니처와 동일 */
|
||||
export interface FnrsCnsltnMngService {
|
||||
findPage(searchDto: FnrsCnsltnMngSearchDto): Promise<PageResponse<FnrsCnsltnMngResponseDto>>;
|
||||
@@ -171,8 +198,11 @@ export interface FnrsCnsltnMngService {
|
||||
saveApply(body: FnrsCnsltnApplySaveRequestDto): Promise<string>;
|
||||
update(fnrsCnsltnDocNo: string, body: FnrsCnsltnMngUpdateRequestDto): Promise<string>;
|
||||
techReview(id: string, body: FnrsCnsltnApplySaveRequestDto): Promise<void>;
|
||||
requestExamine(docNo: string, body: FnrsExamineRequestDto): Promise<string>;
|
||||
requestActnReview(docNo: string, body: EnvoActnReviewReqDto): Promise<string>;
|
||||
submitSupplement(id: string, body: FnrsCnsltnApplySaveRequestDto): Promise<string>;
|
||||
submitReqSupplement(id: string, body: FnrsCnsltnApplySaveRequestDto): Promise<string>;
|
||||
remove(fnrsCnsltnDocNo: string): Promise<void>;
|
||||
findFileVrfcResults(fnrsCnsltnDocNo: string): Promise<FnrsCnsltnFileVrfcRsltResponseDto[]>;
|
||||
changePrgrsSttsComCd(body: DocPrgrsUpdateRequestDto): Promise<void>;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import { z } from "zod";
|
||||
|
||||
/** 백엔드 FnrsExamineRequestDto */
|
||||
export const fnrsExamineRequestSchema = z.object({
|
||||
prgrsSttsComCd: z.string().optional(),
|
||||
});
|
||||
|
||||
/** 백엔드 EnvoActnReviewReqDto */
|
||||
export const envoActnReviewReqSchema = z.object({
|
||||
envoDocNo: z.string().min(1),
|
||||
specialRqstCn: z.string().optional(),
|
||||
});
|
||||
|
||||
export type FnrsExamineRequestDto = z.infer<typeof fnrsExamineRequestSchema>;
|
||||
export type EnvoActnReviewReqDto = z.infer<typeof envoActnReviewReqSchema>;
|
||||
@@ -25,6 +25,7 @@ import {
|
||||
fnrsCnsltnKeys,
|
||||
type FnrsCnsltnMngDetResponseDto,
|
||||
fnrsCnsltnMngService,
|
||||
type FnrsExamineRequestDto,
|
||||
} from "@/entities/fnrs-cnsltn";
|
||||
import { SplmntDmndDialog, type SplmntDmndDialogConfirmPayload } from "@/widgets/pbadms-sprt";
|
||||
import { Button } from "@common/components";
|
||||
@@ -299,10 +300,8 @@ function FloatingActionButtons({
|
||||
}
|
||||
|
||||
async function handleRqstTechRvwClick() {
|
||||
const payload: DocPrgrsUpdateRequestDto = {
|
||||
docNo,
|
||||
docSeComCd,
|
||||
prgrsSttsComCd: "ER",
|
||||
const payload: FnrsExamineRequestDto = {
|
||||
prgrsSttsComCd: "FV",
|
||||
};
|
||||
await executeAction({
|
||||
action: FNRS_CNSLTN_VIEW_BUTTON_ACTION.rqstTechRvw,
|
||||
@@ -602,6 +601,19 @@ async function handleDmndSplmnt(
|
||||
}
|
||||
}
|
||||
|
||||
async function handleRequestExamine(
|
||||
context: ActionHandlerContext<FnrsExamineRequestDto>,
|
||||
): Promise<void> {
|
||||
try {
|
||||
const docNo = context.data.detDto?.fnrsCnsltnDocNo;
|
||||
if (!docNo) throw new Error("문서번호가 없습니다.");
|
||||
await fnrsCnsltnMngService.requestExamine(docNo, context.request);
|
||||
context.onSuccess?.();
|
||||
} catch (error) {
|
||||
context.onError?.(error as Error);
|
||||
}
|
||||
}
|
||||
|
||||
async function handleAtrzAprv(context: ActionHandlerContext): Promise<void> {
|
||||
// TODO: 결재 승인 처리 API 호출
|
||||
context.onSuccess?.();
|
||||
@@ -621,7 +633,7 @@ const buttonActionHandlers: Record<FnrsCnsltnViewButtonAction, ActionHandler> =
|
||||
rjct: handleChangePrgrsSttsComCd,
|
||||
sncnAprv: handleChangePrgrsSttsComCd,
|
||||
rvwRply: handleTechReview,
|
||||
rqstTechRvw: handleChangePrgrsSttsComCd,
|
||||
rqstTechRvw: handleRequestExamine,
|
||||
fileVrfc: () => Promise.resolve(),
|
||||
dsgntRvwr: handleDsgntRvwr,
|
||||
pwrkCnsltnDocCrt: () => Promise.resolve(),
|
||||
|
||||
@@ -456,11 +456,12 @@ function AttachmentsFormSection({
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
<label>
|
||||
<label htmlFor={`${file.esntlAtchFileId}-file-input`}>
|
||||
<input
|
||||
type="file"
|
||||
id={`${file.esntlAtchFileId}-file-input`}
|
||||
multiple
|
||||
className="hidden"
|
||||
className="peer sr-only"
|
||||
accept={toFileAccept(file.esntlAtchFileRmrk) || undefined}
|
||||
onChange={(e) => {
|
||||
void handleEssentialFileUpload(
|
||||
@@ -470,7 +471,12 @@ function AttachmentsFormSection({
|
||||
e.target.value = "";
|
||||
}}
|
||||
/>
|
||||
<Button size="sm" variant="outline" asChild>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
className="peer-focus-visible:ring-2 peer-focus-visible:ring-ring peer-focus-visible:ring-offset-2"
|
||||
asChild
|
||||
>
|
||||
<span>파일추가</span>
|
||||
</Button>
|
||||
</label>
|
||||
@@ -489,17 +495,23 @@ function AttachmentsFormSection({
|
||||
<div className="px-2">
|
||||
<div className="mb-4 flex items-center justify-between">
|
||||
<h3 className="text-xl font-semibold">기타 첨부파일</h3>
|
||||
<label>
|
||||
<label htmlFor="etc-file-input">
|
||||
<input
|
||||
type="file"
|
||||
id="etc-file-input"
|
||||
multiple
|
||||
className="hidden"
|
||||
className="peer sr-only"
|
||||
onChange={(e) => {
|
||||
void handleEtcFileUpload(e.target.files);
|
||||
e.target.value = "";
|
||||
}}
|
||||
/>
|
||||
<Button size="sm" variant="outline" asChild>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
className="peer-focus-visible:ring-2 peer-focus-visible:ring-ring peer-focus-visible:ring-offset-2"
|
||||
asChild
|
||||
>
|
||||
<span>파일추가</span>
|
||||
</Button>
|
||||
</label>
|
||||
|
||||
Reference in New Issue
Block a user