Files
frontend/apps/st/src/components/util/column/management/LogHistoryDetailColumn.ts
T

253 lines
7.9 KiB
TypeScript

import { ValueType } from "realgrid";
import type { ColumnConfig, ColumnInfo } from "@/components/type/DataGrid.type";
import type { StatisticsLogDetailRow } from "@/entities/statistics/model/statistics-interface";
export const LOG_DETAIL_DETAILS_FIELD = "details";
export const LOG_DETAIL_DETAILS_RAW_FIELD = "__detailsRaw";
export interface LogDetailVerificationColumn {
tblNm: string;
metaSn: number;
fieldNm: string;
engColNm: string;
dataScpCd: string;
kornColNm: string;
}
export interface LogDetailVerificationItem {
columns: LogDetailVerificationColumn[];
cmprOpert: string | null;
vrfcDtlSn: number;
refStatsCd: string | null;
eqtnCombSeq: number;
engArtopnEqtnCn: string;
kornArtopnEqtnCn: string;
}
const COMMON_FIELD_ORDER = ["rmrk", "sggNm", "admdstCd", "admdstNm"] as const;
const FIELD_HEADER: Record<string, string> = {
rmrk: "비고",
sggNm: "시군구",
admdstCd: "행정구역코드",
admdstNm: "행정구역명",
vrfcSn: "검증순번",
vrfcNm: "검증명",
trgtStatsCd: "대상통계코드",
trgtStatsNm: "대상통계명",
vrfcClsfCd: "검증분류코드",
vrfcClsfNm: "검증분류명",
dcptDigtCnt: "소수점자릿수",
dcptPrcsMthCd: "소수점처리방법코드",
dcptPrcsMthNm: "소수점처리방법명",
useYn: "사용여부",
regDt: "등록일시",
mdfcnDt: "수정일시",
[LOG_DETAIL_DETAILS_FIELD]: "상세",
};
/** 검증 규칙 로그 상세(vrfcSn 등) 컬럼 표시 순서 */
const VERIFICATION_FIELD_ORDER = [
"vrfcSn",
"vrfcNm",
"trgtStatsCd",
"trgtStatsNm",
"vrfcClsfCd",
"vrfcClsfNm",
"dcptDigtCnt",
"dcptPrcsMthCd",
"dcptPrcsMthNm",
"useYn",
"regDt",
"mdfcnDt",
] as const;
/** API `data`가 배열 또는 단일 객체일 때 그리드 행 배열로 정규화 */
export function normalizeLogDetailList(
data: StatisticsLogDetailRow[] | StatisticsLogDetailRow | null | undefined,
): StatisticsLogDetailRow[] {
if (Array.isArray(data)) return data;
if (data != null && typeof data === "object") return [data];
return [];
}
function parseLogDetailsValue(
value: unknown,
): LogDetailVerificationItem[] | null {
if (value == null || value === "") return null;
if (Array.isArray(value)) {
return value as LogDetailVerificationItem[];
}
if (typeof value === "string") {
try {
const parsed: unknown = JSON.parse(value);
return Array.isArray(parsed)
? (parsed as LogDetailVerificationItem[])
: null;
} catch {
return null;
}
}
return null;
}
/** details JSON 배열을 그리드 버튼 셀 + 숨김 raw 필드로 변환 */
export function prepareLogDetailRowsForGrid(
rows: StatisticsLogDetailRow[],
): Record<string, string | number>[] {
return rows.map((row) => {
const next: Record<string, string | number> = {};
for (const [key, value] of Object.entries(row)) {
if (key === LOG_DETAIL_DETAILS_FIELD) {
const parsed = parseLogDetailsValue(value);
if (parsed && parsed.length > 0) {
next[LOG_DETAIL_DETAILS_RAW_FIELD] = JSON.stringify(parsed);
next[LOG_DETAIL_DETAILS_FIELD] = "상세보기";
} else {
next[LOG_DETAIL_DETAILS_RAW_FIELD] = "";
next[LOG_DETAIL_DETAILS_FIELD] =
value === null || value === undefined
? ""
: typeof value === "object"
? ""
: String(value);
}
continue;
}
next[key] =
value === null || value === undefined
? ""
: (value as string | number);
}
if (
LOG_DETAIL_DETAILS_FIELD in row &&
!(LOG_DETAIL_DETAILS_RAW_FIELD in next)
) {
next[LOG_DETAIL_DETAILS_RAW_FIELD] = "";
}
return next;
});
}
function collectFieldNames(rows: StatisticsLogDetailRow[]): string[] {
const keys = new Set<string>();
for (const row of rows) {
for (const key of Object.keys(row)) {
keys.add(key);
}
}
const verification = VERIFICATION_FIELD_ORDER.filter((k) => keys.has(k));
const common = COMMON_FIELD_ORDER.filter((k) => keys.has(k));
const ordered = new Set<string>([
...verification,
...common,
LOG_DETAIL_DETAILS_FIELD,
]);
const rest = [...keys]
.filter((k) => !ordered.has(k) && k !== LOG_DETAIL_DETAILS_RAW_FIELD)
.sort((a, b) => a.localeCompare(b, "ko"));
const details = keys.has(LOG_DETAIL_DETAILS_FIELD)
? [LOG_DETAIL_DETAILS_FIELD]
: [];
return [...verification, ...common, ...rest, ...details];
}
function isNumericField(rows: StatisticsLogDetailRow[], fieldName: string): boolean {
return rows.some((row) => {
const v = row[fieldName];
return typeof v === "number" && !Number.isNaN(v);
});
}
function buildColumns(rows: StatisticsLogDetailRow[]): ColumnConfig[] {
const fieldNames = collectFieldNames(rows);
if (fieldNames.length === 0) {
return COMMON_FIELD_ORDER.map((fieldName) => ({
name: fieldName,
fieldName,
header: { text: FIELD_HEADER[fieldName] ?? fieldName },
editable: false,
width: Math.min(340, Math.max(90, fieldName.length * 9 + 28))
}));
}
return fieldNames.map((fieldName) => {
const numeric = isNumericField(rows, fieldName);
if (fieldName === LOG_DETAIL_DETAILS_FIELD) {
return {
name: fieldName,
fieldName,
header: { text: FIELD_HEADER[fieldName] ?? fieldName },
editable: false,
width: 100,
styleName: "rg-cell-outline-action-btn",
};
}
return {
name: fieldName,
fieldName,
header: { text: FIELD_HEADER[fieldName] ?? fieldName },
editable: false,
valueType: numeric ? ValueType.NUMBER : undefined,
dataFormat: numeric ? "#,##0.###" : undefined,
width: Math.min(340, Math.max(90, fieldName.length * 9 + 28)),
visible: fieldName !== "admdstCd",
};
});
}
function appendHiddenDetailsRawColumn(columns: ColumnConfig[]): ColumnConfig[] {
const hasDetails = columns.some(
(column) => column.fieldName === LOG_DETAIL_DETAILS_FIELD,
);
if (!hasDetails) return columns;
return [
...columns,
{
name: LOG_DETAIL_DETAILS_RAW_FIELD,
fieldName: LOG_DETAIL_DETAILS_RAW_FIELD,
header: { text: "" },
editable: false,
visible: false,
},
];
}
export function buildLogHistoryDetailColumnInfo(
rows: StatisticsLogDetailRow[],
): ColumnInfo {
const columns = appendHiddenDetailsRawColumn(buildColumns(rows));
return {
columns,
columnLayout: columns
.filter((column) => column.visible !== false)
.map((column) => column.fieldName),
};
}
/** 그리드 field 정의와 행 키를 맞춤 (누락 필드 → "") */
export function alignLogDetailRowsToColumns(
rows: Record<string, string | number>[],
columnInfo: ColumnInfo,
): Record<string, string | number>[] {
const fieldNames = columnInfo.columns.map((c) => c.fieldName);
return rows.map((row) => {
const aligned: Record<string, string | number> = {};
for (const fieldName of fieldNames) {
const value = row[fieldName];
aligned[fieldName] =
value === null || value === undefined ? "" : value;
}
return aligned;
});
}
export const emptyLogHistoryDetailColumnInfo =
buildLogHistoryDetailColumnInfo([]);