[st] - feat: 입력 04 작업 중 & 수정 요청 사항 적용

This commit is contained in:
2026-06-05 10:35:31 +09:00
parent d58b2db786
commit 7b9081a125
27 changed files with 1293 additions and 280 deletions
+107 -3
View File
@@ -457,10 +457,29 @@ function sortRowsGroupPreserving(
}
/** columnInfo → RealGrid DataField 배열 (computed → valueCallback 포함, setFields용) */
function getDecimalPlacesFromDataFormat(dataFormat?: string): number | null {
if (!dataFormat) return null;
const match = dataFormat.match(/\.(0+|#+)/);
const decimalToken = match?.[1];
if (!decimalToken) return null;
return decimalToken.length;
}
function truncateByDecimalPlaces(value: number, decimalPlaces: number): number {
if (!Number.isFinite(value)) return 0;
if (decimalPlaces < 0) return value;
const factor = 10 ** decimalPlaces;
const scaled = value * factor;
// 절삭(truncate): 반올림 없이 소수점 이하를 버린다.
const truncated = scaled >= 0 ? Math.floor(scaled) : Math.ceil(scaled);
return truncated / factor;
}
function buildFieldsFromColumns(
columns: {
fieldName: string;
valueType?: ValueType;
dataFormat?: string;
datetimeFormat?: string;
editor?: { type?: string; datetimeFormat?: string };
computed?: import("../type/DataGrid.type").ComputedFieldConfig;
@@ -509,17 +528,23 @@ function buildFieldsFromColumns(
"subtrahendFields" in col.computed &&
col.computed.minuendFields.length > 0;
const hasSumOfMaxGroups =
col.computed.type === "sumOfMaxOfFieldAndSumGroups" &&
"groups" in col.computed &&
Array.isArray(col.computed.groups) &&
col.computed.groups.length > 0;
const hasPumpGrossPpqty =
col.computed.type === "pumpGrossPpqty" &&
"pumpGroups" in col.computed &&
col.computed.pumpGroups.length > 0;
if (
hasFields ||
hasNumeratorDenominator ||
hasMaxOfFieldAndSum ||
hasDifferenceOfSums ||
hasSumOfMaxGroups
hasSumOfMaxGroups ||
hasPumpGrossPpqty
) {
field.valueCallback = getComputedValueCallback(
const computedCallback = getComputedValueCallback(
col.computed,
) as (
prod: unknown,
@@ -528,6 +553,24 @@ function buildFieldsFromColumns(
fieldNames: string[],
values: unknown[],
) => number;
const decimalPlaces = getDecimalPlacesFromDataFormat(col.dataFormat);
field.valueCallback = (
prod: unknown,
dataRow: number,
fieldName: string,
fieldNames: string[],
values: unknown[],
) => {
const computedValue = computedCallback(
prod,
dataRow,
fieldName,
fieldNames,
values,
);
if (decimalPlaces == null) return computedValue;
return truncateByDecimalPlaces(computedValue, decimalPlaces);
};
}
}
return field;
@@ -1046,6 +1089,14 @@ const DataGrid = ({
}
columns.forEach((column) => {
const gv = gridView as GridViewWithSetColumnProperty;
const exists =
typeof (
gridView as { columnByName?: (name: string) => unknown }
).columnByName === "function" &&
(gridView as { columnByName: (name: string) => unknown }).columnByName(
column.name,
) != null;
if (!exists) return;
gv.setColumnProperty?.(
column.name,
"header",
@@ -1181,10 +1232,63 @@ const DataGrid = ({
const hasFooterUserSpans = checkFooterUserSpans(columnLayout);
if (hasFooterUserSpans) {
const validColumnNames = new Set(columns.map((c) => c.name));
const sanitizeColumnLayout = (
layout: unknown[],
): unknown[] => {
const result: unknown[] = [];
for (const item of layout) {
if (typeof item === "string") {
if (validColumnNames.has(item)) {
result.push(item);
}
continue;
}
if (!item || typeof item !== "object") continue;
if ("column" in item) {
const columnName = (item as { column?: unknown }).column;
if (
typeof columnName === "string" &&
validColumnNames.has(columnName)
) {
result.push(item);
}
continue;
}
if ("items" in item && Array.isArray((item as { items?: unknown[] }).items)) {
const nextItems = sanitizeColumnLayout(
(item as { items: unknown[] }).items,
);
if (nextItems.length > 0) {
result.push({
...(item as Record<string, unknown>),
items: nextItems,
});
}
continue;
}
result.push(item);
}
return result;
};
// setColumnLayout은 그리드가 완전히 렌더링된 후 호출해야 함
setTimeout(() => {
if (gridRef.current?.gridView && columnLayout) {
gridRef.current.gridView.setColumnLayout?.(columnLayout);
const safeLayout = sanitizeColumnLayout(columnLayout as unknown[]);
if (safeLayout.length === 0) return;
try {
(
gridRef.current.gridView.setColumnLayout as
| ((layout: unknown) => void)
| undefined
)?.(safeLayout);
} catch (e) {
console.warn("setColumnLayout skipped due to invalid columns:", e);
}
}
}, 100);
}
@@ -33,7 +33,7 @@ import DataGrid from "@/components/module/DataGrid";
import { VerificationResultHistoryPagination } from "@/components/module/VerificationResultHistoryPagination";
import type { CellStyleCallbackParams, ColumnInfo } from "@/components/type/DataGrid.type";
/** 통계 입력 현황 > 행 클릭 시 예외처리(prcsYn=3) 내역만 조회 */
/** 통계 입력 현황 > 행 클릭 시 사용자예외처리(prcsYn=2) 내역만 조회 */
export interface ExceptionProcessingHistoryQueryContext {
sewerBzmnCd: string;
ctpvNm: string;
@@ -92,13 +92,15 @@ function exceptionPrcsYnNmCellStyle({ fieldName, row }: CellStyleCallbackParams)
const exceptionHistoryColumnInfo: ColumnInfo = {
columns: [
{ name: "ctpvNm", fieldName: "ctpvNm", header: { text: "시도" }, editable: false },
{ name: "sggNm", fieldName: "sggNm", header: { text: "시군구" }, editable: false },
{ name: "admdstNm", fieldName: "admdstNm", header: { text: "행정구역" }, editable: false },
{ name: "vrfcTrgtNm", fieldName: "vrfcTrgtNm", header: { text: "검증대상" }, editable: false },
{ name: "rsnExpln", fieldName: "rsnExpln", header: { text: "사유" }, editable: false },
{ name: "nowVlCn", fieldName: "nowVlCn", header: { text: "입력값" }, editable: false },
{ name: "compVlCn", fieldName: "compVlCn", header: { text: "기준값" }, editable: false },
{ name: "prcsYnNm", fieldName: "prcsYnNm", header: { text: "처리여부" }, editable: false },
{ name: "pbcpAprvYn", fieldName: "pbcpAprvYn", header: { text: "공단승인여부" }, editable: false },
{ name: "stVrfRjtComNm", fieldName: "stVrfRjtComNm", header: { text: "반려상태" }, editable: false },
{ name: "prcsOpnnExpln", fieldName: "prcsOpnnExpln", header: { text: "처리의견" }, editable: false },
{ name: "rjctOpnnCn", fieldName: "rjctOpnnCn", header: { text: "반려의견" }, editable: false },
{ name: "regDt", fieldName: "regDt", header: { text: "등록일시" }, editable: false },
{ name: "mdfcnDt", fieldName: "mdfcnDt", header: { text: "갱신일시" }, editable: false },
{
@@ -114,13 +116,15 @@ const exceptionHistoryColumnInfo: ColumnInfo = {
],
columnLayout: [
"ctpvNm",
"sggNm",
"admdstNm",
"vrfcTrgtNm",
"rsnExpln",
"nowVlCn",
"compVlCn",
"prcsYnNm",
"pbcpAprvYn",
"stVrfRjtComNm",
"prcsOpnnExpln",
"rjctOpnnCn",
"regDt",
"mdfcnDt",
"individualReject",
@@ -190,9 +194,9 @@ export function ExceptionProcessingHistoryDialog({
sggNm: queryContext.sggNm,
statsYr: queryContext.statsYr,
statsCd: queryContext.statsCd,
pbcpAprvYn: "N",
vrfcMthdCd,
prcsYnList: PRCS_YN_LIST,
rejectCodeList: "0,1,2",
page: pageIndex + 1,
size: 30,
};
@@ -308,14 +312,16 @@ export function ExceptionProcessingHistoryDialog({
const mapRows = (entries: VerificationResultReportItem[]): ExceptionHistoryGridRow[] =>
entries.map((item) => ({
ctpvNm: item.ctpvNm,
sggNm: item.sggNm,
admdstNm: item.admdstNm ?? item.sggNm ?? "-",
vrfcTrgtNm: item.vrfcTrgtNm,
rsnExpln: item.rsnExpln,
nowVlCn: item.nowVlCn ?? "-",
compVlCn: item.compVlCn ?? "-",
prcsYnNm: item.prcsYnNm ?? "-",
stVrfRjtComNm: item.stVrfRjtComNm ?? "-",
prcsOpnnExpln: item.prcsOpnnExpln ?? "-",
rjctOpnnCn: item.rjctOpnnCn ?? "-",
prcsYn: item.prcsYn ?? "",
pbcpAprvYn: item.pbcpAprvYn,
regDt: (item.regDt ?? "").slice(0, 16),
mdfcnDt: (item.mdfcnDt ?? "").slice(0, 16),
individualReject: "개별 반려",
+1 -1
View File
@@ -41,7 +41,7 @@ const getSggListFetcher = async (
key: [string, string, string, string],
): Promise<ApiResponse<Sgg[]>> => {
const gugunSkipYn = doList.includes(key[3]) ? "N" : "Y";
return statisticsApi.getSggAllList(key[1], {
return statisticsApi.getSggList(key[1], {
instSeCd: key[2],
gugunSkipYn,
});
@@ -92,13 +92,12 @@ const verificationSubmitResultColumnInfo: ColumnInfo = {
{ name: "vrfcMthdNm", fieldName: "vrfcMthdNm", header: { text: "구분" }, editable: false },
{ name: "vrfcMthdCd", fieldName: "vrfcMthdCd", header: { text: "검증유형코드" }, editable: false, visible: false },
{ name: "ctpvNm", fieldName: "ctpvNm", header: { text: "시도" }, editable: false },
{ name: "sggNm", fieldName: "sggNm", header: { text: "시군구" }, editable: false },
{ name: "admdstNm", fieldName: "admdstNm", header: { text: "행정구역" }, editable: false },
{ name: "vrfcTrgtNm", fieldName: "vrfcTrgtNm", header: { text: "검증대상" }, editable: false },
{ name: "rsnExpln", fieldName: "rsnExpln", header: { text: "사유" }, editable: false },
{ name: "nowVlCn", fieldName: "nowVlCn", header: { text: "입력값" }, editable: false },
{ name: "compVlCn", fieldName: "compVlCn", header: { text: "기준값" }, editable: false },
{ name: "prcsYn", fieldName: "prcsYn", header: { text: "처리여부" }, editable: false },
{ name: "pbcpAprvYn", fieldName: "pbcpAprvYn", header: { text: "공단승인여부" }, editable: false },
{ name: "regDt", fieldName: "regDt", header: { text: "등록일시" }, editable: false },
{ name: "mdfcnDt", fieldName: "mdfcnDt", header: { text: "갱신일시" }, editable: false },
{
@@ -111,13 +110,12 @@ const verificationSubmitResultColumnInfo: ColumnInfo = {
columnLayout: [
"vrfcMthdNm",
"ctpvNm",
"sggNm",
"admdstNm",
"vrfcTrgtNm",
"rsnExpln",
"nowVlCn",
"compVlCn",
"prcsYn",
"pbcpAprvYn",
"regDt",
"mdfcnDt",
"prcsOpnnExpln",
@@ -202,13 +200,12 @@ export function VerificationResultDialog({
vrfcMthdCd,
vrfcMthdNm: VRFC_MTHD_NAME_MAP[vrfcMthdCd] ?? item.vrfcMthdNm,
ctpvNm: item.ctpvNm,
sggNm: item.sggNm,
admdstNm: item.admdstNm ?? item.sggNm ?? "-",
vrfcTrgtNm: item.vrfcTrgtNm,
rsnExpln: item.rsnExpln,
nowVlCn: item.nowVlCn ?? "-",
compVlCn: item.compVlCn ?? "-",
prcsYn: PRCS_YN_LABEL[item.prcsYn] ?? item.prcsYn,
pbcpAprvYn: item.pbcpAprvYn,
prcsYn: item.prcsYnNm ?? PRCS_YN_LABEL[item.prcsYn] ?? item.prcsYn,
regDt: (item.regDt ?? "").slice(0, 16),
mdfcnDt: (item.mdfcnDt ?? "").slice(0, 16),
prcsOpnnExpln: item.prcsOpnnExpln ?? "",
@@ -226,9 +223,9 @@ export function VerificationResultDialog({
sggNm: stableQueryContext.sggNm,
statsYr: stableQueryContext.statsYr,
statsCd: stableQueryContext.statsCd,
pbcpAprvYn: "N",
vrfcMthdCd,
prcsYnList: "0",
rejectCodeList: "0,1,2",
page: page + 1,
size: 10000,
};
@@ -137,12 +137,15 @@ function verificationPrcsYnNmCellStyle({ fieldName, row }: CellStyleCallbackPara
const verificationResultColumnInfo: ColumnInfo = {
columns: [
{ name: "ctpvNm", fieldName: "ctpvNm", header: { text: "시도" }, editable: false },
{ name: "sggNm", fieldName: "sggNm", header: { text: "시군구" }, editable: false },
{ name: "admdstNm", fieldName: "admdstNm", header: { text: "행정구역" }, editable: false },
{ name: "vrfcTrgtNm", fieldName: "vrfcTrgtNm", header: { text: "검증대상" }, editable: false },
{ name: "rsnExpln", fieldName: "rsnExpln", header: { text: "사유" }, editable: false },
{ name: "nowVlCn", fieldName: "nowVlCn", header: { text: "입력값" }, editable: false },
{ name: "compVlCn", fieldName: "compVlCn", header: { text: "기준값" }, editable: false },
{ name: "prcsYnNm", fieldName: "prcsYnNm", header: { text: "처리여부" }, editable: false },
{ name: "stVrfRjtComNm", fieldName: "stVrfRjtComNm", header: { text: "반려상태" }, editable: false },
{ name: "prcsOpnnExpln", fieldName: "prcsOpnnExpln", header: { text: "처리의견" }, editable: false },
{ name: "rjctOpnnCn", fieldName: "rjctOpnnCn", header: { text: "반려의견" }, editable: false },
{ name: "regDt", fieldName: "regDt", header: { text: "등록일시" }, editable: false },
{ name: "mdfcnDt", fieldName: "mdfcnDt", header: { text: "갱신일시" }, editable: false },
{ name: "prcsYn", fieldName: "prcsYn", header: { text: "검증결과상태코드" }, editable: false, visible: false },
@@ -150,12 +153,15 @@ const verificationResultColumnInfo: ColumnInfo = {
],
columnLayout: [
"ctpvNm",
"sggNm",
"admdstNm",
"vrfcTrgtNm",
"rsnExpln",
"nowVlCn",
"compVlCn",
"prcsYnNm",
"stVrfRjtComNm",
"prcsOpnnExpln",
"rjctOpnnCn",
"regDt",
"mdfcnDt",
"prcsYn",
@@ -245,9 +251,9 @@ export function VerificationResultHistoryDialog({
sggNm: stableQueryContext.sggNm,
statsYr: stableQueryContext.statsYr,
statsCd: stableQueryContext.statsCd,
pbcpAprvYn: "N",
vrfcMthdCd,
prcsYnList: "0,2,3",
prcsYnList: "0,2",
rejectCodeList: "0,1,2",
page: pageIndex + 1,
size: PAGE_SIZE,
};
@@ -376,14 +382,16 @@ export function VerificationResultHistoryDialog({
vrfcSn: item.vrfcSn,
__checked: isChecked ? 1 : 0,
ctpvNm: item.ctpvNm,
sggNm: item.sggNm,
admdstNm: item.admdstNm ?? item.sggNm ?? "-",
vrfcTrgtNm: item.vrfcTrgtNm,
rsnExpln: item.rsnExpln,
nowVlCn: item.nowVlCn ?? "-",
compVlCn: item.compVlCn ?? "-",
prcsYnNm: item.prcsYnNm ?? "-",
stVrfRjtComNm: item.stVrfRjtComNm ?? "-",
prcsOpnnExpln: item.prcsOpnnExpln ?? "-",
rjctOpnnCn: item.rjctOpnnCn ?? "-",
prcsYn: item.prcsYn ?? "",
pbcpAprvYn: item.pbcpAprvYn,
regDt: (item.regDt ?? "").slice(0, 16),
mdfcnDt: (item.mdfcnDt ?? "").slice(0, 16),
};
@@ -34,8 +34,8 @@ export async function hasAnyPostSubmitVerificationResult(
sggNm: ctx.sggNm,
statsYr: ctx.statsYr,
statsCd: ctx.statsCd,
pbcpAprvYn: "N" as const,
prcsYnList: "0",
rejectCodeList: "0,1,2",
page: 1,
size: 1,
};