Compare 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
71 changed files with 568 additions and 727 deletions
@@ -33,10 +33,10 @@
├──────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ [영역 B] 필수첨부파일 목록 │
│ ┌────────────────────────────────────────────────────────────────────────────────────────────────┐ │
│ │ 필수첨부파일아이디 | 문서구분 | 필수첨부파일명 | 필수첨부여부 | 비고 | 등록일시 | 수정일시 | 관리 │
│ │ 필수첨부파일아이디 | 문서구분 | 필수첨부파일명 | 필수첨부여부 | 필수파일검토내용 | 비고 | 관리 │ │
│ ├────────────────────────────────────────────────────────────────────────────────────────────────┤ │
│ │ EAF-0001 | 설치인가 | 사업계획서 | 예 | 서명본 첨부 | 2026-04-22 10:10:00 | — | [수정][삭제] │ │
│ │ EAF-0002 | 기본계획 | 변경사유서 | 예 | — | 2026-04-22 11:00:00 | — | [수정][삭제] │ │
│ │ EAF-0001 | 설치인가 | 사업계획서 | 예 | 사업비 내역 검토 | 서명본 첨부 | [수정][삭제] │ │
│ │ EAF-0002 | 기본계획 | 변경사유서 | 예 | 변경 전후 비교표 | — | [수정][삭제] │ │
│ └────────────────────────────────────────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────────────────────────────────────┘
@@ -47,7 +47,9 @@
│ 필수첨부파일아이디(자동발급) [EAF-0004] │
│ 문서구분 [설치인가 ▼] │
│ 필수첨부파일명 [예: 사업계획서] │
│ 양식파일 [파일 선택] │
│ 필수첨부여부 [ ] 예 [ ] 아니오 │
│ 필수파일검토내용 [검토내용을 입력하세요]│
│ 비고 [비고를 입력하세요] │
├──────────────────────────────────────────────────────┤
│ [취소] [저장] │
@@ -139,16 +141,16 @@ END
### 5-2. 그리드 컬럼
| 컬럼명 | Field | 타입 | 비고 |
| ------------------ | ------------------- | ------ | ------------------------------------- |
| 필수첨부파일아이디 | `esntlAtchFileId` | string | 예: `EAF-0001`, 클라이언트 생성/입력 |
| 문서구분 | `docSeComCd` | string | 코드명 표시 — 공통코드 (TBD 상위코드) |
| 필수첨부파일명 | `esntlAtchFileNm` | string | |
| 필수첨부여부 | `esntlAtchYn` | string | `Y`→예, `N`→아니오 |
| 비고 | `esntlAtchFileRmrk` | string | 없으면 `—` 표시 |
| 등록일시 | `regDt` | string | `yyyy-MM-dd HH:mm:ss` |
| 수정일시 | `mdfcnDt` | string | 미수정 시 빈 값 또는 `—` |
| 관리 | — | — | 행별 **[수정] · [삭제]** |
| 컬럼명 | Field | 타입 | 비고 |
| ------------------ | -------------------- | ------ | ------------------------------------- |
| 필수첨부파일아이디 | `esntlAtchFileId` | string | 예: `EAF-0001`, 클라이언트 생성/입력 |
| 문서구분 | `docSeComCd` | string | 코드명 표시 — 공통코드 (TBD 상위코드) |
| 필수첨부파일명 | `esntlAtchFileNm` | string | |
| 필수첨부여부 | `esntlAtchYn` | string | `Y`→예, `N`→아니오 |
| 필수파일검토내용 | `esntlAtchFileRvwCn` | string | 없으면 `—` 표시, 최대 2줄 clamp |
| 비고 | `esntlAtchFileRmrk` | string | 없으면 `—` 표시 |
| 양식 내려받기 | `atchFileGroupId` | string | 양식파일 다운로드 버튼 |
| 관리 | — | — | 행별 **[수정] · [삭제]** |
- 행 클릭 시 별도 상세 화면 없음. 편집은 **[수정]** 버튼으로 다이얼로그 C를 연다.
@@ -167,13 +169,15 @@ END
### 6-1. 폼 필드
| 표시명 | 입력 유형 | 필수 | 기본값 | API 파라미터 매핑 | 비고 |
| ------------------ | ---------- | ---- | ------ | ------------------- | -------------------------------- |
| 필수첨부파일아이디 | (Readonly) | Y | — | `esntlAtchFileId` | 등록 시 미노출 |
| 문서구분 | CodeSelect | Y | — | `docSeComCd` | 예: 설치인가, 기본계획 |
| 필수첨부파일명 | Text | Y | — | `esntlAtchFileNm` | placeholder: "예: 사업계획서" |
| 필수첨부여부 | RadioGroup | Y | Y | `esntlAtchYn` | Y/N, UI 표시: 예/아니오 |
| 비고 | Textarea | N | — | `esntlAtchFileRmrk` | placeholder: "비고를 입력하세요" |
| 표시명 | 입력 유형 | 필수 | 기본값 | API 파라미터 매핑 | 비고 |
| ------------------ | ------------------- | ---- | ------ | -------------------- | ---------------------------------- |
| 필수첨부파일아이디 | (Readonly) | Y | — | `esntlAtchFileId` | 등록 시 미노출 |
| 문서구분 | CodeSelect | Y | — | `docSeComCd` | 예: 설치인가, 기본계획 |
| 필수첨부파일명 | Text | Y | — | `esntlAtchFileNm` | placeholder: "예: 사업계획서" |
| 양식파일 | AttachmentFileField | N | — | `atchFileGroupId` | TUS 파일 업로드 |
| 필수첨부여부 | RadioGroup | Y | Y | `esntlAtchYn` | Y/N, UI 표시: 예/아니오 |
| 필수파일검토내용 | Textarea | N | — | `esntlAtchFileRvwCn` | placeholder: "검토내용을 입력하세요"|
| 비고 | Textarea | N | — | `esntlAtchFileRmrk` | placeholder: "비고를 입력하세요" |
### 6-2. 버튼
@@ -210,28 +214,32 @@ END
**응답 DTO**
| Field | 타입 | 설명 |
| ------------------- | ------- | -------------------------------- |
| `esntlAtchFileId` | string | 필수첨부파일아이디 |
| `docSeComCd` | string | 문서구분공통코드 |
| `esntlAtchFileNm` | string | 필수첨부파일명 |
| `esntlAtchYn` | string | 필수첨부여부 (Y/N) |
| `esntlAtchFileRmrk` | string? | 필수첨부파일비고 |
| `rgtr` | string? | 등록자 UUID |
| `rgtrNm` | string? | 등록자명 |
| `mdfr` | string? | 수정자 UUID |
| `regDt` | string? | 등록일시 (`yyyy-MM-dd HH:mm:ss`) |
| `mdfcnDt` | string? | 수정일시 |
| Field | 타입 | 설명 |
| -------------------- | -------------------------- | -------------------------------- |
| `esntlAtchFileId` | string | 필수첨부파일아이디 |
| `docSeComCd` | string | 문서구분공통코드 |
| `esntlAtchFileNm` | string | 필수첨부파일명 |
| `esntlAtchYn` | string | 필수첨부여부 (Y/N) |
| `esntlAtchFileRvwCn` | string? | 필수첨부파일검토내용 |
| `esntlAtchFileRmrk` | string? | 필수첨부파일비고 |
| `atchFileGroupId` | string? | 첨부파일그룹아이디 (양식파일) |
| `atchFileList` | AtflMngClientResponseDto[]?| 첨부파일 목록 |
| `rgtr` | string? | 등록자 UUID |
| `rgtrNm` | string? | 등록자명 |
| `mdfr` | string? | 수정자 UUID |
| `regDt` | string? | 등록일시 (`yyyy-MM-dd HH:mm:ss`) |
| `mdfcnDt` | string? | 수정일시 |
**저장 Request**
| Field | 필수 | 설명 |
| ------------------- | ---- | ------------------ |
| `esntlAtchFileId` | Y | 필수첨부파일아이디 |
| `docSeComCd` | N | 문서구분 |
| `esntlAtchFileNm` | N | 필수첨부파일명 |
| `esntlAtchYn` | N | 필수첨부여부 |
| `esntlAtchFileRmrk` | N | 비고 |
| Field | 필수 | 설명 |
| -------------------- | ---- | -------------------- |
| `docSeComCd` | Y | 문서구분 |
| `esntlAtchFileNm` | N | 필수첨부파일명 |
| `esntlAtchYn` | N | 필수첨부여부 |
| `esntlAtchFileRvwCn` | N | 필수첨부파일검토내용 |
| `esntlAtchFileRmrk` | N | 비고 |
| `atchFileGroupId` | N | 첨부파일그룹아이디 |
**검색 Query**
@@ -19,6 +19,8 @@ export interface EsntlAtchFileMng {
esntlAtchYn: "Y" | "N";
/** 필수첨부파일비고 */
esntlAtchFileRmrk?: string;
/** 필수첨부파일검토내용 */
esntlAtchFileRvwCn?: string;
/** 첨부파일그룹아이디 (양식파일 그룹) */
atchFileGroupId?: string;
/** 첨부파일 목록 (양식파일) */
@@ -77,6 +79,8 @@ export interface EsntlAtchFileMngSaveRequest {
esntlAtchYn?: "Y" | "N";
/** 필수첨부파일비고 */
esntlAtchFileRmrk?: string;
/** 필수첨부파일검토내용 */
esntlAtchFileRvwCn?: string;
/** 첨부파일그룹아이디 (양식파일 그룹) */
atchFileGroupId?: string;
}
@@ -51,6 +51,19 @@ export function createEsntlAtchFileColumns({
meta: { align: "center" },
cell: ({ getValue }) => formatYn(getValue()),
}),
columnHelper.accessor("esntlAtchFileRvwCn", {
header: "필수파일검토내용",
meta: { align: "left" },
cell: ({ getValue }) => {
const val = getValue();
if (!val?.trim()) return "-";
return (
<span className="line-clamp-2 max-w-[20rem] text-left" title={val}>
{val}
</span>
);
},
}),
columnHelper.accessor("esntlAtchFileRmrk", {
header: "비고",
meta: { align: "center" },
@@ -26,6 +26,7 @@ export interface EsntlAtchFileFormValues {
docSeComCd: string;
esntlAtchFileNm: string;
esntlAtchYn: "Y" | "N";
esntlAtchFileRvwCn: string;
esntlAtchFileRmrk: string;
atchFileGroupId: string;
}
@@ -68,6 +69,7 @@ function EsntlAtchFileFormDialog({
docSeComCd: "",
esntlAtchFileNm: "",
esntlAtchYn: "Y",
esntlAtchFileRvwCn: "",
esntlAtchFileRmrk: "",
atchFileGroupId: "",
},
@@ -115,6 +117,7 @@ function EsntlAtchFileFormDialog({
docSeComCd: initial.docSeComCd ?? "",
esntlAtchFileNm: initial.esntlAtchFileNm ?? "",
esntlAtchYn: initial.esntlAtchYn ?? "Y",
esntlAtchFileRvwCn: initial.esntlAtchFileRvwCn ?? "",
esntlAtchFileRmrk: initial.esntlAtchFileRmrk ?? "",
atchFileGroupId: initial.atchFileGroupId ?? "",
});
@@ -127,6 +130,7 @@ function EsntlAtchFileFormDialog({
docSeComCd: "",
esntlAtchFileNm: "",
esntlAtchYn: "Y",
esntlAtchFileRvwCn: "",
esntlAtchFileRmrk: "",
atchFileGroupId: "",
});
@@ -249,6 +253,17 @@ function EsntlAtchFileFormDialog({
</div>
</RadioGroup>
</div>
<div className="space-y-2">
<Label htmlFor="esntlAtchFileRvwCn">필수파일검토내용</Label>
<Textarea
id="esntlAtchFileRvwCn"
rows={3}
placeholder="검토내용을 입력하세요"
maxLength={4000}
{...register("esntlAtchFileRvwCn")}
className="!text-xl"
/>
</div>
<div className="space-y-2">
<Label htmlFor="esntlAtchFileRmrk">비고</Label>
<Textarea
@@ -100,7 +100,7 @@ function EsntlAtchFileList({
manualPagination: true,
},
dataTableOptions: {
caption: "필수첨부파일 목록으로 필수첨부파일아이디, 문서구분, 필수첨부파일명, 필수첨부여부, 비고, 양식 내려받기, 관리 정보 제공",
caption: "필수첨부파일 목록으로 필수첨부파일아이디, 문서구분, 필수첨부파일명, 필수첨부여부, 필수파일검토내용, 비고, 양식 내려받기, 관리 정보 제공",
viewPagination: true,
},
});
@@ -112,6 +112,7 @@ function EsntlAtchFileList({
docSeComCd: values.docSeComCd,
esntlAtchFileNm: values.esntlAtchFileNm,
esntlAtchYn: values.esntlAtchYn,
esntlAtchFileRvwCn: values.esntlAtchFileRvwCn || undefined,
esntlAtchFileRmrk: values.esntlAtchFileRmrk || undefined,
atchFileGroupId: values.atchFileGroupId || undefined,
};
+33 -35
View File
@@ -33,7 +33,6 @@ import {
hasAdmdstCdColumn,
isExcelImportAdmdstCdInvalid,
mergeExcelImportWithExistingRows,
parseExcelImportCellValue,
prepareGridColumnsForExcelExport,
releaseFixedOptionsForExcelExport,
resolveColumnStyleName,
@@ -290,28 +289,14 @@ type GridViewWithSetColumnProperty = {
};
type GridViewWithEditOptions = {
setEditOptions?: (options: {
commitWhenLeave?: boolean;
appendable?: boolean;
insertable?: boolean;
}) => void;
setPasteOptions?: (options: { enableAppend?: boolean }) => void;
setEditOptions?: (options: { commitWhenLeave?: boolean }) => void;
};
/** 포커스가 그리드 밖으로 나갈 때 편집 중인 값을 자동 commit */
function applyDefaultGridEditOptions(
gridView: GridViewWithEditOptions | null | undefined,
options?: { preventRowAppend?: boolean },
) {
gridView?.setEditOptions?.({
commitWhenLeave: true,
...(options?.preventRowAppend
? { appendable: false, insertable: false }
: {}),
});
if (options?.preventRowAppend) {
gridView?.setPasteOptions?.({ enableAppend: false });
}
gridView?.setEditOptions?.({ commitWhenLeave: true });
}
function isDateEditorColumn(column: {
@@ -868,7 +853,6 @@ const DataGrid = ({
gridHeight = 55, // vh 단위
headerHeight = 60,
fitStyle = GridFitStyle.EVEN,
preventRowAppend = true,
}: DataGridProps) => {
const { columns, columnLayout } = columnInfo;
const fixedColumnNameSet = useMemo(
@@ -1347,11 +1331,6 @@ useEffect(() => {
columnLayout,
{ skipNonEditable: false },
);
const valueTypeByFieldName = new Map(
columns
.filter((c) => c.fieldName)
.map((c) => [c.fieldName as string, c.valueType]),
);
// 데이터 행 읽기
const dataRows: Record<string, string | number>[] = [];
@@ -1373,13 +1352,36 @@ useEffect(() => {
];
const fieldName = excelColumnFieldNames[colIndex];
const allFieldName = excelColumnFieldNamesAll[colIndex];
const typeFieldName = fieldName || allFieldName;
const cellValue = parseExcelImportCellValue(
cell,
typeFieldName
? valueTypeByFieldName.get(typeFieldName)
: undefined,
);
let cellValue: string | number = "";
if (
cell &&
cell.v !== undefined &&
cell.v !== null
) {
// 엑셀 셀 타입이 숫자이거나 값이 숫자인 경우 숫자로 변환
if (
cell.t === "n" ||
(typeof cell.v === "number" &&
!isNaN(cell.v))
) {
cellValue = cell.v as number;
} else {
const stringValue = String(cell.v);
// 문자열이지만 숫자로 변환 가능한 경우 숫자로 변환
const numericValue = Number(
stringValue.replace(/,/g, ""),
);
if (
!isNaN(numericValue) &&
stringValue.trim() !== ""
) {
cellValue = numericValue;
} else {
cellValue = stringValue;
}
}
}
if (allFieldName === "admdstCd" && cellValue !== "") {
rowAdmdstCd = cellValue;
@@ -1465,9 +1467,7 @@ useEffect(() => {
const scheduleFooterTask = (task: () => void, delayMs: number) => {
footerTimerIds.push(setTimeout(task, delayMs));
};
applyDefaultGridEditOptions(gridView as GridViewWithEditOptions, {
preventRowAppend,
});
applyDefaultGridEditOptions(gridView as GridViewWithEditOptions);
const columnsFieldsSig = columns.map((c) => c.fieldName).join("\u0001");
if (columnsFieldsSig !== columnsFieldsSigRef.current) {
@@ -1990,7 +1990,6 @@ useEffect(() => {
fitStyle,
enableSort,
footerVerificationResult,
preventRowAppend,
]);
// 행·셀 클릭 이벤트 (검증 오류 툴팁은 hover만 — onShowTooltip / 빈 셀 오버레이)
@@ -2528,7 +2527,6 @@ useEffect(() => {
const gridView = gridRef.current?.gridView ?? grid;
applyDefaultGridEditOptions(
gridView as GridViewWithEditOptions,
{ preventRowAppend },
);
if (!gridView?.columnByName) return;
setTimeout(() => {
+17 -13
View File
@@ -53,7 +53,6 @@ import {
hasAdmdstCdColumn,
isExcelImportAdmdstCdInvalid,
mergeExcelImportWithExistingRows,
parseExcelImportCellValue,
prepareGridColumnsForExcelExport,
releaseFixedOptionsForExcelExport,
resolveColumnStyleName,
@@ -612,11 +611,6 @@ const TabDataGrid = ({
columnLayout,
{ skipNonEditable: false },
);
const valueTypeByFieldName = new Map(
columns
.filter((c) => c.fieldName)
.map((c) => [c.fieldName as string, c.valueType]),
);
// 데이터 행 읽기
const dataRows: Record<string, string | number>[] = [];
@@ -631,13 +625,23 @@ const TabDataGrid = ({
const cell = worksheet[XLSX.utils.encode_cell({ r: row, c: col })];
const fieldName = excelColumnFieldNames[colIndex];
const allFieldName = excelColumnFieldNamesAll[colIndex];
const typeFieldName = fieldName || allFieldName;
const cellValue = parseExcelImportCellValue(
cell,
typeFieldName
? valueTypeByFieldName.get(typeFieldName)
: undefined,
);
let cellValue: string | number = '';
if (cell && cell.v !== undefined && cell.v !== null) {
// 엑셀 셀 타입이 숫자이거나 값이 숫자인 경우 숫자로 변환
if (cell.t === 'n' || (typeof cell.v === 'number' && !isNaN(cell.v))) {
cellValue = cell.v as number;
} else {
const stringValue = String(cell.v);
// 문자열이지만 숫자로 변환 가능한 경우 숫자로 변환
const numericValue = Number(stringValue.replace(/,/g, ''));
if (!isNaN(numericValue) && stringValue.trim() !== '') {
cellValue = numericValue;
} else {
cellValue = stringValue;
}
}
}
if (allFieldName === 'admdstCd' && cellValue !== '') {
rowAdmdstCd = cellValue;
@@ -20,7 +20,6 @@ import { nullableTrimmedString } from "@/components/util/input/nullableString";
import { statisticsInputExcretaTreatmentFacilityApi } from "@/entities/statistics/api/input/statistics-input-excreta-treatment-facility-service";
import { statisticsInputSewSupplyRateApi } from "@/entities/statistics/api/input/statistics-input-sew-supply-rate-service";
import { statisticsApi } from "@/entities/statistics/api/statistics-service";
import { callImrServiceWithRollbackOnFailure } from "@/components/util/input/call-imr-with-rollback";
import {
EXCRETA_TREATMENT_FACILITY_STATS_CD,
type ExcretaTreatmentFacilityItem,
@@ -76,11 +75,7 @@ import { toGridRows } from "@/components/util/grid/gridFunction";
import { formatCompactYmdForGrid, toCompactYmdOrNull } from "@/components/util/grid/ymdFormat";
import { toExcelExportSearchContextFromSearchForm } from "@/components/util/gridExcelExport";
import { isVerificationFailedFromCommonInfo } from "@/components/util/input/verification-failed-flag";
import {
formatVerificationTooltip,
normalizeVerificationResultMap,
type VerificationResultLikeMap,
} from "@/components/util/input/verification-result-display";
import type { VerificationResultLikeMap } from "@/components/util/input/verification-result-display";
import { useAdminStore } from "@/store/admin-store";
import { useTabStore } from "@/store/tab-store";
@@ -212,7 +207,7 @@ function mapExcretaTreatmentFacilityListToGridRows(
},
])[0] as GridRowWithVerification;
if (item.verificationResult) {
row.verificationResult = normalizeVerificationResultMap(item.verificationResult);
row.verificationResult = item.verificationResult;
}
return row;
});
@@ -507,9 +502,7 @@ const ExcretaTreatmentFacilityInp = () => {
setStatusInfo(nextStatusInfo);
setStatsCd(commonInfo.statsCd);
setIsVerificationFailed(isVerificationFailedFromCommonInfo(commonInfo));
setFooterVerificationResult(
normalizeVerificationResultMap(commonInfo.verificationResult) ?? null,
);
setFooterVerificationResult(commonInfo.verificationResult ?? null);
if (overrides?.mode !== "PRE") {
setIsNotApplicable(gridRows.length === 0);
} else if (gridRows.length > 0) {
@@ -820,13 +813,7 @@ const ExcretaTreatmentFacilityInp = () => {
};
try {
const imrResponse = await callImrServiceWithRollbackOnFailure(imrParam, {
statsYr: lastSearchedValues.year ?? "",
statsCd: statsCdForSubmit,
sewerBzmnCd: lastSearchedValues?.bsnmCd ?? "",
rollbackStatusCode: statusInfo?.lclgvSbmsnSttsCd ?? "",
mdfrUuid: userInfo.userUnqId,
});
const imrResponse = await statisticsApi.callImrService(imrParam);
if (imrResponse.job_id) {
await waitForImrJobCompleted(
imrResponse.job_id,
@@ -965,7 +952,19 @@ const ExcretaTreatmentFacilityInp = () => {
if (!verification) return;
const errors = verification[fieldName];
if (Array.isArray(errors) && errors.length > 0) {
return formatVerificationTooltip(errors);
return errors
.map((e) => {
const prefix =
e.vrfcCd === "IMR"
? "시계열 검증 - "
: e.vrfcCd === "NV"
? "일반검증 - "
: "모집단 검증 -";
return `${prefix}${e.rsnExpln}
(검증 기준값: ${e.cmprCn} / 현재 입력값: ${e.nowCn})`;
})
.filter(Boolean)
.join("\n");
}
};
@@ -9,7 +9,6 @@ import { ConfirmDialog } from "@/shared/ui/confirm-dialog";
import { statisticsInputPrivateSewageFacilityApi } from "@/entities/statistics/api/input/statistics-input-private-sewage-facility-service";
import { statisticsInputSewSupplyRateApi } from "@/entities/statistics/api/input/statistics-input-sew-supply-rate-service";
import { statisticsApi } from "@/entities/statistics/api/statistics-service";
import { callImrServiceWithRollbackOnFailure } from "@/components/util/input/call-imr-with-rollback";
import type {
PrivateSewageDesigStatusItem,
PrivateSewageDesigStatusSaveItem,
@@ -69,11 +68,7 @@ import {
patchFooterVerificationByTab,
patchVerificationFailedByTab,
} from "@/components/util/input/verification-failed-flag";
import {
formatVerificationTooltip,
normalizeVerificationResultMap,
type VerificationResultLikeMap,
} from "@/components/util/input/verification-result-display";
import type { VerificationResultLikeMap } from "@/components/util/input/verification-result-display";
import { nullableTrimmedString } from "@/components/util/input/nullableString";
import { useAdminStore } from "@/store/admin-store";
import { useTabStore } from "@/store/tab-store";
@@ -180,9 +175,7 @@ function mapRegionalListToGridRows<
return filterBySelectedAdmdstCd(list, sidoListRef.current, values).map((item) => {
const { verificationResult, ...rest } = item;
const row = toGridRows([{ ctpvNm, ...rest }])[0] as GridRowWithVerification;
if (verificationResult) {
row.verificationResult = normalizeVerificationResultMap(verificationResult);
}
if (verificationResult) row.verificationResult = verificationResult;
return row;
});
}
@@ -895,13 +888,7 @@ const PrivateSewageFacilityInp = () => {
};
try {
const imrResponse = await callImrServiceWithRollbackOnFailure(imrParam, {
statsYr: lastSearchedValues.year ?? "",
statsCd: statsCdForTab,
sewerBzmnCd: lastSearchedValues?.bsnmCd ?? "",
rollbackStatusCode: activeTabStatus?.lclgvSbmsnSttsCd ?? "",
mdfrUuid: userInfo.userUnqId,
});
const imrResponse = await statisticsApi.callImrService(imrParam);
if (imrResponse.job_id) {
await waitForImrJobCompleted(
imrResponse.job_id,
@@ -1157,7 +1144,18 @@ const PrivateSewageFacilityInp = () => {
if (!verification) return;
const errors = verification[fieldName];
if (Array.isArray(errors) && errors.length > 0) {
return formatVerificationTooltip(errors);
return errors
.map((e) => {
const prefix =
e.vrfcCd === "IMR"
? "시계열 검증 - "
: e.vrfcCd === "NV"
? "일반검증 - "
: "모집단 검증 - ";
return `${prefix}${e.rsnExpln}(검증 기준값: ${e.cmprCn} / 현재 입력값: ${e.nowCn})`;
})
.filter(Boolean)
.join("\n");
}
};
@@ -8,7 +8,6 @@ import { useStatisticsInputPendingNavigation } from '@/shared/lib/use-statistics
import { statisticsInputSewSupplyRateApi } from '@/entities/statistics/api/input/statistics-input-sew-supply-rate-service';
import { statisticsInputPublicSewageFacilityApiService } from '@/entities/statistics/api/input/statistics-public-sewage-facility-input-service';
import { statisticsApi } from '@/entities/statistics/api/statistics-service';
import { callImrServiceWithRollbackOnFailure } from '@/components/util/input/call-imr-with-rollback';
import type {
PublicSewageFacilityRequestParams,
PublicSewageFacilitySaveItem,
@@ -53,11 +52,7 @@ import { fetchColumnGuidesRecord } from '@/components/util/grid/columnGuides';
import { toGridRows } from '@/components/util/grid/gridFunction';
import { toExcelExportSearchContextFromSearchForm } from "@/components/util/gridExcelExport";
import { patchFooterVerificationByTab,patchVerificationFailedByTab } from '@/components/util/input/verification-failed-flag';
import {
formatVerificationTooltip,
withNormalizedVerificationResult,
type VerificationResultLikeMap,
} from '@/components/util/input/verification-result-display';
import type { VerificationResultLikeMap } from '@/components/util/input/verification-result-display';
import { nullableTrimmedString } from '@/components/util/input/nullableString';
import { useAdminStore } from '@/store/admin-store';
import { useTabStore } from '@/store/tab-store';
@@ -443,13 +438,7 @@ const PublicSewageFacilityInp = () => {
};
try {
const imrResponse = await callImrServiceWithRollbackOnFailure(imrParam, {
statsYr: lastSearchedValues.year ?? '',
statsCd: statsCdForTab,
sewerBzmnCd: lastSearchedValues?.bsnmCd ?? '',
rollbackStatusCode: activeTabStatus?.lclgvSbmsnSttsCd ?? '',
mdfrUuid: userInfo.userUnqId,
});
const imrResponse = await statisticsApi.callImrService(imrParam);
if (imrResponse.job_id) {
await waitForImrJobCompleted(
imrResponse.job_id,
@@ -800,13 +789,11 @@ const PublicSewageFacilityInp = () => {
sidoListRef.current,
searchContext,
);
const convertedData = list.map((item) =>
withNormalizedVerificationResult({
ctpvNm: commonInfo.ctpvNm,
...item,
rmrk: item.rmrk ?? null,
}),
);
const convertedData = list.map((item) => ({
ctpvNm: commonInfo.ctpvNm,
...item,
rmrk: item.rmrk ?? null,
}));
if (list.length === 0) {
setShowIsNotApplicable(true);
@@ -1588,7 +1575,17 @@ const PublicSewageFacilityInp = () => {
if (!verification) return;
const errors = verification[fieldName];
if (Array.isArray(errors) && errors.length > 0) {
return formatVerificationTooltip(errors);
return errors
.map((e) => {
const prefix =
e.vrfcCd === "IMR" ? "시계열 검증 - " : (e.vrfcCd === "NV" ? "일반검증 - " : "모집단 검증 - ");
const comp = e.cmprCn ?? "";
const now = e.nowCn ?? "";
return `${prefix}${e.rsnExpln}
(검증 기준값: ${comp} / 현재 입력값: ${now})`;
})
.filter(Boolean)
.join("\n");
}
}}
/>
@@ -20,7 +20,6 @@ import { nullableTrimmedString } from "@/components/util/input/nullableString";
import { statisticsInputPumpingStationApi } from "@/entities/statistics/api/input/statistics-input-pumping-station-service";
import { statisticsInputSewSupplyRateApi } from "@/entities/statistics/api/input/statistics-input-sew-supply-rate-service";
import { statisticsApi } from "@/entities/statistics/api/statistics-service";
import { callImrServiceWithRollbackOnFailure } from "@/components/util/input/call-imr-with-rollback";
import {
PUMPING_STATION_STATS_CD,
type PumpingStationItem,
@@ -76,11 +75,7 @@ import { toGridRows } from "@/components/util/grid/gridFunction";
import { formatCompactYmdForGrid, toCompactYmdOrNull } from "@/components/util/grid/ymdFormat";
import { toExcelExportSearchContextFromSearchForm } from "@/components/util/gridExcelExport";
import { isVerificationFailedFromCommonInfo } from "@/components/util/input/verification-failed-flag";
import {
formatVerificationTooltip,
normalizeVerificationResultMap,
type VerificationResultLikeMap,
} from "@/components/util/input/verification-result-display";
import type { VerificationResultLikeMap } from "@/components/util/input/verification-result-display";
import { useAdminStore } from "@/store/admin-store";
import { useTabStore } from "@/store/tab-store";
@@ -211,7 +206,7 @@ function mapPumpingStationListToGridRows(
},
])[0] as GridRowWithVerification;
if (item.verificationResult) {
row.verificationResult = normalizeVerificationResultMap(item.verificationResult);
row.verificationResult = item.verificationResult;
}
return row;
});
@@ -498,9 +493,7 @@ const PumpingStationInp = () => {
setStatusInfo(nextStatusInfo);
setStatsCd(commonInfo.statsCd);
setIsVerificationFailed(isVerificationFailedFromCommonInfo(commonInfo));
setFooterVerificationResult(
normalizeVerificationResultMap(commonInfo.verificationResult) ?? null,
);
setFooterVerificationResult(commonInfo.verificationResult ?? null);
if (overrides?.mode !== "PRE") {
setIsNotApplicable(gridRows.length === 0);
} else if (gridRows.length > 0) {
@@ -803,13 +796,7 @@ const PumpingStationInp = () => {
};
try {
const imrResponse = await callImrServiceWithRollbackOnFailure(imrParam, {
statsYr: lastSearchedValues.year ?? "",
statsCd: statsCdForSubmit,
sewerBzmnCd: lastSearchedValues?.bsnmCd ?? "",
rollbackStatusCode: statusInfo?.lclgvSbmsnSttsCd ?? "",
mdfrUuid: userInfo.userUnqId,
});
const imrResponse = await statisticsApi.callImrService(imrParam);
if (imrResponse.job_id) {
await waitForImrJobCompleted(
imrResponse.job_id,
@@ -948,7 +935,19 @@ const PumpingStationInp = () => {
if (!verification) return;
const errors = verification[fieldName];
if (Array.isArray(errors) && errors.length > 0) {
return formatVerificationTooltip(errors);
return errors
.map((e) => {
const prefix =
e.vrfcCd === "IMR"
? "시계열 검증 - "
: e.vrfcCd === "NV"
? "일반검증 - "
: "모집단 검증 -";
return `${prefix}${e.rsnExpln}
(검증 기준값: ${e.cmprCn} / 현재 입력값: ${e.nowCn})`;
})
.filter(Boolean)
.join("\n");
}
};
@@ -13,7 +13,6 @@ import { nullableTrimmedString } from "@/components/util/input/nullableString";
import { statisticsInputSewSupplyRateApi } from "@/entities/statistics/api/input/statistics-input-sew-supply-rate-service";
import { statisticsInputSewageExcretaRelatedApi } from "@/entities/statistics/api/input/statistics-input-sewage-excreta-related-service";
import { statisticsApi } from "@/entities/statistics/api/statistics-service";
import { callImrServiceWithRollbackOnFailure } from "@/components/util/input/call-imr-with-rollback";
import type {
ExcretaRelatedIndustrySaveItem,
PrivateSewageTreatRelatedIndustrySaveItem,
@@ -85,11 +84,7 @@ import {
patchFooterVerificationByTab,
patchVerificationFailedByTab,
} from "@/components/util/input/verification-failed-flag";
import {
formatVerificationTooltip,
normalizeVerificationResultMap,
type VerificationResultLikeMap,
} from "@/components/util/input/verification-result-display";
import type { VerificationResultLikeMap } from "@/components/util/input/verification-result-display";
import { useAdminStore } from "@/store/admin-store";
import { useTabStore } from "@/store/tab-store";
@@ -557,7 +552,19 @@ function verificationCellTooltip({
if (!verification) return;
const errors = verification[fieldName];
if (Array.isArray(errors) && errors.length > 0) {
return formatVerificationTooltip(errors);
return errors
.map((e) => {
const prefix =
e.vrfcCd === "IMR"
? "시계열 검증 - "
: e.vrfcCd === "NV"
? "일반검증 - "
: "모집단 검증 - ";
return `${prefix}${e.rsnExpln}
(검증 기준값: ${e.cmprCn} / 현재 입력값: ${e.nowCn})`;
})
.filter(Boolean)
.join("\n");
}
}
@@ -581,7 +588,7 @@ function mapListToGridRows<T extends { verificationResult?: SewageExcretaRelated
return filterBySelectedAdmdstCd(list, sidoListRef.current, values).map((item) => {
const row = toGridRows([{ ctpvNm, ...item }])[0] as GridRowWithVerification;
if (item.verificationResult) {
row.verificationResult = normalizeVerificationResultMap(item.verificationResult);
row.verificationResult = item.verificationResult;
}
return row;
});
@@ -1496,13 +1503,7 @@ const SewageExcretaRelatedInp = () => {
};
try {
const imrResponse = await callImrServiceWithRollbackOnFailure(imrParam, {
statsYr: lastSearchedValues.year ?? "",
statsCd: statsCdForTab,
sewerBzmnCd: lastSearchedValues?.bsnmCd ?? "",
rollbackStatusCode: activeTabStatus?.lclgvSbmsnSttsCd ?? "",
mdfrUuid: userInfo.userUnqId,
});
const imrResponse = await statisticsApi.callImrService(imrParam);
if (imrResponse.job_id) {
await waitForImrJobCompleted(
imrResponse.job_id,
@@ -21,7 +21,6 @@ import { nullableTrimmedString } from "@/components/util/input/nullableString";
import { statisticsInputSewSupplyRateApi } from "@/entities/statistics/api/input/statistics-input-sew-supply-rate-service";
import { statisticsInputSewageExcretaSlurgeApi } from "@/entities/statistics/api/input/statistics-input-sewage-excreta-slurge-service";
import { statisticsApi } from "@/entities/statistics/api/statistics-service";
import { callImrServiceWithRollbackOnFailure } from "@/components/util/input/call-imr-with-rollback";
import type {
DigestorOperatingStatusSaveItem,
ExcretaSlurgeProcessingStatusSaveItem,
@@ -91,11 +90,7 @@ import {
patchFooterVerificationByTab,
patchVerificationFailedByTab,
} from "@/components/util/input/verification-failed-flag";
import {
formatVerificationTooltip,
normalizeVerificationResultMap,
type VerificationResultLikeMap,
} from "@/components/util/input/verification-result-display";
import type { VerificationResultLikeMap } from "@/components/util/input/verification-result-display";
import { useAdminStore } from "@/store/admin-store";
import { useTabStore } from "@/store/tab-store";
@@ -153,7 +148,19 @@ function verificationCellTooltip({
if (!verification) return;
const errors = verification[fieldName];
if (Array.isArray(errors) && errors.length > 0) {
return formatVerificationTooltip(errors);
return errors
.map((e) => {
const prefix =
e.vrfcCd === "IMR"
? "시계열 검증 - "
: e.vrfcCd === "NV"
? "일반검증 - "
: "모집단 검증 - ";
return `${prefix}${e.rsnExpln}
(검증 기준값: ${e.cmprCn} / 현재 입력값: ${e.nowCn})`;
})
.filter(Boolean)
.join("\n");
}
}
@@ -178,7 +185,7 @@ function mapListToGridRows<T extends { verificationResult?: SewageExcretaSlurgeV
return filterBySelectedAdmdstCd(list, sidoListRef.current, values).map((item) => {
const row = toGridRows([{ ctpvNm, ...item }])[0] as GridRowWithVerification;
if (item.verificationResult) {
row.verificationResult = normalizeVerificationResultMap(item.verificationResult);
row.verificationResult = item.verificationResult;
}
return row;
});
@@ -620,6 +627,7 @@ function buildExcretaSlurgeProcessingStatusSaveItem(
rcycQnt: toGridNumber(row.rcycQnt),
olandLfvlm: toGridNumber(row.olandLfvlm),
incnrQnt: toGridNumber(row.incnrQnt),
oceanInputQnt: toGridNumber(row.oceanInputQnt),
fuelQnt: toGridNumber(row.fuelQnt),
etcQnt: toGridNumber(row.etcQnt),
unprcsQnt: toGridNumber(row.unprcsQnt),
@@ -1478,13 +1486,7 @@ const SewageExcretaSlurgeInp = () => {
};
try {
const imrResponse = await callImrServiceWithRollbackOnFailure(imrParam, {
statsYr: lastSearchedValues.year ?? "",
statsCd: statsCdForSubmit,
sewerBzmnCd: lastSearchedValues?.bsnmCd ?? "",
rollbackStatusCode: activeTabStatus?.lclgvSbmsnSttsCd ?? "",
mdfrUuid: userInfo.userUnqId,
});
const imrResponse = await statisticsApi.callImrService(imrParam);
if (imrResponse.job_id) {
await waitForImrJobCompleted(
imrResponse.job_id,
@@ -10,7 +10,6 @@ import {
import { statisticsInputSewSupplyRateApi } from "@/entities/statistics/api/input/statistics-input-sew-supply-rate-service";
import { statisticsInputSewPipeApiService } from "@/entities/statistics/api/input/statistics-sewage-pipe-input-service";
import { statisticsApi } from "@/entities/statistics/api/statistics-service";
import { callImrServiceWithRollbackOnFailure } from "@/components/util/input/call-imr-with-rollback";
import type {
SeweragePipeInputFactInstallStatusSaveRequest,
SeweragePipeInputFactInstallStatusSubmitRequest,
@@ -83,10 +82,7 @@ import {
} from "@/components/util/input/verification-failed-flag";
import { nullableTrimmedString } from "@/components/util/input/nullableString";
import {
formatVerificationTooltip,
getVerificationErrorsForField,
normalizeVerificationResultMap,
withNormalizedVerificationResult,
type VerificationResultLikeMap,
} from "@/components/util/input/verification-result-display";
import { useAdminStore } from "@/store/admin-store";
@@ -293,10 +289,7 @@ function normalizePipeTypeStatusRow(
lyngPrlg: Number(lyngPrlgWhol ?? 0),
dmltnPrlg: Number(dmltnPrlgWhol ?? 0),
...yearFields,
verificationResult:
normalizeVerificationResultMap(
remapPipeTypePrlgWholVerification(verificationResult),
) ?? undefined,
verificationResult: remapPipeTypePrlgWholVerification(verificationResult) ?? undefined,
};
}
@@ -726,12 +719,10 @@ const SewagePipeInp = () => {
const response = await statisticsInputSewPipeApiService.getRegionalFactStatus(searchParam);
if (response.status === 'success') {
const commonInfo = response.data.commonInfo;
const convertedData = filterFetched(response.data.regionalFactStatusList).map((d) =>
withNormalizedVerificationResult({
ctpvNm: commonInfo.ctpvNm,
...d,
}),
);
const convertedData = filterFetched(response.data.regionalFactStatusList).map((d) => ({
ctpvNm: commonInfo.ctpvNm,
...d,
}));
applyRowMap((m) => m.set('ST00020001', toGridRows(convertedData)));
setTabStatusInfoMap((prev) => new Map(prev).set(tabKey, response.data.statusInfo));
setTabStatsCdMap((prev) => new Map(prev).set(tabKey, commonInfo.statsCd));
@@ -758,12 +749,10 @@ const SewagePipeInp = () => {
const response = await statisticsInputSewPipeApiService.getFactInstallStatus(searchParam);
if (response.status === 'success') {
const commonInfo = response.data.commonInfo;
const convertedData = filterFetched(response.data.factInstallStatusList).map((d) =>
withNormalizedVerificationResult({
ctpvNm: commonInfo.ctpvNm,
...d,
}),
);
const convertedData = filterFetched(response.data.factInstallStatusList).map((d) => ({
ctpvNm: commonInfo.ctpvNm,
...d,
}));
if (convertedData.length === 0) {
const alreadyHandledForSameSearch =
factInstallNoDataHandledKeyRef.current === searchConditionKey;
@@ -840,12 +829,10 @@ const SewagePipeInp = () => {
const response = await statisticsInputSewPipeApiService.getPipeDiameterStatus(searchParam);
if (response.status === 'success') {
const commonInfo = response.data.commonInfo;
const convertedData = filterFetched(response.data.pipeDiameterStatusList).map((d) =>
withNormalizedVerificationResult({
ctpvNm: commonInfo.ctpvNm,
...d,
}),
);
const convertedData = filterFetched(response.data.pipeDiameterStatusList).map((d) => ({
ctpvNm: commonInfo.ctpvNm,
...d,
}));
applyRowMap((m) => m.set('ST00020004', toGridRows(convertedData)));
setTabStatusInfoMap((prev) => new Map(prev).set(tabKey, response.data.statusInfo));
setTabStatsCdMap((prev) => new Map(prev).set(tabKey, commonInfo.statsCd));
@@ -872,12 +859,10 @@ const SewagePipeInp = () => {
const response = await statisticsInputSewPipeApiService.getManholeStatus(searchParam);
if (response.status === 'success') {
const commonInfo = response.data.commonInfo;
const convertedData = filterFetched(response.data.manholeStatusList).map((d) =>
withNormalizedVerificationResult({
ctpvNm: commonInfo.ctpvNm,
...d,
}),
);
const convertedData = filterFetched(response.data.manholeStatusList).map((d) => ({
ctpvNm: commonInfo.ctpvNm,
...d,
}));
applyRowMap((m) => m.set('ST00020005', toGridRows(convertedData)));
setTabStatusInfoMap((prev) => new Map(prev).set(tabKey, response.data.statusInfo));
setTabStatsCdMap((prev) => new Map(prev).set(tabKey, commonInfo.statsCd));
@@ -904,15 +889,13 @@ const SewagePipeInp = () => {
const response = await statisticsInputSewPipeApiService.getConnectionStatus(searchParam);
if (response.status === 'success') {
const commonInfo = response.data.commonInfo;
const facilityConvertedData = filterFetched(response.data.facilityConnectionList).map((d) =>
withNormalizedVerificationResult({ ...d }),
);
const adminConvertedData = filterFetched(response.data.adminAreaConnectionList).map((d) =>
withNormalizedVerificationResult({
...d,
ctpvNm: d.ctpvNm || commonInfo.ctpvNm,
}),
);
const facilityConvertedData = filterFetched(response.data.facilityConnectionList).map((d) => ({
...d,
}));
const adminConvertedData = filterFetched(response.data.adminAreaConnectionList).map((d) => ({
...d,
ctpvNm: d.ctpvNm || commonInfo.ctpvNm,
}));
applyRowMap((m) => {
m.set('treatmentPlantConnectionInp', toGridRows(facilityConvertedData));
m.set('adminAreaConnectionInp', toGridRows(adminConvertedData));
@@ -1355,6 +1338,7 @@ const SewagePipeInp = () => {
rmrk: nullableTrimmedString(d.rmrk),
lyngPrlgWhol: formatNumber(d.lyngPrlg, "lyngPrlg"),
dmltnPrlgWhol: formatNumber(d.dmltnPrlg, "dmltnPrlg"),
pipeByPrstWhol: formatNumber(d.pipeByPrstWhol, "pipeByPrstWhol"),
};
});
const requestData: SeweragePipeInputPipeTypeStatusSubmitRequest = {
@@ -1471,13 +1455,7 @@ const SewagePipeInp = () => {
};
try {
const imrResponse = await callImrServiceWithRollbackOnFailure(imrParam, {
statsYr: values.year ?? "",
statsCd: statsCdForTab,
sewerBzmnCd: values?.bsnmCd ?? "",
rollbackStatusCode: statusForTab?.lclgvSbmsnSttsCd ?? "",
mdfrUuid: userInfo.userUnqId,
});
const imrResponse = await statisticsApi.callImrService(imrParam);
if (imrResponse.job_id) {
await waitForImrJobCompleted(
imrResponse.job_id,
@@ -1852,6 +1830,7 @@ const SewagePipeInp = () => {
rmrk: nullableTrimmedString(d.rmrk),
lyngPrlgWhol: formatNumber(d.lyngPrlg, "lyngPrlg"),
dmltnPrlgWhol: formatNumber(d.dmltnPrlg, "dmltnPrlg"),
pipeByPrstWhol: formatNumber(d.pipeByPrstWhol, "pipeByPrstWhol"),
};
});
@@ -2258,7 +2237,17 @@ const SewagePipeInp = () => {
tabKey,
);
if (Array.isArray(errors) && errors.length > 0) {
return formatVerificationTooltip(errors);
return errors
.map((e) => {
const prefix =
e.vrfcCd === "IMR" ? "시계열 검증 - " : (e.vrfcCd === "NV" ? "일반검증 - " : "모집단 검증 - ");
const comp = e.cmprCn ?? "";
const now = e.nowCn ?? "";
return `${prefix}${e.rsnExpln}
(검증 기준값: ${comp} / 현재 입력값: ${now})`;
})
.filter(Boolean)
.join("\n");
}
}}
/>
@@ -11,7 +11,6 @@ import { ConfirmDialog } from "@/shared/ui/confirm-dialog";
import { statisticsInputSewSupplyRateApi } from "@/entities/statistics/api/input/statistics-input-sew-supply-rate-service";
import { sewagePipeMaintainApiService } from "@/entities/statistics/api/input/statistics-input-sewage-pipe-maintain-service";
import { statisticsApi } from "@/entities/statistics/api/statistics-service";
import { callImrServiceWithRollbackOnFailure } from "@/components/util/input/call-imr-with-rollback";
import type {
SewagePipeMaintainDredgingSaveItem,
SewagePipeMaintainDredgingSaveRequest,
@@ -70,11 +69,7 @@ import {
patchFooterVerificationByTab,
patchVerificationFailedByTab,
} from "@/components/util/input/verification-failed-flag";
import {
formatVerificationTooltip,
withNormalizedVerificationResult,
type VerificationResultLikeMap,
} from "@/components/util/input/verification-result-display";
import type { VerificationResultLikeMap } from "@/components/util/input/verification-result-display";
import { nullableTrimmedString } from "@/components/util/input/nullableString";
import { useAdminStore } from "@/store/admin-store";
import { useStatisticsInputFocusStore } from "@/store/statistics-input-focus-store";
@@ -449,12 +444,10 @@ const SewagePipeMaintainInp = () => {
repairStatusList ?? [],
sidoListRef.current,
searchContext,
).map((d) =>
withNormalizedVerificationResult({
ctpvNm: commonInfo.ctpvNm,
...d,
}),
);
).map((d) => ({
ctpvNm: commonInfo.ctpvNm,
...d,
}));
const gridRows = toGridRows(convertedData);
applyRowMap(mergeIntoExistingMap, (m) => {
@@ -494,12 +487,10 @@ const SewagePipeMaintainInp = () => {
pipeDredgingStatusList ?? [],
sidoListRef.current,
searchContext,
).map((d) =>
withNormalizedVerificationResult({
ctpvNm: commonInfo.ctpvNm,
...d,
}),
);
).map((d) => ({
ctpvNm: commonInfo.ctpvNm,
...d,
}));
const gridRows = toGridRows(convertedData);
applyRowMap(mergeIntoExistingMap, (m) => {
@@ -687,13 +678,7 @@ const SewagePipeMaintainInp = () => {
};
try {
const imrResponse = await callImrServiceWithRollbackOnFailure(imrParam, {
statsYr: lastSearchedValues.year ?? "",
statsCd: statsCdForTab,
sewerBzmnCd: lastSearchedValues?.bsnmCd ?? "",
rollbackStatusCode: activeTabStatus?.lclgvSbmsnSttsCd ?? "",
mdfrUuid: userInfo.userUnqId,
});
const imrResponse = await statisticsApi.callImrService(imrParam);
if (imrResponse.job_id) {
await waitForImrJobCompleted(
imrResponse.job_id,
@@ -1118,7 +1103,14 @@ const SewagePipeMaintainInp = () => {
if (!verification) return;
const errors = verification[fieldName];
if (Array.isArray(errors) && errors.length > 0) {
return formatVerificationTooltip(errors);
return errors
.map((e) => {
const prefix =
e.vrfcCd === "IMR" ? "시계열 검증 - " : e.vrfcCd === "NV" ? "일반검증 - " : "모집단 검증 - ";
return `${prefix}${e.rsnExpln}(검증 기준값: ${e.cmprCn} / 현재 입력값: ${e.nowCn})`;
})
.filter(Boolean)
.join("\n");
}
};
@@ -21,7 +21,6 @@ import { nullableTrimmedString } from "@/components/util/input/nullableString";
import { statisticsInputSewSupplyRateApi } from "@/entities/statistics/api/input/statistics-input-sew-supply-rate-service";
import { statisticsInputSewageReservoirPumpApi } from "@/entities/statistics/api/input/statistics-input-sewage-reservoir-pump-service";
import { statisticsApi } from "@/entities/statistics/api/statistics-service";
import { callImrServiceWithRollbackOnFailure } from "@/components/util/input/call-imr-with-rollback";
import {
type RainfallPumpStationItem,
type RainfallPumpStationSaveItem,
@@ -90,11 +89,7 @@ import {
patchFooterVerificationByTab,
patchVerificationFailedByTab,
} from "@/components/util/input/verification-failed-flag";
import {
formatVerificationTooltip,
normalizeVerificationResultMap,
type VerificationResultLikeMap,
} from "@/components/util/input/verification-result-display";
import type { VerificationResultLikeMap } from "@/components/util/input/verification-result-display";
import { useAdminStore } from "@/store/admin-store";
import { useTabStore } from "@/store/tab-store";
@@ -284,7 +279,7 @@ function mapSewageReservoirListToGridRows(
},
])[0] as GridRowWithVerification;
if (item.verificationResult) {
row.verificationResult = normalizeVerificationResultMap(item.verificationResult);
row.verificationResult = item.verificationResult;
}
return row;
});
@@ -440,7 +435,7 @@ function mapRainfallPumpStationListToGridRows(
])[0] as GridRowWithVerification;
if (item.verificationResult) {
row.verificationResult = normalizeVerificationResultMap(item.verificationResult);
row.verificationResult = item.verificationResult;
}
return row;
});
@@ -869,13 +864,7 @@ const SewageReservoirPumpInp = () => {
};
try {
const imrResponse = await callImrServiceWithRollbackOnFailure(imrParam, {
statsYr: lastSearchedValues.year ?? "",
statsCd: statsCdForTab,
sewerBzmnCd: lastSearchedValues?.bsnmCd ?? "",
rollbackStatusCode: activeTabStatus?.lclgvSbmsnSttsCd ?? "",
mdfrUuid: userInfo.userUnqId,
});
const imrResponse = await statisticsApi.callImrService(imrParam);
if (imrResponse.job_id) {
await waitForImrJobCompleted(
imrResponse.job_id,
@@ -1527,7 +1516,19 @@ const SewageReservoirPumpInp = () => {
if (!verification) return;
const errors = verification[fieldName];
if (Array.isArray(errors) && errors.length > 0) {
return formatVerificationTooltip(errors);
return errors
.map((e) => {
const prefix =
e.vrfcCd === "IMR"
? "시계열 검증 - "
: e.vrfcCd === "NV"
? "일반검증 - "
: "모집단 검증 -";
return `${prefix}${e.rsnExpln}
(검증 기준값: ${e.cmprCn} / 현재 입력값: ${e.nowCn})`;
})
.filter(Boolean)
.join("\n");
}
};
@@ -8,7 +8,6 @@ import { useStatisticsInputPendingNavigation } from "@/shared/lib/use-statistics
import { statisticsInputSewSupplyRateApi } from "@/entities/statistics/api/input/statistics-input-sew-supply-rate-service";
import { statisticsInputSewerageFinanceApi } from "@/entities/statistics/api/input/statistics-input-sewerage-finance-service";
import { statisticsApi } from "@/entities/statistics/api/statistics-service";
import { callImrServiceWithRollbackOnFailure } from "@/components/util/input/call-imr-with-rollback";
import type {
CauserBurdenItem,
CauserBurdenSaveItem,
@@ -74,11 +73,7 @@ import {
patchFooterVerificationByTab,
patchVerificationFailedByTab,
} from "@/components/util/input/verification-failed-flag";
import {
formatVerificationTooltip,
normalizeVerificationResultMap,
type VerificationResultLikeMap,
} from "@/components/util/input/verification-result-display";
import type { VerificationResultLikeMap } from "@/components/util/input/verification-result-display";
import { nullableTrimmedString } from "@/components/util/input/nullableString";
import { useAdminStore } from "@/store/admin-store";
import { useTabStore } from "@/store/tab-store";
@@ -179,9 +174,7 @@ function mapRegionalListToGridRows<
return filterBySelectedAdmdstCd(list, sidoListRef.current, values).map((item) => {
const { verificationResult, ...rest } = item;
const row = toGridRows([{ ctpvNm, ...rest }])[0] as GridRowWithVerification;
if (verificationResult) {
row.verificationResult = normalizeVerificationResultMap(verificationResult);
}
if (verificationResult) row.verificationResult = verificationResult;
return row;
});
}
@@ -947,13 +940,7 @@ const SewerageFinanceInp = () => {
};
try {
const imrResponse = await callImrServiceWithRollbackOnFailure(imrParam, {
statsYr: lastSearchedValues.year ?? "",
statsCd: statsCdForTab,
sewerBzmnCd: lastSearchedValues?.bsnmCd ?? "",
rollbackStatusCode: activeTabStatus?.lclgvSbmsnSttsCd ?? "",
mdfrUuid: userInfo.userUnqId,
});
const imrResponse = await statisticsApi.callImrService(imrParam);
if (imrResponse.job_id) {
await waitForImrJobCompleted(
imrResponse.job_id,
@@ -1228,7 +1215,18 @@ const SewerageFinanceInp = () => {
if (!verification) return;
const errors = verification[fieldName];
if (Array.isArray(errors) && errors.length > 0) {
return formatVerificationTooltip(errors);
return errors
.map((e) => {
const prefix =
e.vrfcCd === "IMR"
? "시계열 검증 - "
: e.vrfcCd === "NV"
? "일반검증 - "
: "모집단 검증 - ";
return `${prefix}${e.rsnExpln}(검증 기준값: ${e.cmprCn} / 현재 입력값: ${e.nowCn})`;
})
.filter(Boolean)
.join("\n");
}
};
@@ -10,7 +10,6 @@ import {
import { ConfirmDialog } from "@/shared/ui/confirm-dialog";
import { statisticsInputSewSupplyRateApi } from "@/entities/statistics/api/input/statistics-input-sew-supply-rate-service";
import { statisticsApi } from "@/entities/statistics/api/statistics-service";
import { callImrServiceWithRollbackOnFailure } from "@/components/util/input/call-imr-with-rollback";
import {
type CheckInputPeriodItem,
getVerificationPeriodActionBlockedMessage,
@@ -59,11 +58,7 @@ import { toGridRows } from "@/components/util/grid/gridFunction";
import { toExcelExportSearchContextFromSearchForm } from "@/components/util/gridExcelExport";
import { nullableTrimmedString } from "@/components/util/input/nullableString";
import { isVerificationFailedFromCommonInfo } from "@/components/util/input/verification-failed-flag";
import {
formatVerificationTooltip,
normalizeVerificationResultMap,
type VerificationResultLikeMap,
} from "@/components/util/input/verification-result-display";
import type { VerificationResultLikeMap } from "@/components/util/input/verification-result-display";
import { useAdminStore } from "@/store/admin-store";
import { useStatisticsInputFocusStore } from "@/store/statistics-input-focus-store";
import { useTabStore } from "@/store/tab-store";
@@ -92,7 +87,7 @@ const remapCommonInfoVerificationToGridFields = (
// API 키로도 조회 가능하도록 유지
if (gridField !== key) remapped[key] = errors;
}
return normalizeVerificationResultMap(remapped) ?? remapped;
return remapped;
};
/** 컬럼 가이드 API 조회용 통계코드 (하수도 보급률) */
@@ -307,11 +302,7 @@ const SeweragePenetrationRateInp = () => {
setRowData(
convertedData.map((d) => {
const row = toGridRows([d])[0] as GridRowWithVerification;
if (d.verificationResult) {
row.verificationResult = normalizeVerificationResultMap(
d.verificationResult,
);
}
if (d.verificationResult) row.verificationResult = d.verificationResult;
return row;
})
);
@@ -567,13 +558,7 @@ const SeweragePenetrationRateInp = () => {
};
try {
const imrResponse = await callImrServiceWithRollbackOnFailure(imrParam, {
statsYr: searchValues?.year ?? "",
statsCd,
sewerBzmnCd: searchValues?.bsnmCd ?? "",
rollbackStatusCode: statusInfo?.lclgvSbmsnSttsCd ?? "",
mdfrUuid: userInfo.userUnqId,
});
const imrResponse = await statisticsApi.callImrService(imrParam);
if (imrResponse.job_id) {
await waitForImrJobCompleted(imrResponse.job_id, (id) =>
statisticsApi.getImrStatus(id),
@@ -882,7 +867,14 @@ const SeweragePenetrationRateInp = () => {
if (!verification) return;
const errors = verification[fieldName];
if (Array.isArray(errors) && errors.length > 0) {
return formatVerificationTooltip(errors);
return errors
.map((e) => {
const prefix = e.vrfcCd === 'IMR' ? '시계열 검증 - ' : (e.vrfcCd === 'NV' ? '일반검증 - ' : '모집단 검증 -');
return `${prefix}${e.rsnExpln}
(검증 기준값: ${e.cmprCn} / 현재 입력값: ${e.nowCn})`;
})
.filter(Boolean)
.join('\n');
}
}}
/>
@@ -9,7 +9,6 @@ import { ConfirmDialog } from "@/shared/ui/confirm-dialog";
import { statisticsInputSewSupplyRateApi } from "@/entities/statistics/api/input/statistics-input-sew-supply-rate-service";
import { statisticsInputSewerageServiceLevelApi } from "@/entities/statistics/api/input/statistics-input-sewerage-service-level-service";
import { statisticsApi } from "@/entities/statistics/api/statistics-service";
import { callImrServiceWithRollbackOnFailure } from "@/components/util/input/call-imr-with-rollback";
import {
SEWERAGE_SERVICE_LEVEL_COMPLAINT_STATS_CD,
SEWERAGE_SERVICE_LEVEL_RESIDENT_FRIENDLY_FACT_STATS_CD,
@@ -64,11 +63,7 @@ import {
patchFooterVerificationByTab,
patchVerificationFailedByTab,
} from "@/components/util/input/verification-failed-flag";
import {
formatVerificationTooltip,
normalizeVerificationResultMap,
type VerificationResultLikeMap,
} from "@/components/util/input/verification-result-display";
import type { VerificationResultLikeMap } from "@/components/util/input/verification-result-display";
import { nullableTrimmedString } from "@/components/util/input/nullableString";
import { useAdminStore } from "@/store/admin-store";
import { useTabStore } from "@/store/tab-store";
@@ -192,9 +187,7 @@ function mapServiceLevelListToGridRows<
return filterBySelectedAdmdstCd(list, sidoListRef.current, values).map((item) => {
const { verificationResult, ...rest } = item;
const row = toGridRows([{ ctpvNm, ...rest }])[0] as GridRowWithVerification;
if (verificationResult) {
row.verificationResult = normalizeVerificationResultMap(verificationResult);
}
if (verificationResult) row.verificationResult = verificationResult;
return row;
});
}
@@ -846,13 +839,7 @@ const SewerageServiceLevelInp = () => {
};
try {
const imrResponse = await callImrServiceWithRollbackOnFailure(imrParam, {
statsYr: lastSearchedValues.year ?? "",
statsCd: statsCdForTab,
sewerBzmnCd: lastSearchedValues?.bsnmCd ?? "",
rollbackStatusCode: activeTabStatus?.lclgvSbmsnSttsCd ?? "",
mdfrUuid: userInfo.userUnqId,
});
const imrResponse = await statisticsApi.callImrService(imrParam);
if (imrResponse.job_id) {
await waitForImrJobCompleted(
imrResponse.job_id,
@@ -1159,7 +1146,18 @@ const SewerageServiceLevelInp = () => {
if (!verification) return;
const errors = verification[fieldName];
if (Array.isArray(errors) && errors.length > 0) {
return formatVerificationTooltip(errors);
return errors
.map((e) => {
const prefix =
e.vrfcCd === "IMR"
? "시계열 검증 - "
: e.vrfcCd === "NV"
? "일반검증 - "
: "모집단 검증 - ";
return `${prefix}${e.rsnExpln}(검증 기준값: ${e.cmprCn} / 현재 입력값: ${e.nowCn})`;
})
.filter(Boolean)
.join("\n");
}
};
@@ -20,8 +20,8 @@ const StatisticsReviewHistory = () => {
{ name: "ctpvNm", fieldName: "ctpvNm", header: { text: "시도" }, editable: false },
{ name: "sggNm", fieldName: "sggNm", header: { text: "시군구" }, editable: false },
{ name: "menuNm", fieldName: "menuNm", header: { text: "통계항목" }, editable: false },
{ name: "splmntOpnnCn", fieldName: "splmntOpnnCn", header: { text: "검토자의견" }, editable: false },
{ name: "rjctOpnnCn", fieldName: "rjctOpnnCn", header: { text: "반려의견" }, editable: false },
{ name: "chckerOpinionSm", fieldName: "chckerOpinionSm", header: { text: "검토자의견" }, editable: false },
{ name: "wrterOpinionSm", fieldName: "wrterOpinionSm", header: { text: "처리내역" }, editable: false },
];
const columnLayout = [
@@ -29,8 +29,8 @@ const StatisticsReviewHistory = () => {
"ctpvNm",
"sggNm",
"menuNm",
"splmntOpnnCn",
"rjctOpnnCn",
"chckerOpinionSm",
"wrterOpinionSm",
];
const handleSearch = async (values: SearchFormValues) => {
@@ -21,7 +21,6 @@ import { nullableTrimmedString } from "@/components/util/input/nullableString";
import { statisticsInputSewSupplyRateApi } from "@/entities/statistics/api/input/statistics-input-sew-supply-rate-service";
import { statisticsInputWaterReuseApi } from "@/entities/statistics/api/input/statistics-input-water-reuse-service";
import { statisticsApi } from "@/entities/statistics/api/statistics-service";
import { callImrServiceWithRollbackOnFailure } from "@/components/util/input/call-imr-with-rollback";
import {
type PublicSewageTreatWaterReuseItem,
type PublicSewageTreatWaterReuseSaveItem,
@@ -94,11 +93,7 @@ import {
patchFooterVerificationByTab,
patchVerificationFailedByTab,
} from "@/components/util/input/verification-failed-flag";
import {
formatVerificationTooltip,
normalizeVerificationResultMap,
type VerificationResultLikeMap,
} from "@/components/util/input/verification-result-display";
import type { VerificationResultLikeMap } from "@/components/util/input/verification-result-display";
import { useAdminStore } from "@/store/admin-store";
import { useTabStore } from "@/store/tab-store";
@@ -448,9 +443,7 @@ function mapPublicSewageTreatWaterReuseListToGridRows(
instlAprvYmd: formatCompactYmdForGrid(item.instlAprvYmd),
},
])[0] as GridRowWithVerification;
if (verificationResult) {
row.verificationResult = normalizeVerificationResultMap(verificationResult);
}
if (verificationResult) row.verificationResult = verificationResult;
return row;
});
}
@@ -469,9 +462,7 @@ function mapRecycledWaterStatusListToGridRows(
oprtngStrtYmd: formatCompactYmdForGrid(item.oprtngStrtYmd),
},
])[0] as GridRowWithVerification;
if (verificationResult) {
row.verificationResult = normalizeVerificationResultMap(verificationResult);
}
if (verificationResult) row.verificationResult = verificationResult;
return row;
});
}
@@ -489,9 +480,7 @@ function mapRainFallUtilFactStatusListToGridRows(
cmptnYmd: formatCompactYmdForGrid(item.cmptnYmd),
},
])[0] as GridRowWithVerification;
if (verificationResult) {
row.verificationResult = normalizeVerificationResultMap(verificationResult);
}
if (verificationResult) row.verificationResult = verificationResult;
return row;
});
}
@@ -508,9 +497,7 @@ function mapTreatWaterReuseStatusListToGridRows<
coRegYmd: formatCompactYmdForGrid(item.coRegYmd),
},
])[0] as GridRowWithVerification;
if (verificationResult) {
row.verificationResult = normalizeVerificationResultMap(verificationResult);
}
if (verificationResult) row.verificationResult = verificationResult;
return row;
});
}
@@ -1368,13 +1355,7 @@ const WaterReuseInp = () => {
};
try {
const imrResponse = await callImrServiceWithRollbackOnFailure(imrParam, {
statsYr: lastSearchedValues.year ?? "",
statsCd: statsCdForTab,
sewerBzmnCd: lastSearchedValues?.bsnmCd ?? "",
rollbackStatusCode: activeTabStatus?.lclgvSbmsnSttsCd ?? "",
mdfrUuid: userInfo.userUnqId,
});
const imrResponse = await statisticsApi.callImrService(imrParam);
if (imrResponse.job_id) {
await waitForImrJobCompleted(
imrResponse.job_id,
@@ -2082,7 +2063,18 @@ const WaterReuseInp = () => {
if (!verification) return;
const errors = verification[fieldName];
if (Array.isArray(errors) && errors.length > 0) {
return formatVerificationTooltip(errors);
return errors
.map((e) => {
const prefix =
e.vrfcCd === "IMR"
? "시계열 검증 - "
: e.vrfcCd === "NV"
? "일반검증 - "
: "모집단 검증 - ";
return `${prefix}${e.rsnExpln}(검증 기준값: ${e.cmprCn} / 현재 입력값: ${e.nowCn})`;
})
.filter(Boolean)
.join("\n");
}
};
@@ -49,6 +49,7 @@ const ExcretaTreatmentFacilityRvw = () => {
const [reviewOpinionContext, setReviewOpinionContext] =
useState<ReviewOpinionDialogContext | null>(null);
const [latestReviewOpinion, setLatestReviewOpinion] = useState("");
const [latestProcessHistory, setLatestProcessHistory] = useState("");
const [lastSearchValues, setLastSearchValues] = useState<SearchFormValues | null>(null);
const columnInfo = useMemo(() => {
@@ -115,6 +116,7 @@ const ExcretaTreatmentFacilityRvw = () => {
setStatsStatusInfo({});
setReviewOpinionContext(null);
setLatestReviewOpinion("");
setLatestProcessHistory("");
setLastSearchValues(null);
setReviewOpinionOpen(false);
setReturnDialogOpen(false);
@@ -168,12 +170,14 @@ const ExcretaTreatmentFacilityRvw = () => {
statsCd: statsInfo.statsCd ?? "",
});
setLatestReviewOpinion(statsInfo.splmntOpnnCn ?? "");
setLatestProcessHistory(statsInfo.splmntRsltCn ?? "");
setRowData(toGridRows(convertedData as unknown as object[]));
} else {
setRowData([]);
setStatsStatusInfo({});
setReviewOpinionContext(null);
setLatestReviewOpinion("");
setLatestProcessHistory("");
alert(response.message || "데이터가 없습니다.");
}
} catch {
@@ -212,6 +216,7 @@ const ExcretaTreatmentFacilityRvw = () => {
const row = detailRes.data?.[0];
setLatestReviewOpinion(row?.splmntOpnnCn ?? "");
setLatestProcessHistory(row?.splmntRsltCn ?? "");
setReviewOpinionOpen(true);
} catch {
alert("검토의견 조회 중 오류가 발생했습니다.");
@@ -229,8 +234,10 @@ const ExcretaTreatmentFacilityRvw = () => {
setStatsStatusInfo((prev) => ({
...prev,
splmntOpnnCn: param.splmntOpnnCn,
splmntRsltCn: param.splmntRsltCn,
}));
setLatestReviewOpinion(param.splmntOpnnCn);
setLatestProcessHistory(param.splmntRsltCn);
} catch {
alert("저장 중 오류가 발생했습니다.");
}
@@ -290,6 +297,7 @@ const ExcretaTreatmentFacilityRvw = () => {
onOpenChange={setReviewOpinionOpen}
context={reviewOpinionContext}
initialReviewOpinion={latestReviewOpinion}
initialProcessHistory={latestProcessHistory}
onSave={saveReviewOpinion}
/>
<ReviewSubmitDialog
@@ -102,6 +102,7 @@ const PrivateSewageFacilityRvw = () => {
}, [activeStatsStatusInfo.statsCd, lastSearchValues]);
const latestReviewOpinion = activeStatsStatusInfo.splmntOpnnCn ?? "";
const latestProcessHistory = activeStatsStatusInfo.splmntRsltCn ?? "";
const submitDialogContext = useMemo<ReviewerInfoRequest | null>(() => {
if (lastSearchValues == null) return null;
@@ -283,6 +284,7 @@ const PrivateSewageFacilityRvw = () => {
next.set(activeTab, {
...(next.get(activeTab) ?? {}),
splmntOpnnCn: row?.splmntOpnnCn ?? "",
splmntRsltCn: row?.splmntRsltCn ?? "",
});
return next;
});
@@ -305,6 +307,7 @@ const PrivateSewageFacilityRvw = () => {
next.set(activeTab, {
...(next.get(activeTab) ?? {}),
splmntOpnnCn: param.splmntOpnnCn,
splmntRsltCn: param.splmntRsltCn,
});
return next;
});
@@ -408,6 +411,7 @@ const PrivateSewageFacilityRvw = () => {
onOpenChange={setReviewOpinionOpen}
context={reviewOpinionContext}
initialReviewOpinion={latestReviewOpinion}
initialProcessHistory={latestProcessHistory}
onSave={saveReviewOpinion}
/>
<ReviewSubmitDialog
@@ -41,6 +41,7 @@ const PublicSewageFacilityRvw = () => {
const [reviewOpinionContext, setReviewOpinionContext] =
useState<ReviewOpinionDialogContext | null>(null);
const [latestReviewOpinion, setLatestReviewOpinion] = useState("");
const [latestProcessHistory, setLatestProcessHistory] = useState("");
const [lastSearchValues, setLastSearchValues] = useState<SearchFormValues | null>(null);
const columnInfo = useMemo(() => {
@@ -99,6 +100,7 @@ const PublicSewageFacilityRvw = () => {
setStatsStatusInfo({});
setReviewOpinionContext(null);
setLatestReviewOpinion("");
setLatestProcessHistory("");
setLastSearchValues(null);
setSubmitDialogOpen(false);
};
@@ -141,12 +143,16 @@ const PublicSewageFacilityRvw = () => {
setLatestReviewOpinion(
response.data.statsStatusInfo.splmntOpnnCn ?? "",
);
setLatestProcessHistory(
response.data.statsStatusInfo.splmntRsltCn ?? "",
);
setRowData(toGridRows(convertedData as unknown as object[]));
} else {
setRowData([]);
setStatsStatusInfo({});
setReviewOpinionContext(null);
setLatestReviewOpinion("");
setLatestProcessHistory("");
alert(response.message || "데이터가 없습니다.");
}
} catch (error) {
@@ -186,6 +192,7 @@ const PublicSewageFacilityRvw = () => {
const row = detailRes.data?.[0];
setLatestReviewOpinion(row?.splmntOpnnCn ?? "");
setLatestProcessHistory(row?.splmntRsltCn ?? "");
setReviewOpinionOpen(true);
} catch {
alert("검토의견 조회 중 오류가 발생했습니다.");
@@ -203,8 +210,10 @@ const PublicSewageFacilityRvw = () => {
setStatsStatusInfo((prev) => ({
...prev,
splmntOpnnCn: param.splmntOpnnCn,
splmntRsltCn: param.splmntRsltCn,
}));
setLatestReviewOpinion(param.splmntOpnnCn);
setLatestProcessHistory(param.splmntRsltCn);
} catch {
alert("저장 중 오류가 발생했습니다.");
}
@@ -258,6 +267,7 @@ const PublicSewageFacilityRvw = () => {
onOpenChange={setReviewOpinionOpen}
context={reviewOpinionContext}
initialReviewOpinion={latestReviewOpinion}
initialProcessHistory={latestProcessHistory}
onSave={saveReviewOpinion}
/>
<ReviewSubmitDialog
@@ -47,6 +47,7 @@ const PumpingStationRvw = () => {
const [reviewOpinionContext, setReviewOpinionContext] =
useState<ReviewOpinionDialogContext | null>(null);
const [latestReviewOpinion, setLatestReviewOpinion] = useState("");
const [latestProcessHistory, setLatestProcessHistory] = useState("");
const [lastSearchValues, setLastSearchValues] = useState<SearchFormValues | null>(null);
const columnInfo = useMemo(() => {
@@ -110,6 +111,7 @@ const PumpingStationRvw = () => {
setStatsStatusInfo({});
setReviewOpinionContext(null);
setLatestReviewOpinion("");
setLatestProcessHistory("");
setLastSearchValues(null);
setReviewOpinionOpen(false);
setReturnDialogOpen(false);
@@ -155,12 +157,14 @@ const PumpingStationRvw = () => {
statsCd: statsInfo.statsCd ?? "",
});
setLatestReviewOpinion(statsInfo.splmntOpnnCn ?? "");
setLatestProcessHistory(statsInfo.splmntRsltCn ?? "");
setRowData(toGridRows(convertedData));
} else {
setRowData([]);
setStatsStatusInfo({});
setReviewOpinionContext(null);
setLatestReviewOpinion("");
setLatestProcessHistory("");
alert(response.message || "데이터가 없습니다.");
}
} catch {
@@ -199,6 +203,7 @@ const PumpingStationRvw = () => {
const row = detailRes.data?.[0];
setLatestReviewOpinion(row?.splmntOpnnCn ?? "");
setLatestProcessHistory(row?.splmntRsltCn ?? "");
setReviewOpinionOpen(true);
} catch {
alert("검토의견 조회 중 오류가 발생했습니다.");
@@ -216,8 +221,10 @@ const PumpingStationRvw = () => {
setStatsStatusInfo((prev) => ({
...prev,
splmntOpnnCn: param.splmntOpnnCn,
splmntRsltCn: param.splmntRsltCn,
}));
setLatestReviewOpinion(param.splmntOpnnCn);
setLatestProcessHistory(param.splmntRsltCn);
} catch {
alert("저장 중 오류가 발생했습니다.");
}
@@ -277,6 +284,7 @@ const PumpingStationRvw = () => {
onOpenChange={setReviewOpinionOpen}
context={reviewOpinionContext}
initialReviewOpinion={latestReviewOpinion}
initialProcessHistory={latestProcessHistory}
onSave={saveReviewOpinion}
/>
<ReviewSubmitDialog
@@ -30,6 +30,8 @@ export interface ReviewOpinionDialogProps {
context: ReviewOpinionDialogContext | null;
/** 다이얼로그 열릴 때 검토의견 초기값 */
initialReviewOpinion?: string;
/** 다이얼로그 열릴 때 처리내역 초기값 */
initialProcessHistory?: string;
/** 저장 시 생성된 param 전달 (API 호출 등) */
onSave?: (param: ReviewOpinionSaveRequest) => void | Promise<void>;
}
@@ -39,15 +41,18 @@ export function ReviewOpinionDialog({
onOpenChange,
context,
initialReviewOpinion = "",
initialProcessHistory = "",
onSave,
}: ReviewOpinionDialogProps) {
const { setMessage: alert } = useAlertStore((state) => state);
const [reviewOpinion, setReviewOpinion] = useState("");
const [processHistory, setProcessHistory] = useState("");
const [isSaving, setIsSaving] = useState(false);
const [reviewOpinionError, setReviewOpinionError] = useState(false);
const resetForm = () => {
setReviewOpinion("");
setProcessHistory("");
setReviewOpinionError(false);
};
@@ -59,7 +64,8 @@ export function ReviewOpinionDialog({
useEffect(() => {
if (!open) return;
setReviewOpinion(initialReviewOpinion);
}, [open, initialReviewOpinion]);
setProcessHistory(initialProcessHistory);
}, [open, initialReviewOpinion, initialProcessHistory]);
const handleSave = async () => {
if (reviewOpinion.trim() === "") {
@@ -90,6 +96,7 @@ export function ReviewOpinionDialog({
sewerBzmnCd: context.sewerBzmnCd,
statsCd: context.statsCd,
splmntOpnnCn: reviewOpinion,
splmntRsltCn: processHistory,
mdfrUuid: userInfo.userUnqId,
};
await onSave?.(param);
@@ -129,6 +136,16 @@ export function ReviewOpinionDialog({
</p>
)}
</div>
<div className="flex flex-col gap-2">
<Label className="text-label mb-2">처리내역</Label>
<Textarea
value={processHistory}
onChange={(e) => setProcessHistory(e.target.value)}
className="min-h-[10rem] resize-y !text-[1.3rem] !leading-6"
placeholder="처리내역을 입력하세요."
disabled={isSaving}
/>
</div>
</div>
<DialogFooter>
<DialogClose asChild>
@@ -30,8 +30,8 @@ export interface ReviewSubmitDialogProps {
context: ReviewerInfoRequest | null;
onSubmitted?: () => void | Promise<void>;
/**
* true면 검토의견 UI를 숨기고
* submit body에 statsCd / splmntOpnnCn을 넣지 않음
* true면 검토의견·처리내역 UI를 숨기고
* submit body에 statsCd / splmntOpnnCn / splmntRsltCn을 넣지 않음
*/
hideOpinionFields?: boolean;
}
@@ -116,6 +116,7 @@ export function ReviewSubmitDialog({
statsCd: context.statsCd,
mdfrUuid: userInfo.userUnqId,
splmntOpnnCn: reviewerInfo?.splmntOpnnCn ?? "",
splmntRsltCn: reviewerInfo?.splmntRsltCn ?? "",
};
const response = await statisticsApi.submitReview(submitParam);
@@ -174,6 +175,14 @@ export function ReviewSubmitDialog({
className="min-h-[10rem] p-2 resize-none !text-xl"
/>
</div>
<div className="col-span-2 flex flex-col gap-2">
<Label className="text-label">처리내역</Label>
<Textarea
value={reviewerInfo?.splmntRsltCn ?? "-"}
readOnly
className="min-h-[10rem] p-2 resize-none !text-xl"
/>
</div>
</>
)}
</div>
@@ -105,6 +105,7 @@ const SewageExcretaRelatedRvw = () => {
}, [activeStatsStatusInfo.statsCd, lastSearchValues]);
const latestReviewOpinion = activeStatsStatusInfo.splmntOpnnCn ?? "";
const latestProcessHistory = activeStatsStatusInfo.splmntRsltCn ?? "";
const submitDialogContext = useMemo<ReviewerInfoRequest | null>(() => {
if (lastSearchValues == null) return null;
@@ -288,6 +289,7 @@ const SewageExcretaRelatedRvw = () => {
next.set(activeTab, {
...(next.get(activeTab) ?? {}),
splmntOpnnCn: row?.splmntOpnnCn ?? "",
splmntRsltCn: row?.splmntRsltCn ?? "",
});
return next;
});
@@ -310,6 +312,7 @@ const SewageExcretaRelatedRvw = () => {
next.set(activeTab, {
...(next.get(activeTab) ?? {}),
splmntOpnnCn: param.splmntOpnnCn,
splmntRsltCn: param.splmntRsltCn,
});
return next;
});
@@ -413,6 +416,7 @@ const SewageExcretaRelatedRvw = () => {
onOpenChange={setReviewOpinionOpen}
context={reviewOpinionContext}
initialReviewOpinion={latestReviewOpinion}
initialProcessHistory={latestProcessHistory}
onSave={saveReviewOpinion}
/>
<ReviewSubmitDialog
@@ -105,6 +105,7 @@ const SewageExcretaSlurgeRvw = () => {
}, [activeStatsStatusInfo.statsCd, lastSearchValues]);
const latestReviewOpinion = activeStatsStatusInfo.splmntOpnnCn ?? "";
const latestProcessHistory = activeStatsStatusInfo.splmntRsltCn ?? "";
const submitDialogContext = useMemo<ReviewerInfoRequest | null>(() => {
if (lastSearchValues == null) return null;
@@ -289,6 +290,7 @@ const SewageExcretaSlurgeRvw = () => {
next.set(activeTab, {
...(next.get(activeTab) ?? {}),
splmntOpnnCn: row?.splmntOpnnCn ?? "",
splmntRsltCn: row?.splmntRsltCn ?? "",
});
return next;
});
@@ -311,6 +313,7 @@ const SewageExcretaSlurgeRvw = () => {
next.set(activeTab, {
...(next.get(activeTab) ?? {}),
splmntOpnnCn: param.splmntOpnnCn,
splmntRsltCn: param.splmntRsltCn,
});
return next;
});
@@ -414,6 +417,7 @@ const SewageExcretaSlurgeRvw = () => {
onOpenChange={setReviewOpinionOpen}
context={reviewOpinionContext}
initialReviewOpinion={latestReviewOpinion}
initialProcessHistory={latestProcessHistory}
onSave={saveReviewOpinion}
/>
<ReviewSubmitDialog
@@ -120,6 +120,7 @@ const SewagePipeMaintainRvw = () => {
}, [activeStatsStatusInfo.statsCd, lastSearchValues]);
const latestReviewOpinion = activeStatsStatusInfo.splmntOpnnCn ?? "";
const latestProcessHistory = activeStatsStatusInfo.splmntRsltCn ?? "";
const submitDialogContext = useMemo<ReviewerInfoRequest | null>(() => {
if (lastSearchValues == null) return null;
@@ -301,6 +302,7 @@ const SewagePipeMaintainRvw = () => {
next.set(activeTab, {
...(next.get(activeTab) ?? {}),
splmntOpnnCn: row?.splmntOpnnCn ?? "",
splmntRsltCn: row?.splmntRsltCn ?? "",
});
return next;
});
@@ -323,6 +325,7 @@ const SewagePipeMaintainRvw = () => {
next.set(activeTab, {
...(next.get(activeTab) ?? {}),
splmntOpnnCn: param.splmntOpnnCn,
splmntRsltCn: param.splmntRsltCn,
});
return next;
});
@@ -426,6 +429,7 @@ const SewagePipeMaintainRvw = () => {
onOpenChange={setReviewOpinionOpen}
context={reviewOpinionContext}
initialReviewOpinion={latestReviewOpinion}
initialProcessHistory={latestProcessHistory}
onSave={saveReviewOpinion}
/>
<ReviewSubmitDialog
@@ -181,6 +181,7 @@ const SewagePipeRvw = () => {
};
}, [activeStatsStatusInfo.statsCd, lastSearchValues]);
const latestReviewOpinion = activeStatsStatusInfo.splmntOpnnCn ?? "";
const latestProcessHistory = activeStatsStatusInfo.splmntRsltCn ?? "";
const submitDialogContext = useMemo<ReviewerInfoRequest | null>(() => {
if (lastSearchValues == null) return null;
@@ -361,6 +362,7 @@ const SewagePipeRvw = () => {
next.set(activeTab, {
...(next.get(activeTab) ?? {}),
splmntOpnnCn: row?.splmntOpnnCn ?? "",
splmntRsltCn: row?.splmntRsltCn ?? "",
});
return next;
});
@@ -383,6 +385,7 @@ const SewagePipeRvw = () => {
next.set(activeTab, {
...(next.get(activeTab) ?? {}),
splmntOpnnCn: param.splmntOpnnCn,
splmntRsltCn: param.splmntRsltCn,
});
return next;
});
@@ -485,6 +488,7 @@ const SewagePipeRvw = () => {
onOpenChange={setReviewOpinionOpen}
context={reviewOpinionContext}
initialReviewOpinion={latestReviewOpinion}
initialProcessHistory={latestProcessHistory}
onSave={saveReviewOpinion}
/>
<ReviewSubmitDialog
@@ -126,6 +126,7 @@ const SewageReservoirPumpRvw = () => {
}, [activeStatsCd, lastSearchValues]);
const latestReviewOpinion = activeStatsStatusInfo.splmntOpnnCn ?? "";
const latestProcessHistory = activeStatsStatusInfo.splmntRsltCn ?? "";
const submitDialogContext = useMemo<ReviewerInfoRequest | null>(() => {
if (lastSearchValues == null) return null;
@@ -307,6 +308,7 @@ const SewageReservoirPumpRvw = () => {
next.set(activeTab, {
...(next.get(activeTab) ?? {}),
splmntOpnnCn: row?.splmntOpnnCn ?? "",
splmntRsltCn: row?.splmntRsltCn ?? "",
});
return next;
});
@@ -329,6 +331,7 @@ const SewageReservoirPumpRvw = () => {
next.set(activeTab, {
...(next.get(activeTab) ?? {}),
splmntOpnnCn: param.splmntOpnnCn,
splmntRsltCn: param.splmntRsltCn,
});
return next;
});
@@ -432,6 +435,7 @@ const SewageReservoirPumpRvw = () => {
onOpenChange={setReviewOpinionOpen}
context={reviewOpinionContext}
initialReviewOpinion={latestReviewOpinion}
initialProcessHistory={latestProcessHistory}
onSave={saveReviewOpinion}
/>
<ReviewSubmitDialog
@@ -105,6 +105,7 @@ const SewerageFinanceRvw = () => {
}, [activeStatsStatusInfo.statsCd, lastSearchValues]);
const latestReviewOpinion = activeStatsStatusInfo.splmntOpnnCn ?? "";
const latestProcessHistory = activeStatsStatusInfo.splmntRsltCn ?? "";
const submitDialogContext = useMemo<ReviewerInfoRequest | null>(() => {
if (lastSearchValues == null) return null;
@@ -286,6 +287,7 @@ const SewerageFinanceRvw = () => {
next.set(activeTab, {
...(next.get(activeTab) ?? {}),
splmntOpnnCn: row?.splmntOpnnCn ?? "",
splmntRsltCn: row?.splmntRsltCn ?? "",
});
return next;
});
@@ -308,6 +310,7 @@ const SewerageFinanceRvw = () => {
next.set(activeTab, {
...(next.get(activeTab) ?? {}),
splmntOpnnCn: param.splmntOpnnCn,
splmntRsltCn: param.splmntRsltCn,
});
return next;
});
@@ -411,6 +414,7 @@ const SewerageFinanceRvw = () => {
onOpenChange={setReviewOpinionOpen}
context={reviewOpinionContext}
initialReviewOpinion={latestReviewOpinion}
initialProcessHistory={latestProcessHistory}
onSave={saveReviewOpinion}
/>
<ReviewSubmitDialog
@@ -40,6 +40,7 @@ const SeweragePenetrationRateRvw = () => {
const [reviewOpinionContext, setReviewOpinionContext] =
useState<ReviewOpinionDialogContext | null>(null);
const [latestReviewOpinion, setLatestReviewOpinion] = useState("");
const [latestProcessHistory, setLatestProcessHistory] = useState("");
const [lastSearchValues, setLastSearchValues] = useState<SearchFormValues | null>(null);
const columnInfo = useMemo(() => {
@@ -99,6 +100,7 @@ const SeweragePenetrationRateRvw = () => {
setStatsStatusInfo({});
setReviewOpinionContext(null);
setLatestReviewOpinion("");
setLatestProcessHistory("");
setLastSearchValues(null);
};
@@ -137,11 +139,15 @@ const SeweragePenetrationRateRvw = () => {
setLatestReviewOpinion(
response.data.statsStatusInfo.splmntOpnnCn ?? "",
);
setLatestProcessHistory(
response.data.statsStatusInfo.splmntRsltCn ?? "",
);
setRowData(toGridRows(convertedData));
} else {
setRowData([]);
setReviewOpinionContext(null);
setLatestReviewOpinion("");
setLatestProcessHistory("");
alert(response.message || "데이터가 없습니다.");
}
} catch (error) {
@@ -181,6 +187,7 @@ const SeweragePenetrationRateRvw = () => {
const row = detailRes.data?.[0];
setLatestReviewOpinion(row?.splmntOpnnCn ?? "");
setLatestProcessHistory(row?.splmntRsltCn ?? "");
setReviewOpinionOpen(true);
} catch {
alert("검토의견 조회 중 오류가 발생했습니다.");
@@ -198,8 +205,10 @@ const SeweragePenetrationRateRvw = () => {
setStatsStatusInfo((prev) => ({
...prev,
splmntOpnnCn: param.splmntOpnnCn,
splmntRsltCn: param.splmntRsltCn,
}));
setLatestReviewOpinion(param.splmntOpnnCn);
setLatestProcessHistory(param.splmntRsltCn);
} catch {
alert("저장 중 오류가 발생했습니다.");
}
@@ -255,6 +264,7 @@ const SeweragePenetrationRateRvw = () => {
onOpenChange={setReviewOpinionOpen}
context={reviewOpinionContext}
initialReviewOpinion={latestReviewOpinion}
initialProcessHistory={latestProcessHistory}
onSave={saveReviewOpinion}
/>
<ReviewSubmitDialog
@@ -103,6 +103,7 @@ const SewerageServiceLevelRvw = () => {
}, [activeStatsStatusInfo.statsCd, lastSearchValues]);
const latestReviewOpinion = activeStatsStatusInfo.splmntOpnnCn ?? "";
const latestProcessHistory = activeStatsStatusInfo.splmntRsltCn ?? "";
const submitDialogContext = useMemo<ReviewerInfoRequest | null>(() => {
if (lastSearchValues == null) return null;
@@ -287,6 +288,7 @@ const SewerageServiceLevelRvw = () => {
next.set(activeTab, {
...(next.get(activeTab) ?? {}),
splmntOpnnCn: row?.splmntOpnnCn ?? "",
splmntRsltCn: row?.splmntRsltCn ?? "",
});
return next;
});
@@ -309,6 +311,7 @@ const SewerageServiceLevelRvw = () => {
next.set(activeTab, {
...(next.get(activeTab) ?? {}),
splmntOpnnCn: param.splmntOpnnCn,
splmntRsltCn: param.splmntRsltCn,
});
return next;
});
@@ -412,6 +415,7 @@ const SewerageServiceLevelRvw = () => {
onOpenChange={setReviewOpinionOpen}
context={reviewOpinionContext}
initialReviewOpinion={latestReviewOpinion}
initialProcessHistory={latestProcessHistory}
onSave={saveReviewOpinion}
/>
<ReviewSubmitDialog
@@ -39,14 +39,22 @@ import { toGridRows } from "@/components/util/grid/gridFunction";
type ReviewHistoryRow = ReviewHistoryResponse["data"][number];
const checkPrvnStatusCd = (code: string) => {
return (code === '0' || code === '1') ? '미작성' : (code === '2' ? '제출완료' : (code === '3' ? '승인완료' : '반려'));
};
const checkSubmitStatusCd = (code: string) => {
return code === '0' ? '미작성' : (code === '1' ? '작성중' : (code === '2' ? '제출완료' : (code === '3' ? '승인완료' : '보완요청')));
};
const isStatsInputStatusRowApprovable = (row: Record<string, string | number>) =>
String(row.lclgvSbmsnSttsCd ?? "") === "3" &&
(String(row.mctpvPicSbmsnSttsCd ?? "") === "2" ||
String(row.mctpvPicSbmsnSttsCd ?? "") === "3");
String(row.submitStatusCd ?? "") === "3" && (String(row.prvnStatusCd ?? "") === "2" || String(row.prvnStatusCd ?? "") === "3");
const mapSewerageListToGridRows = (rawList: StatsInputStatusReviewItem[]) =>
rawList.map((r) => ({
...r,
prvnStatusNm: checkPrvnStatusCd(r.prvnStatusCd || "0"),
submitStatusNm: checkSubmitStatusCd(r.submitStatusCd || "0"),
reviewHistory: "검토이력",
}));
@@ -89,7 +97,7 @@ const StatsInputStatusRvw = () => {
const reviewHistoryColumns = useMemo(() => {
const col = createColumnHelper<ReviewHistoryRow>();
return [
col.accessor("regDt", {
col.accessor("logDt", {
header: "등록일",
cell: (info) => info.getValue(),
}),
@@ -230,23 +238,23 @@ const StatsInputStatusRvw = () => {
const allSubmitStatusApproved = useMemo(
() =>
rowData.length > 0 &&
rowData.every((row) => String(row.lclgvSbmsnSttsCd ?? "") === "3"),
rowData.every((row) => String(row.submitStatusCd ?? "") === "3"),
[rowData],
);
/** 진행상태(광역) 코드가 모두 승인완료(3) */
/** 진행상태(prvn) 코드가 모두 승인완료(3) */
const allPrvnStatusApproved = useMemo(
() =>
rowData.length > 0 &&
rowData.every((row) => String(row.mctpvPicSbmsnSttsCd ?? "") === "3"),
rowData.every((row) => String(row.prvnStatusCd ?? "") === "3"),
[rowData],
);
/** 진행상태(광역) 코드가 모두 제출완료(2) */
/** 진행상태(prvn) 코드가 모두 제출완료(2) */
const allPrvnStatusSubmitted = useMemo(
() =>
rowData.length > 0 &&
rowData.every((row) => String(row.mctpvPicSbmsnSttsCd ?? "") === "2"),
rowData.every((row) => String(row.prvnStatusCd ?? "") === "2"),
[rowData],
);
@@ -384,8 +392,8 @@ const StatsInputStatusRvw = () => {
setRowData={setRowData}
onDataCellClick={handleReviewHistoryCellClick}
cellStyleCallback={({ row, fieldName }) => {
if (fieldName === "lclgvSbmsnSttsNm") {
const statusCd = String(row.lclgvSbmsnSttsCd ?? "");
if (fieldName === "submitStatusNm") {
const statusCd = String(row.submitStatusCd ?? "");
if (statusCd === "0" || statusCd === "1") {
return "rg-cell-stats-status-pending";
@@ -400,8 +408,8 @@ const StatsInputStatusRvw = () => {
return "rg-cell-stats-status-rejected";
}
return undefined;
} else if (fieldName === "mctpvPicSbmsnSttsNm") {
const statusCd = String(row.mctpvPicSbmsnSttsCd ?? "");
} else if (fieldName === "prvnStatusNm") {
const statusCd = String(row.prvnStatusCd ?? "");
if (statusCd === "0" || statusCd === "1") {
return "rg-cell-stats-status-pending";
@@ -108,6 +108,7 @@ const WaterReuseRvw = () => {
}, [activeStatsStatusInfo.statsCd, lastSearchValues]);
const latestReviewOpinion = activeStatsStatusInfo.splmntOpnnCn ?? "";
const latestProcessHistory = activeStatsStatusInfo.splmntRsltCn ?? "";
const submitDialogContext = useMemo<ReviewerInfoRequest | null>(() => {
if (lastSearchValues == null) return null;
@@ -290,6 +291,7 @@ const WaterReuseRvw = () => {
next.set(activeTab, {
...(next.get(activeTab) ?? {}),
splmntOpnnCn: row?.splmntOpnnCn ?? "",
splmntRsltCn: row?.splmntRsltCn ?? "",
});
return next;
});
@@ -312,6 +314,7 @@ const WaterReuseRvw = () => {
next.set(activeTab, {
...(next.get(activeTab) ?? {}),
splmntOpnnCn: param.splmntOpnnCn,
splmntRsltCn: param.splmntRsltCn,
});
return next;
});
@@ -415,6 +418,7 @@ const WaterReuseRvw = () => {
onOpenChange={setReviewOpinionOpen}
context={reviewOpinionContext}
initialReviewOpinion={latestReviewOpinion}
initialProcessHistory={latestProcessHistory}
onSave={saveReviewOpinion}
/>
<ReviewSubmitDialog
@@ -554,20 +554,17 @@ const DetailComponent = ({
const userInfo = await getUserInfo();
let response;
const vrfcSn = detailInfo?.commonInfo.vrfcSn;
if (vrfcSn == null) {
alert("검증 일련번호가 없습니다.");
return;
}
if (!hasPersistedDetails) {
response =
await generalverificationApiService.RegistVrfcDetail(vrfcSn, {
await generalverificationApiService.RegistVrfcDetail({
vrfcSn: detailInfo?.commonInfo.vrfcSn ?? null,
detailList: rows,
rgtrUuid: userInfo.userUnqId,
});
} else {
response =
await generalverificationApiService.UpdateVrfcDetail(vrfcSn, {
await generalverificationApiService.UpdateVrfcDetail({
vrfcSn: detailInfo?.commonInfo.vrfcSn ?? null,
detailList: rows,
mdfrUuid: userInfo.userUnqId,
});
@@ -146,15 +146,9 @@ const RegistDialog = memo(
}
};
const handleUpdateVrfcMaster = async (
updateVrfcSn: number,
body: UpdateVrfcMasterRequest,
) => {
const handleUpdateVrfcMaster = async (data: GeneralVerificationMasterSubmitFormData) => {
try {
const response = await generalverificationApiService.UpdateVrfcMaster(
updateVrfcSn,
body,
);
const response = await generalverificationApiService.UpdateVrfcMaster(data as UpdateVrfcMasterRequest);
if (response.status === 'success') {
alert('수정되었습니다.');
@@ -264,11 +258,8 @@ const RegistDialog = memo(
if (await confirm(confirmMessage)) {
const userInfo = await getUserInfo();
if (isVrfcSn) {
if (data.vrfcSn == null) {
alert('수정 대상이 없습니다.');
return;
}
await handleUpdateVrfcMaster(data.vrfcSn, {
const updateData = {
vrfcSn: data.vrfcSn,
vrfcNm: data.vrfcNm,
vrfcClsfCd: data.vrfcClsfCd,
dcptDgt: data.dcptDgt,
@@ -276,8 +267,10 @@ const RegistDialog = memo(
...(data.dcptDgt === 0
? {}
: { dcptPrcsMthCd: data.dcptPrcsMthCd }),
trgtStatsCd: data.trgtStatsCd,
mdfrUuid: userInfo.userUnqId,
});
};
await handleUpdateVrfcMaster(updateData);
} else {
const registData = {
vrfcNm: data.vrfcNm,
@@ -481,11 +481,6 @@ export type DataGridProps = {
headerHeight?: number;
/** RealGrid `displayOptions.fitStyle` (기본 EVEN). 컬럼별 width 유지 시 NONE */
fitStyle?: GridFitStyle;
/**
* true면 붙여넣기·편집으로 행을 추가하지 않음.
* 검증 결과처럼 조회 행 수가 고정된 그리드에서 사용.
*/
preventRowAppend?: boolean;
};
export type TabDataGridProps = {
@@ -8,7 +8,6 @@ export const doList = [
"4600000000",
"4700000000",
"4800000000",
"5000000000",
];
export const needBsnmFrgnrPoplSmSidoList = [
@@ -27,6 +27,7 @@ export const ExcretaSlurgeProcessingStatusInpColumn = {
'rcycQnt',
'olandLfvlm',
'incnrQnt',
'oceanInputQnt',
'fuelQnt',
'etcQnt',
'unprcsQnt'
@@ -50,6 +51,7 @@ export const ExcretaSlurgeProcessingStatusInpColumn = {
'rcycQnt',
'olandLfvlm',
'incnrQnt',
'oceanInputQnt',
'fuelQnt',
'etcQnt',
],
@@ -59,6 +61,7 @@ export const ExcretaSlurgeProcessingStatusInpColumn = {
{ name: 'rcycQnt', fieldName: 'rcycQnt', header: { text: '재활용' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'olandLfvlm', fieldName: 'olandLfvlm', header: { text: '육상매립' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'incnrQnt', fieldName: 'incnrQnt', header: { text: '소각' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'oceanInputQnt', fieldName: 'oceanInputQnt', header: { text: '해양투기' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'fuelQnt', fieldName: 'fuelQnt', header: { text: '연료화' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'etcQnt', fieldName: 'etcQnt', header: { text: '기타' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
@@ -97,6 +100,7 @@ export const ExcretaSlurgeProcessingStatusInpColumn = {
'rcycQnt',
'olandLfvlm',
'incnrQnt',
'oceanInputQnt',
'fuelQnt',
'etcQnt'
]
@@ -49,43 +49,6 @@ export const ManholeStatusInpColumn = {
styleName: "rg-cell-readonly",
footer: { text: "합계" },
},
{
name: "wholMnhlCntSum",
fieldName: "wholMnhlCntSum",
header: { text: "맨홀총계" },
footer: footerInt,
editable: false,
valueType: ValueType.NUMBER,
dataFormat: "#,##0",
styleName: "rg-cell-readonly",
},
{
name: "frlvlMnhlCntSum",
fieldName: "frlvlMnhlCntSum",
header: { text: "주철계" },
footer: footerInt,
editable: false,
valueType: ValueType.NUMBER,
dataFormat: "#,##0",
styleName: "rg-cell-readonly",
},
{
name: "thlvlMnhlCntSum",
fieldName: "thlvlMnhlCntSum",
header: { text: "콘크리트계" },
footer: footerInt,
editable: false,
valueType: ValueType.NUMBER,
dataFormat: "#,##0",
styleName: "rg-cell-readonly",
},
{
name: "picNm",
fieldName: "picNm",
header: { text: "담당자명" },
editable: false,
styleName: "rg-cell-readonly",
},
{
...intCol("wholMnhlCnt", "계", false),
computed: {
@@ -125,12 +88,6 @@ export const ManholeStatusInpColumn = {
},
"admdstNm",
"emphsMngSeNm",
{
name: "adminRefGroup",
hideChildHeaders: false,
header: { text: "행정참고자료" },
items: ["wholMnhlCntSum", "frlvlMnhlCntSum", "thlvlMnhlCntSum", "picNm"],
},
{
name: "mtrlGroup",
hideChildHeaders: false,
@@ -75,8 +75,6 @@ export const PublicSewageTreatWaterReuseInpColumn = {
name: 'lglYn',
fieldName: 'lglYn',
header: { text: '법적의무대상(대상/미대상)' },
editable: false,
styleName: 'rg-cell-readonly'
},
PUBLIC_SEWAGE_TREAT_WATER_REUSE_LGL_YN_VALUES,
),
@@ -32,11 +32,11 @@ export const getGeneralVerificationColumns = (
},
{
accessorKey: "vrfcClsfNm",
header: "검증 분류",
header: "검증분류",
},
{
accessorKey: "useYn",
header: "사용 여부",
header: "사용여부",
},
{
accessorKey: "regDt",
@@ -346,6 +346,7 @@ const rawSludgeList = [
decimal1Col("rcycQnt", "재활용"),
decimal1Col("olandLfvlm", "육상매립"),
decimal1Col("incnrQnt", "소각"),
decimal1Col("oceanInputQnt", "해양투기"),
decimal1Col("fuelQnt", "연료화"),
decimal1Col("etcQnt", "기타"),
decimal1Col("unprcsQnt", "미처분량\n(이월량)(C)"),
@@ -365,6 +366,7 @@ const rawSludgeList = [
"rcycQnt",
"olandLfvlm",
"incnrQnt",
"oceanInputQnt",
"fuelQnt",
"etcQnt",
],
@@ -491,10 +491,6 @@ const rawList = [
{ name: 'sggNm', fieldName: 'sggNm', header: { text: '시군구' }, mergeCells: true, sortdisable: true },
{ name: 'admdstNm', fieldName: 'admdstNm', header: { text: '행정구역명' }, mergeCells: true, sortdisable: true },
{ name: 'emphsMngSeNm', fieldName: 'emphsMngSeNm', header: { text: '집중강우\n중점관리\n구역구분' }, mergeCells: true },
{ name: 'wholMnhlCntSum', fieldName: 'wholMnhlCntSum', header: { text: '맨홀총계' }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'frlvlMnhlCntSum', fieldName: 'frlvlMnhlCntSum', header: { text: '주철계' }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'thlvlMnhlCntSum', fieldName: 'thlvlMnhlCntSum', header: { text: '콘크리트계' }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'picNm', fieldName: 'picNm', header: { text: '담당자명' } },
{
name: 'displayYear',
fieldName: 'displayYear',
@@ -548,12 +544,6 @@ const rawList = [
],
},
'emphsMngSeNm',
{
name: 'adminRefGroup',
hideChildHeaders: false,
header: { text: '행정참고자료' },
items: ['wholMnhlCntSum', 'frlvlMnhlCntSum', 'thlvlMnhlCntSum', 'picNm'],
},
'displayYear',
{
name: 'mtrlGroup',
@@ -17,12 +17,12 @@ const StatsInputStatusInfoColumnRaw = {
styleName: 'rg-cell-outline-action-btn',
},
{ name: 'lclgvPicNm', fieldName: 'lclgvPicNm', header: { text: '제출자' } },
{ name: 'lclgvPicTelnoCn', fieldName: 'lclgvPicTelnoCn', header: { text: '연락처' } },
{ name: 'lclgvSbmsnSttsNm', fieldName: 'lclgvSbmsnSttsNm', header: { text: '지자체진행상태' } },
{ name: 'lclgvSbmsnSttsCd', fieldName: 'lclgvSbmsnSttsCd', header: { text: '지자체진행상태코드' }, visible: false },
{ name: 'mctpvPicSbmsnSttsNm', fieldName: 'mctpvPicSbmsnSttsNm', header: { text: '진행상태' } },
{ name: 'mctpvPicSbmsnSttsCd', fieldName: 'mctpvPicSbmsnSttsCd', header: { text: '진행상태코드' }, visible: false },
{ name: 'chargerNm', fieldName: 'chargerNm', header: { text: '제출자' } },
{ name: 'chargerCttpc', fieldName: 'chargerCttpc', header: { text: '연락처' } },
{ name: 'submitStatusNm', fieldName: 'submitStatusNm', header: { text: '지자체진행상태' } },
{ name: 'submitStatusCd', fieldName: 'submitStatusCd', header: { text: '지자체진행상태코드' }, visible: false },
{ name: 'prvnStatusNm', fieldName: 'prvnStatusNm', header: { text: '진행상태' } },
{ name: 'prvnStatusCd', fieldName: 'prvnStatusCd', header: { text: '진행상태코드' }, visible: false },
],
columnLayout: [
@@ -30,14 +30,14 @@ const StatsInputStatusInfoColumnRaw = {
'sggNm',
'tabNm',
'reviewHistory',
'lclgvPicNm',
'lclgvPicTelnoCn',
'lclgvSbmsnSttsNm',
'mctpvPicSbmsnSttsNm',
'chargerNm',
'chargerCttpc',
'submitStatusNm',
'prvnStatusNm',
'statsCd',
'sewerBzmnCd',
'lclgvSbmsnSttsCd',
'mctpvPicSbmsnSttsCd',
'submitStatusCd',
'prvnStatusCd'
]
};
@@ -710,10 +710,6 @@ const rawSewagePipeTotList = [
{ name: "sggNm", fieldName: "sggNm", header: { text: "시군구" }, editable: false },
{ name: "admdstNm", fieldName: "admdstNm", header: { text: "행정구역명" }, editable: false },
{ name: "emphsMngSeNm", fieldName: "emphsMngSeNm", header: { text: "집중강우\n중점관리\n구역구분" }, editable: false },
{ name: "wholMnhlCntSum", fieldName: "wholMnhlCntSum", header: { text: "맨홀총계" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
{ name: "frlvlMnhlCntSum", fieldName: "frlvlMnhlCntSum", header: { text: "주철계" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
{ name: "thlvlMnhlCntSum", fieldName: "thlvlMnhlCntSum", header: { text: "콘크리트계" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
{ name: "picNm", fieldName: "picNm", header: { text: "담당자명" }, editable: false },
{ name: "wholMnhlCnt", fieldName: "wholMnhlCnt", header: { text: "계" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
{ name: "frlvlMnhlCnt", fieldName: "frlvlMnhlCnt", header: { text: "주철" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
{ name: "sclvlMnhlCnt", fieldName: "sclvlMnhlCnt", header: { text: "주철(조화)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0", editable: false },
@@ -724,11 +720,6 @@ const rawSewagePipeTotList = [
columnLayout: [
{ name: "addrGroup", header: { text: "행정구역" }, items: ["ctpvNm", "sggNm", "admdstNm"] },
"emphsMngSeNm",
{
name: "adminRefGroup",
header: { text: "행정참고자료" },
items: ["wholMnhlCntSum", "frlvlMnhlCntSum", "thlvlMnhlCntSum", "picNm"],
},
{
name: "mtrlGroup",
header: { text: "맨홀뚜껑 재질(개)" },
@@ -16,6 +16,7 @@ export const HumanWasteSludgeTab4ColumnConfig = {
{ name: "rcycQnt", fieldName: "rcycQnt", header: { text: "재활용" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false },
{ name: "olandLfvlm", fieldName: "olandLfvlm", header: { text: "육상매립" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false },
{ name: "incnrQnt", fieldName: "incnrQnt", header: { text: "소각" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false },
{ name: "oceanInputQnt", fieldName: "oceanInputQnt", header: { text: "해양투기" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false },
{ name: "fuelQnt", fieldName: "fuelQnt", header: { text: "연료화" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false },
{ name: "etcQnt", fieldName: "etcQnt", header: { text: "기타" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false },
{ name: "unprcsQnt", fieldName: "unprcsQnt", header: { text: "미처분량\n(이월량)(C)" }, valueType: ValueType.NUMBER, dataFormat: "#,##0.0", editable: false },
@@ -28,7 +29,7 @@ export const HumanWasteSludgeTab4ColumnConfig = {
{
name: "dspsGroup",
header: { text: "처분량" },
items: ["wholDspsQnt", "rcycQnt", "olandLfvlm", "incnrQnt", "fuelQnt", "etcQnt"]
items: ["wholDspsQnt", "rcycQnt", "olandLfvlm", "incnrQnt", "oceanInputQnt", "fuelQnt", "etcQnt"]
},
"unprcsQnt",
"swtrmtFcltNm"
+3 -85
View File
@@ -1,4 +1,4 @@
import { type GridView, ValueType } from "realgrid";
import type { GridView } from "realgrid";
import type {
ColumnConfig,
@@ -124,82 +124,6 @@ export function getNonEditableColumnFieldNames(
return fieldNames;
}
type ExcelImportCell = {
t?: string;
v?: unknown;
w?: string;
};
/** 엑셀 셀 → 그리드 값. NUMBER 컬럼만 숫자화하고, 코드(01)는 문자열로 유지 */
export function parseExcelImportCellValue(
cell: ExcelImportCell | undefined,
valueType?: ColumnConfig["valueType"],
): string | number {
if (!cell || cell.v === undefined || cell.v === null || cell.v === "") {
return "";
}
if (valueType === ValueType.NUMBER) {
if (typeof cell.v === "number" && !Number.isNaN(cell.v)) {
return cell.v;
}
const stringValue = String(cell.v);
const numericValue = Number(stringValue.replace(/,/g, ""));
if (!Number.isNaN(numericValue) && stringValue.trim() !== "") {
return numericValue;
}
return stringValue;
}
const formatted = typeof cell.w === "string" ? cell.w.trim() : "";
if (/^0\d+$/.test(formatted)) {
return formatted;
}
return String(cell.v);
}
function collectZeroPaddedCodeWidths(
rows: Record<string, string | number>[],
): Map<string, number> {
const widthByField = new Map<string, number>();
for (const row of rows) {
for (const [field, value] of Object.entries(row)) {
const s = String(value ?? "").trim();
if (!/^0\d+$/.test(s)) continue;
widthByField.set(field, Math.max(widthByField.get(field) ?? 0, s.length));
}
}
return widthByField;
}
function restoreImportedCodeLeadingZeros(
importedRow: Record<string, string | number>,
existingRow: Record<string, string | number> | undefined,
codeWidthByField: Map<string, number>,
): Record<string, string | number> {
const next: Record<string, string | number> = { ...importedRow };
for (const [field, value] of Object.entries(next)) {
if (value === "" || value == null) continue;
if (typeof value === "number" && !Number.isInteger(value)) continue;
const importedStr = String(value).trim();
if (!/^\d+$/.test(importedStr)) continue;
const existingStr =
existingRow != null ? String(existingRow[field] ?? "").trim() : "";
let width = codeWidthByField.get(field);
if (!width && /^0\d+$/.test(existingStr)) {
width = existingStr.length;
}
if (!width && field.endsWith("Cd") && importedStr.length <= 2) {
width = 2;
}
if (width && importedStr.length < width) {
next[field] = importedStr.padStart(width, "0");
}
}
return next;
}
/** 엑셀 import 결과를 기존 행과 병합 — preserveFieldNames는 기존 그리드 값 유지 */
export function mergeExcelImportWithExistingRows(
existingRows: Record<string, string | number>[],
@@ -210,20 +134,14 @@ export function mergeExcelImportWithExistingRows(
options?.preserveFieldNames && options.preserveFieldNames.length > 0
? new Set(options.preserveFieldNames)
: null;
const codeWidthByField = collectZeroPaddedCodeWidths(existingRows);
return importedRows.map((importedRow, index) => {
const existingRow = existingRows[index];
const normalizedImported = restoreImportedCodeLeadingZeros(
importedRow,
existingRow,
codeWidthByField,
);
if (!existingRow) return normalizedImported;
if (!existingRow) return { ...importedRow };
const merged: Record<string, string | number> = {
...existingRow,
...normalizedImported,
...importedRow,
};
if (preserveSet) {
for (const fieldName of preserveSet) {
@@ -1,23 +0,0 @@
import { statisticsApi } from "@/entities/statistics/api/statistics-service";
import type {
ImrRequest,
ImrResponse,
rollbackStatusRequest,
} from "@/entities/statistics/model/statistics-interface";
/** 제출 직후 IMR 요청이 실패하면 검증 이전 상태로 롤백 */
export async function callImrServiceWithRollbackOnFailure(
imrParam: ImrRequest,
rollbackParam: rollbackStatusRequest,
): Promise<ImrResponse> {
try {
return await statisticsApi.callImrService(imrParam);
} catch (error) {
try {
await statisticsApi.rollbackStatus(rollbackParam);
} catch {
// 롤백 실패는 IMR 실패를 가리지 않음
}
throw error;
}
}
@@ -1,9 +1,6 @@
import type { StatisticsInputCommonInfoBase } from "@/entities/statistics/model/statistics-interface";
import {
normalizeVerificationResultMap,
type VerificationResultLikeMap,
} from "./verification-result-display";
import type { VerificationResultLikeMap } from "./verification-result-display";
export function isVerificationFailedFromCommonInfo(
commonInfo: Pick<StatisticsInputCommonInfoBase, "isVerificationFailed"> | null | undefined,
@@ -31,9 +28,6 @@ export function patchFooterVerificationByTab(
| undefined,
): Map<string, VerificationResultLikeMap | null> {
const next = new Map(prev);
next.set(
tabKey,
normalizeVerificationResultMap(commonInfo?.verificationResult) ?? null,
);
next.set(tabKey, commonInfo?.verificationResult ?? null);
return next;
}
@@ -13,49 +13,6 @@ export type VerificationResultLikeMap = Record<
VerificationResultLikeItem[]
>;
/** 입력 조회 verificationResult의 기준값/입력값이 null이면 "-" */
export function displayVerificationCn(
value: string | null | undefined,
): string {
if (value == null || String(value).trim() === "") return "-";
return String(value);
}
export function normalizeVerificationResultItem<T extends VerificationResultLikeItem>(
item: T,
): T {
return {
...item,
nowCn: displayVerificationCn(item.nowCn ?? item.nowVlCn),
cmprCn: displayVerificationCn(item.cmprCn ?? item.compVlCn),
};
}
export function normalizeVerificationResultMap<T extends VerificationResultLikeMap>(
result: T | null | undefined,
): T | undefined {
if (!result || typeof result !== "object") return undefined;
const next = {} as T;
for (const [key, items] of Object.entries(result)) {
(next as VerificationResultLikeMap)[key] = Array.isArray(items)
? items.map((item) => normalizeVerificationResultItem(item))
: [];
}
return next;
}
/** 조회 행/객체에 붙은 verificationResult의 cmprCn·nowCn null을 "-"로 바꿈 */
export function withNormalizedVerificationResult<T extends object>(item: T): T {
if (!("verificationResult" in item) || item.verificationResult == null) {
return item;
}
const verificationResult = normalizeVerificationResultMap(
item.verificationResult as VerificationResultLikeMap,
);
if (!verificationResult) return item;
return { ...item, verificationResult };
}
/** 검증 오류 배열 → RealGrid styleName */
export function getVerificationStyleName(
errors: VerificationResultLikeItem[] | null | undefined,
@@ -80,8 +37,8 @@ export function formatVerificationTooltip(
: e.vrfcCd === "NV"
? "일반검증 - "
: "모집단 검증 - ";
const comp = displayVerificationCn(e.cmprCn ?? e.compVlCn);
const now = displayVerificationCn(e.nowCn ?? e.nowVlCn);
const comp = e.cmprCn ?? e.compVlCn ?? "";
const now = e.nowCn ?? e.nowVlCn ?? "";
return `${prefix}${e.rsnExpln}\n(검증 기준값: ${comp} / 현재 입력값: ${now})`;
})
.filter(Boolean)
@@ -56,11 +56,10 @@ export class GeneralverificationApiService implements GeneralverificationService
}
public async UpdateVrfcMaster(
vrfcSn: number,
param: UpdateVrfcMasterRequest,
): Promise<UpdateVrfcMasterResponse> {
const response = await stClient.put<UpdateVrfcMasterResponse>(
`${ENDPOINT.ST_SERVICE.INPUTVERIFY}/masters/${vrfcSn}`,
`${ENDPOINT.ST_SERVICE.INPUTVERIFY}/masters/${param.vrfcSn}`,
param,
);
return response.data;
@@ -128,22 +127,20 @@ export class GeneralverificationApiService implements GeneralverificationService
}
public async RegistVrfcDetail(
vrfcSn: number,
param: VerifyDetailRegistRequest,
): Promise<RegistVrfcMasterResponse> {
const response = await stClient.post<RegistVrfcMasterResponse>(
`${ENDPOINT.ST_SERVICE.INPUTVERIFY}/${vrfcSn}/details`,
`${ENDPOINT.ST_SERVICE.INPUTVERIFY}/${param.vrfcSn}/details`,
param,
);
return response.data;
}
public async UpdateVrfcDetail(
vrfcSn: number,
param: VerifyDetailUpdateRequest,
): Promise<RegistVrfcMasterResponse> {
const response = await stClient.put<RegistVrfcMasterResponse>(
`${ENDPOINT.ST_SERVICE.INPUTVERIFY}/${vrfcSn}/details`,
`${ENDPOINT.ST_SERVICE.INPUTVERIFY}/${param.vrfcSn}/details`,
param,
);
return response.data;
@@ -16,8 +16,6 @@ import {
type ImrStatusResponse,
type ReviewerInfoRequest,
type ReviewerInfoResponse,
type rollbackStatusRequest,
type rollbackStatusResponse,
type ReviewInputSaveRequest,
type ReviewOpinionSaveRequest,
type ReviewOpinionSaveResponse,
@@ -158,17 +156,6 @@ export class StatisticsApiService implements StatisticsService {
return response.data;
}
// 입력 - 검증 이전 상태로 롤백
public async rollbackStatus(
param: rollbackStatusRequest,
): Promise<rollbackStatusResponse> {
const response = await stClient.put<rollbackStatusResponse>(
`${ENDPOINT.ST_SERVICE.INPUTCOMMON}/rollback-status`,
param,
);
return response.data;
}
public async callImrService(param: ImrRequest): Promise<ImrResponse> {
const response = await client.post<ImrResponse>(
`${ENDPOINT.STATVERIFY_SERVICE.IMR}`,
@@ -46,6 +46,7 @@ export interface CheckVerificationNameResponse {
}
export interface UpdateVrfcMasterRequest {
vrfcSn?: number | null;
vrfcNm?: string | null;
vrfcClsfCd?: string | null;
dcptDgt?: number | null;
@@ -183,11 +184,13 @@ export interface VerifyDetailRegistData {
}
export interface VerifyDetailRegistRequest {
vrfcSn: number | null;
detailList: VerifyDetailRegistData[];
rgtrUuid: string;
}
export interface VerifyDetailUpdateRequest {
vrfcSn: number | null;
detailList: VerifyDetailRegistData[];
mdfrUuid: string;
}
@@ -294,7 +297,6 @@ export interface GeneralverificationService {
param: CheckVerificationNameRequest,
): Promise<CheckVerificationNameResponse>;
UpdateVrfcMaster(
vrfcSn: number,
param: UpdateVrfcMasterRequest,
): Promise<UpdateVrfcMasterResponse>;
RegistVrfcMaster(
@@ -313,11 +315,9 @@ export interface GeneralverificationService {
param: string,
): Promise<VerifyColumnListResponse>;
RegistVrfcDetail(
vrfcSn: number,
param: VerifyDetailRegistRequest,
): Promise<RegistVrfcMasterResponse>;
UpdateVrfcDetail(
vrfcSn: number,
param: VerifyDetailUpdateRequest,
): Promise<RegistVrfcMasterResponse>;
CopyVrfcData(
@@ -437,6 +437,7 @@ export interface ExcretaSlurgeProcessingStatusItem {
rcycQnt: number;
olandLfvlm: number;
incnrQnt: number;
oceanInputQnt: number;
fuelQnt: number;
etcQnt: number;
unprcsQnt: number;
@@ -81,6 +81,7 @@ export interface ReportStatusResponseDto {
lastCfmtnPicTelnoCn?: string | null;
lastCfmtnPicDeptNm?: string | null;
lastCfmtnPicJbgdNm?: string | null;
splmntRsltCn?: string | null;
splmntOpnnCn?: string | null;
rjctOpnnCn?: string | null;
}
@@ -308,6 +308,7 @@ export interface SeweragePipeInputPipeTypeStatusSaveItem {
rmrk: string | null;
lyngPrlgWhol: number;
dmltnPrlgWhol: number;
pipeByPrstWhol: number;
}
/** 관종별 현황 저장 API 요청 */
@@ -419,10 +420,6 @@ export interface SeweragePipeInputManholeStatusItem {
folvlMnhlCnt: number;
filvlMnhlCnt: number;
rmrk: string;
wholMnhlCntSum: number;
frlvlMnhlCntSum: number;
thlvlMnhlCntSum: number;
picNm: string | null;
verificationResult?: SeweragePipeInputVerificationResult;
}
@@ -9,12 +9,12 @@ export interface StatsInputStatusReviewItem {
sewerBzmnCd?: string;
tabNm?: string;
statsCd?: string;
lclgvPicNm?: string;
lclgvPicTelnoCn?: string;
lclgvSbmsnSttsCd?: string;
lclgvSbmsnSttsNm?: string;
mctpvPicSbmsnSttsCd?: string;
mctpvPicSbmsnSttsNm?: string;
submitStatusNm?: string;
prvnStatusNm?: string;
chargerNm?: string;
chargerCttpc?: string;
submitStatusCd?: string;
prvnStatusCd?: string;
}
export interface StatsInputStatusReviewResponseData {
@@ -37,7 +37,7 @@ export interface ReviewHistoryResponse {
status: string;
message?: string;
data: {
regDt: string;
logDt: string;
historyContent: string;
}[]
}
@@ -31,21 +31,6 @@ export interface cancelSubmitResponse {
message?: string;
}
/** 검증 이전 상태로 롤백 요청 */
export interface rollbackStatusRequest {
statsYr: string;
statsCd: string;
sewerBzmnCd: string;
rollbackStatusCode: string;
mdfrUuid: string;
}
/** 검증 이전 상태로 롤백 응답 */
export interface rollbackStatusResponse {
status: string;
message?: string;
}
export interface ApiResponse<T> {
status: string;
data?: T;
@@ -342,6 +327,7 @@ export interface ReviewOpinionDetailItem {
sewerBzmnCd: string;
statsCd: string;
splmntOpnnCn: string | null;
splmntRsltCn: string | null;
mdfrUuid: string | null;
}
@@ -357,6 +343,7 @@ export interface ReviewOpinionSaveRequest {
sewerBzmnCd: string;
statsCd: string;
splmntOpnnCn: string;
splmntRsltCn: string;
mdfrUuid: string;
}
@@ -401,6 +388,7 @@ export interface ReviewSubmitRequest {
/** 통계항목 단위 제출 시 사용 (통계 입력 현황 일괄 제출에는 미사용) */
statsCd?: string;
splmntOpnnCn?: string;
splmntRsltCn?: string;
}
export interface ReviewSubmitResponse {
@@ -466,6 +454,7 @@ export interface ReviewerInfoResponse {
dpmNm: string;
cps: string;
splmntOpnnCn: string;
splmntRsltCn: string;
}
}
@@ -489,8 +478,9 @@ export interface StatiscicsManagementReviewHistoryItem {
ctpvNm: string;
sggNm: string;
menuNm: string;
splmntOpnnCn: string | null;
rjctOpnnCn: string | null;
wrterOpinionSm: string | null;
chckerOpinionSm: string | null;
returnOpinionSm: string | null;
}
export interface StatiscicsManagementReviewHistoryResponse {
@@ -567,7 +557,6 @@ export interface StatisticsService {
saveStatsInputPeriod(param: StatsInputPeriodSaveRequest): Promise<StatsInputPeriodSaveApiResponse>;
deleteStatsInputPeriod(param: StatsInputPeriodDeleteRequest): Promise<StatsInputPeriodDeleteApiResponse>;
cancelSubmit(param: cancelSubmitRequest): Promise<cancelSubmitResponse>;
rollbackStatus(param: rollbackStatusRequest): Promise<rollbackStatusResponse>;
callImrService(param: ImrRequest): Promise<ImrResponse>;
submitException(param: SubmitExceptionRequest): Promise<SubmitExceptionResponse>;
getLogList(param: StatisticsLogRequest): Promise<StatisticsLogResponse>;
@@ -127,6 +127,7 @@ export interface HumanWasteSludgeReviewItem {
rcycQnt: number | null;
olandLfvlm: number | null;
incnrQnt: number | null;
oceanInputQnt: number | null;
fuelQnt: number | null;
etcQnt: number | null;
unprcsQnt: number | null;
@@ -217,6 +218,7 @@ export function mapSewageExcretaSlurgeReviewRows(list: unknown[]): object[] {
rcycQnt: row.rcycQnt ?? 0,
olandLfvlm: row.olandLfvlm ?? 0,
incnrQnt: row.incnrQnt ?? 0,
oceanInputQnt: row.oceanInputQnt ?? 0,
fuelQnt: row.fuelQnt ?? 0,
etcQnt: row.etcQnt ?? 0,
unprcsQnt: row.unprcsQnt ?? 0,
@@ -108,4 +108,5 @@ export interface StatsStatusInfo {
mctpvPicSbmsnSttsCd?: string;
rjctOpnnCn?: string;
splmntOpnnCn?: string;
splmntRsltCn?: string;
}
@@ -247,10 +247,6 @@ export interface SewagePipeReviewManholeItem {
folvlMnhlCnt: number;
filvlMnhlCnt: number;
rmrk: string | null;
wholMnhlCntSum: number;
frlvlMnhlCntSum: number;
thlvlMnhlCntSum: number;
picNm: string | null;
}
export interface SewagePipeManholeResponseData {
@@ -270,4 +266,5 @@ export interface StatsStatusInfo {
mctpvPicSbmsnSttsCd?: string;
rjctOpnnCn?: string;
splmntOpnnCn?: string;
splmntRsltCn?: string;
}
@@ -59,6 +59,7 @@ export interface statsStatusInfo {
mctpvPicSbmsnSttsCd?: string;
rjctOpnnCn?: string;
splmntOpnnCn?: string;
splmntRsltCn?: string;
}
export interface SewerageSupplyRateReviewResponseData {
@@ -168,10 +168,6 @@ export interface FcltyManholeSummaryItem {
thlvlMnhlCnt: number;
folvlMnhlCnt: number;
filvlMnhlCnt: number;
wholMnhlCntSum: number;
frlvlMnhlCntSum: number;
thlvlMnhlCntSum: number;
picNm: string | null;
}
/**
@@ -135,6 +135,7 @@ export interface HumanWasteSludgeStatusTotalItem {
rcycQnt: number;
olandLfvlm: number;
incnrQnt: number;
oceanInputQnt: number;
fuelQnt: number;
etcQnt: number;
unprcsQnt: number;
@@ -16,8 +16,8 @@ interface VrfcReportPanelProps {
const TYPE_CODE_NAME_MAP: Record<string, string> = {
NV: "일반",
IMR: "I-MR",
HB: "HB",
IMR: "시계열",
HB: "모집단",
};
const TYPE_CODE_COLOR_MAP: Record<string, BadgeProps["color"]> = {
@@ -73,8 +73,8 @@ export function VrfcReportPanel({ params }: VrfcReportPanelProps) {
),
)}
</div>
)}
)
}
{/* TBD: 테이블 컬럼 정의, 증감 표시 방법 */}
<div className="max-h-[44rem] overflow-y-auto">
{data.list.length === 0 ? (
@@ -114,9 +114,9 @@ export function VrfcReportPanel({ params }: VrfcReportPanelProps) {
<span>{item.admdstNm}</span>
<span>
{/* TBD: 증감 표시 방법 (nowCn - cmprCn 계산?) */}
현재:{" "}{item.nowCn ?? "-"}
/ 비교:{" "}
{item.cmprCn ?? "-"}
현재: {item.nowCn ?? "-"}{" "}
/ 비교:
{item.cmprCn ?? "-"}{" "}
</span>
</div>
{/* TBD: 등급/점수/단계/업데이트 시각 필드 미제공 */}
@@ -1,7 +1,5 @@
export const STATISTICS_ROUTE = "/statistics";
export const STATISTICS_TAB_QUERY_KEY = "tab";
/** 기본 화면(대시보드) 도움말 매칭용. URL에는 넣지 않음 */
export const STATISTICS_DASHBOARD_TAB_ID = "Dashboard";
export function buildStatisticsTabUrl(tabId: string): string {
return `${STATISTICS_ROUTE}?${STATISTICS_TAB_QUERY_KEY}=${encodeURIComponent(tabId)}`;
@@ -1,20 +1,13 @@
import { PageTitle } from "@common/components";
import { findMenuItemByUrlPath } from "@/shared/lib/stats-cd-input-navigation";
import { STATISTICS_DASHBOARD_TAB_ID } from "@/shared/lib/statistics-tab-url";
import { TabBar } from "./tab-bar";
import { useTabStore } from "@/store/tab-store";
function StatisticsPageLayout({ children }: { children: React.ReactNode }) {
const { activeTabId, tabs, menuList } = useTabStore();
const { activeTabId, tabs } = useTabStore();
const pageTitle = tabs.find((t) => t.id === activeTabId);
const dashboardMenu =
!pageTitle && menuList
? findMenuItemByUrlPath(menuList, STATISTICS_DASHBOARD_TAB_ID)
: null;
const menuPaths = pageTitle?.path?.split(">").map((path) => path.trim());
@@ -23,7 +16,7 @@ function StatisticsPageLayout({ children }: { children: React.ReactNode }) {
<TabBar />
<PageTitle
title={pageTitle?.label ?? ""}
menuCd={pageTitle?.menuItem?.menuCd ?? dashboardMenu?.menuCd}
menuCd={pageTitle?.menuItem?.menuCd}
menuPaths={menuPaths ?? []}
/>
{children}