734 lines
20 KiB
TypeScript
734 lines
20 KiB
TypeScript
/** 관로분석 검색 조건, 집계 컬럼과 표시값 변환 유틸을 제공한다. */
|
|
"use client";
|
|
|
|
import type { SelectDataType } from "@common/business_components";
|
|
import type { IUserInfo } from "@/shared/store";
|
|
import { MS_SWAGE_PPL_CODES, MS_SWAGE_PPL_USE_CODES } from "../common/table";
|
|
|
|
export const PIPE_ANALYSIS_DETAIL_ROW_SELECT_MESSAGE =
|
|
"SN_PIPE_ANALYSIS_DETAIL_ROW_SELECT";
|
|
|
|
export type PipeAnalysisType = "YEAR" | "MATERIAL" | "YEAR_MATERIAL";
|
|
|
|
export type PipeAnalysisSummaryFilters = {
|
|
analysisType: PipeAnalysisType;
|
|
ctpvCd: string;
|
|
sggCd: string;
|
|
swtrmtZoneNm: string;
|
|
scopeRegionCode: string;
|
|
};
|
|
|
|
export type PipeAnalysisDetailFilters = {
|
|
ctpvCd: string;
|
|
sggCd: string;
|
|
swtrmtZoneNm: string;
|
|
useCd: string;
|
|
materialCode: string;
|
|
diameterFrom: string;
|
|
diameterTo: string;
|
|
};
|
|
|
|
export type PipeAnalysisMaterialColumn = {
|
|
key: string;
|
|
materialCode: string;
|
|
materialName: string;
|
|
};
|
|
|
|
export type PipeAnalysisSummaryRow = {
|
|
rowNo: number;
|
|
ctpvCd: string;
|
|
ctpvNm?: string;
|
|
sggCd: string;
|
|
sggNm?: string;
|
|
swtrmtZoneNm: string;
|
|
totalLengthKm: number;
|
|
elapsedYearBucket?: string;
|
|
elapsedYearLabel?: string;
|
|
metrics: Record<string, number>;
|
|
};
|
|
|
|
export type PipeAnalysisSummaryResponse = {
|
|
analysisType: PipeAnalysisType;
|
|
materialColumns: PipeAnalysisMaterialColumn[];
|
|
data: PipeAnalysisSummaryRow[];
|
|
};
|
|
|
|
export type PipeAnalysisDetailRow = {
|
|
rowNo: number;
|
|
fid: string;
|
|
lyrTypeNm: string;
|
|
fcltMngNo: string;
|
|
instMngNo: string;
|
|
mngInstCd: string;
|
|
ctpvCd: string;
|
|
ctpvNm?: string;
|
|
sggCd: string;
|
|
sggNm?: string;
|
|
swtrmtZoneNm: string;
|
|
instlYmd: string;
|
|
elapsedYears?: number;
|
|
useCd: string;
|
|
materialCode: string;
|
|
pdmtVl?: number | string;
|
|
prlgVl?: number | string;
|
|
};
|
|
|
|
export type PipeAnalysisZoneOption = {
|
|
value: string;
|
|
label: string;
|
|
ctpvCd: string;
|
|
sggCd: string;
|
|
};
|
|
|
|
export type PipeAnalysisScope = {
|
|
regionCode: string;
|
|
ctpvCd: string;
|
|
sggCd: string;
|
|
ctpvLocked: boolean;
|
|
sggLocked: boolean;
|
|
};
|
|
|
|
export type PipeAnalysisCellFilter = {
|
|
elapsedYearBucket?: string;
|
|
materialCode?: string;
|
|
};
|
|
|
|
export type PipeAnalysisSummaryViewRow = Record<string, unknown> & {
|
|
__isNoZone: boolean;
|
|
__isSummaryTotal: boolean;
|
|
__cellFilters: Record<string, PipeAnalysisCellFilter>;
|
|
__base: {
|
|
ctpvCd: string;
|
|
sggCd: string;
|
|
swtrmtZoneNm: string;
|
|
};
|
|
};
|
|
|
|
export type PipeAnalysisYearMaterialGroupRow = {
|
|
elapsedYearBucket: string;
|
|
elapsedYearLabel: string;
|
|
metrics: Record<string, number>;
|
|
cellFilters: Record<string, PipeAnalysisCellFilter>;
|
|
};
|
|
|
|
export type PipeAnalysisYearMaterialGroup = {
|
|
rowNo: number;
|
|
ctpvNm: string;
|
|
sggNm: string;
|
|
swtrmtZoneNm: string;
|
|
totalLengthKm: number;
|
|
base: {
|
|
ctpvCd: string;
|
|
sggCd: string;
|
|
swtrmtZoneNm: string;
|
|
};
|
|
rows: PipeAnalysisYearMaterialGroupRow[];
|
|
};
|
|
|
|
const materialMap = new Map(
|
|
MS_SWAGE_PPL_CODES.map((item) => [item.val.trim(), item.text]),
|
|
);
|
|
const useMap = new Map(
|
|
MS_SWAGE_PPL_USE_CODES.map((item) => [item.val.trim(), item.text]),
|
|
);
|
|
|
|
export const PIPE_ANALYSIS_TYPE_OPTIONS: Array<{
|
|
value: PipeAnalysisType;
|
|
label: string;
|
|
}> = [
|
|
{ value: "YEAR", label: "경과년도별" },
|
|
{ value: "MATERIAL", label: "재질별" },
|
|
{ value: "YEAR_MATERIAL", label: "경과년도&재질" },
|
|
];
|
|
|
|
export const PIPE_ANALYSIS_SUMMARY_DEFAULT_FILTERS: PipeAnalysisSummaryFilters =
|
|
{
|
|
analysisType: "YEAR",
|
|
// 관로분석 최초 조회·초기화의 기본 시도는 부산광역시다. 계정 지역 제한은 별도 적용한다.
|
|
ctpvCd: "26",
|
|
sggCd: "0",
|
|
swtrmtZoneNm: "0",
|
|
scopeRegionCode: "",
|
|
};
|
|
|
|
export const PIPE_ANALYSIS_DETAIL_DEFAULT_FILTERS: PipeAnalysisDetailFilters = {
|
|
ctpvCd: "0",
|
|
sggCd: "0",
|
|
swtrmtZoneNm: "0",
|
|
useCd: "0",
|
|
materialCode: "0",
|
|
diameterFrom: "",
|
|
diameterTo: "",
|
|
};
|
|
|
|
export const PIPE_ANALYSIS_FIXED_YEAR_COLUMNS = [
|
|
{ key: "LT10", title: "10년 미만(km)" },
|
|
{ key: "Y10_20", title: "10년~20년(km)" },
|
|
{ key: "Y20_30", title: "20년~30년(km)" },
|
|
{ key: "GE30", title: "30년 이상(km)" },
|
|
{ key: "UNKNOWN", title: "확인 불가(km)" },
|
|
];
|
|
|
|
const elapsedYearBucketLabelMap: Record<string, string> = {
|
|
LT10: "10년 미만",
|
|
Y10_20: "10년~20년",
|
|
Y20_30: "20년~30년",
|
|
GE30: "30년 이상",
|
|
UNKNOWN: "확인 불가",
|
|
};
|
|
|
|
const YEAR_MATERIAL_BASE_BUCKETS = ["LT10", "Y10_20", "Y20_30", "GE30"];
|
|
|
|
export const PIPE_ANALYSIS_DETAIL_HEADLIST = [
|
|
{ id: "rowNo", title: "순번", widthPercent: 6 },
|
|
{ id: "ctpvNm", title: "시도", widthPercent: 9 },
|
|
{ id: "sggNm", title: "시군구", widthPercent: 10 },
|
|
{ id: "fcltMngNo", title: "시설관리번호", widthPercent: 15 },
|
|
{ id: "instMngNo", title: "기관관리번호", widthPercent: 15 },
|
|
{ id: "swtrmtZoneNm", title: "처리구역", widthPercent: 12 },
|
|
{ id: "instlYmd", title: "설치일자", widthPercent: 10 },
|
|
{ id: "useNm", title: "용도", widthPercent: 9 },
|
|
{ id: "pdmtVl", title: "관경(mm)", widthPercent: 8, align: "right" },
|
|
{ id: "prlgVl", title: "연장(m)", widthPercent: 8, align: "right" },
|
|
{ id: "materialName", title: "관재질", widthPercent: 12 },
|
|
];
|
|
|
|
export const buildPipeAnalysisScope = (
|
|
userInfo: IUserInfo | null,
|
|
): PipeAnalysisScope => {
|
|
const admin = userInfo?.adminRoleYn === "Y";
|
|
if (admin) {
|
|
return {
|
|
regionCode: "",
|
|
ctpvCd: "",
|
|
sggCd: "",
|
|
ctpvLocked: false,
|
|
sggLocked: false,
|
|
};
|
|
}
|
|
|
|
const digits = String(userInfo?.laeSeCd ?? "").replace(/[^0-9]/g, "");
|
|
if (digits.length >= 5) {
|
|
return {
|
|
regionCode: digits.slice(0, 5),
|
|
ctpvCd: digits.slice(0, 2),
|
|
sggCd: digits.slice(0, 5),
|
|
ctpvLocked: true,
|
|
sggLocked: true,
|
|
};
|
|
}
|
|
if (digits.length >= 2) {
|
|
return {
|
|
regionCode: digits.slice(0, 2),
|
|
ctpvCd: digits.slice(0, 2),
|
|
sggCd: "",
|
|
ctpvLocked: true,
|
|
sggLocked: false,
|
|
};
|
|
}
|
|
|
|
return {
|
|
regionCode: "",
|
|
ctpvCd: "",
|
|
sggCd: "",
|
|
ctpvLocked: false,
|
|
sggLocked: false,
|
|
};
|
|
};
|
|
|
|
export const applyScopeToSummaryFilters = (
|
|
filters: PipeAnalysisSummaryFilters,
|
|
scope: PipeAnalysisScope,
|
|
): PipeAnalysisSummaryFilters => {
|
|
const next = { ...filters, scopeRegionCode: scope.regionCode };
|
|
if (scope.ctpvCd) {
|
|
next.ctpvCd = scope.ctpvCd;
|
|
}
|
|
if (scope.sggCd) {
|
|
next.sggCd = scope.sggCd;
|
|
} else if (scope.ctpvCd && !next.sggCd.startsWith(scope.ctpvCd)) {
|
|
next.sggCd = "0";
|
|
}
|
|
return next;
|
|
};
|
|
|
|
export const applyScopeToDetailFilters = (
|
|
filters: PipeAnalysisDetailFilters,
|
|
scope: PipeAnalysisScope,
|
|
): PipeAnalysisDetailFilters => {
|
|
const next = { ...filters };
|
|
if (scope.ctpvCd) {
|
|
next.ctpvCd = scope.ctpvCd;
|
|
}
|
|
if (scope.sggCd) {
|
|
next.sggCd = scope.sggCd;
|
|
} else if (scope.ctpvCd && !next.sggCd.startsWith(scope.ctpvCd)) {
|
|
next.sggCd = "0";
|
|
}
|
|
return next;
|
|
};
|
|
|
|
export const buildPipeAnalysisSummaryHeadList = (
|
|
analysisType: PipeAnalysisType,
|
|
materialColumns: PipeAnalysisMaterialColumn[],
|
|
) => {
|
|
const common = [
|
|
{ id: "rowNo", title: "순번", widthPercent: 6 },
|
|
{ id: "ctpvNm", title: "시도", widthPercent: 10 },
|
|
{ id: "sggNm", title: "시군구", widthPercent: 10 },
|
|
{ id: "swtrmtZoneNm", title: "처리구역", widthPercent: 14 },
|
|
{
|
|
id: "totalLengthKm",
|
|
title: "합계(km)",
|
|
widthPercent: 10,
|
|
align: "right",
|
|
},
|
|
];
|
|
|
|
if (analysisType === "YEAR") {
|
|
return [
|
|
...common,
|
|
...PIPE_ANALYSIS_FIXED_YEAR_COLUMNS.map((column) => ({
|
|
id: column.key,
|
|
title: column.title,
|
|
widthPercent: 12,
|
|
align: "right" as const,
|
|
})),
|
|
];
|
|
}
|
|
|
|
const materialHeads = materialColumns.map((column) => ({
|
|
id: column.key,
|
|
title: resolveMaterialColumnName(column),
|
|
widthPercent: 12,
|
|
align: "right" as const,
|
|
}));
|
|
|
|
if (analysisType === "YEAR_MATERIAL") {
|
|
return [
|
|
...common,
|
|
{ id: "elapsedYearLabel", title: "경과년도", widthPercent: 12 },
|
|
...materialHeads,
|
|
];
|
|
}
|
|
|
|
return [...common, ...materialHeads];
|
|
};
|
|
|
|
export const buildPipeAnalysisSummaryViewRows = (
|
|
response: PipeAnalysisSummaryResponse,
|
|
ctpvOptions: SelectDataType[],
|
|
sggOptions: SelectDataType[],
|
|
): PipeAnalysisSummaryViewRow[] => {
|
|
const materialCodes = response.materialColumns.map((column) => column.key);
|
|
|
|
return response.data.map((row) => {
|
|
const isNoZone = String(row.swtrmtZoneNm ?? "").trim() === "NO_ZONE";
|
|
const isSummaryTotal =
|
|
!String(row.ctpvCd ?? "").trim() &&
|
|
String(row.swtrmtZoneNm ?? "").trim() === "-";
|
|
const viewRow: PipeAnalysisSummaryViewRow = {
|
|
rowNo: row.rowNo,
|
|
ctpvNm: isNoZone
|
|
? "전체"
|
|
: row.ctpvNm || resolveRegionName(row.ctpvCd, ctpvOptions, 2),
|
|
sggNm: isNoZone
|
|
? "-"
|
|
: row.sggNm || resolveRegionName(row.sggCd, sggOptions, 5),
|
|
swtrmtZoneNm: isNoZone ? "-" : row.swtrmtZoneNm || "-",
|
|
totalLengthKm: row.totalLengthKm ?? 0,
|
|
elapsedYearBucket: row.elapsedYearBucket ?? "",
|
|
elapsedYearLabel: row.elapsedYearLabel ?? "",
|
|
__isNoZone: isNoZone,
|
|
__isSummaryTotal: isSummaryTotal,
|
|
__cellFilters: {},
|
|
__base: {
|
|
ctpvCd: isNoZone ? "" : row.ctpvCd,
|
|
sggCd: isNoZone ? "" : row.sggCd,
|
|
swtrmtZoneNm: isNoZone ? "NO_ZONE" : row.swtrmtZoneNm,
|
|
},
|
|
};
|
|
|
|
if (isSummaryTotal) {
|
|
viewRow.ctpvNm = row.ctpvNm || viewRow.ctpvNm;
|
|
viewRow.sggNm = row.sggNm || "-";
|
|
viewRow.swtrmtZoneNm = "-";
|
|
viewRow.__base.ctpvCd = "";
|
|
viewRow.__base.sggCd = "";
|
|
viewRow.__base.swtrmtZoneNm = "";
|
|
}
|
|
|
|
if (response.analysisType === "YEAR") {
|
|
if (isNoZone || isSummaryTotal) {
|
|
PIPE_ANALYSIS_FIXED_YEAR_COLUMNS.forEach((column) => {
|
|
viewRow[column.key] = Number(row.metrics?.[column.key] ?? 0);
|
|
});
|
|
return viewRow;
|
|
}
|
|
PIPE_ANALYSIS_FIXED_YEAR_COLUMNS.forEach((column) => {
|
|
const value = Number(row.metrics?.[column.key] ?? 0);
|
|
viewRow[column.key] = value;
|
|
if (value > 0) {
|
|
viewRow.__cellFilters[column.key] = {
|
|
elapsedYearBucket: column.key,
|
|
};
|
|
}
|
|
});
|
|
return viewRow;
|
|
}
|
|
|
|
const columns =
|
|
response.analysisType === "YEAR_MATERIAL" ? materialCodes : materialCodes;
|
|
|
|
columns.forEach((columnKey) => {
|
|
const value = Number(row.metrics?.[columnKey] ?? 0);
|
|
viewRow[columnKey] = value;
|
|
if (!isNoZone && !isSummaryTotal && value > 0) {
|
|
viewRow.__cellFilters[columnKey] = {
|
|
elapsedYearBucket:
|
|
response.analysisType === "YEAR_MATERIAL"
|
|
? row.elapsedYearBucket
|
|
: undefined,
|
|
materialCode: columnKey === "OTHER" ? "" : columnKey,
|
|
};
|
|
}
|
|
});
|
|
|
|
return viewRow;
|
|
});
|
|
};
|
|
|
|
export const buildPipeAnalysisYearMaterialGroups = (
|
|
rows: PipeAnalysisSummaryViewRow[],
|
|
materialColumns: PipeAnalysisMaterialColumn[],
|
|
): PipeAnalysisYearMaterialGroup[] => {
|
|
const materialKeys = materialColumns.map((column) => column.key);
|
|
const groupMap = new Map<string, PipeAnalysisYearMaterialGroup>();
|
|
|
|
rows.forEach((row) => {
|
|
if (row.__isNoZone || row.__isSummaryTotal) {
|
|
return;
|
|
}
|
|
|
|
const groupKey = `${row.__base.ctpvCd}|${row.__base.sggCd}|${row.__base.swtrmtZoneNm}`;
|
|
const elapsedYearBucket = String(row.elapsedYearBucket ?? "").trim();
|
|
|
|
let group = groupMap.get(groupKey);
|
|
if (!group) {
|
|
group = {
|
|
rowNo: groupMap.size + 1,
|
|
ctpvNm: String(row.ctpvNm ?? ""),
|
|
sggNm: String(row.sggNm ?? ""),
|
|
swtrmtZoneNm: String(row.swtrmtZoneNm ?? ""),
|
|
totalLengthKm: Number(row.totalLengthKm ?? 0),
|
|
base: {
|
|
ctpvCd: row.__base.ctpvCd,
|
|
sggCd: row.__base.sggCd,
|
|
swtrmtZoneNm: row.__base.swtrmtZoneNm,
|
|
},
|
|
rows: [],
|
|
};
|
|
groupMap.set(groupKey, group);
|
|
}
|
|
|
|
const metrics: Record<string, number> = {};
|
|
materialKeys.forEach((materialKey) => {
|
|
metrics[materialKey] = Number(row[materialKey] ?? 0);
|
|
});
|
|
|
|
group.rows.push({
|
|
elapsedYearBucket,
|
|
elapsedYearLabel: String(
|
|
row.elapsedYearLabel ??
|
|
resolveElapsedYearBucketLabel(elapsedYearBucket),
|
|
),
|
|
metrics,
|
|
cellFilters: row.__cellFilters,
|
|
});
|
|
});
|
|
|
|
return Array.from(groupMap.values()).map((group) => {
|
|
const rowByBucket = new Map(
|
|
group.rows.map((row) => [row.elapsedYearBucket, row]),
|
|
);
|
|
const orderedBuckets = [...YEAR_MATERIAL_BASE_BUCKETS];
|
|
|
|
if (rowByBucket.has("UNKNOWN")) {
|
|
orderedBuckets.push("UNKNOWN");
|
|
}
|
|
|
|
const normalizedRows = orderedBuckets.map((bucket) => {
|
|
const existing = rowByBucket.get(bucket);
|
|
if (existing) {
|
|
return existing;
|
|
}
|
|
|
|
const emptyMetrics: Record<string, number> = {};
|
|
materialKeys.forEach((materialKey) => {
|
|
emptyMetrics[materialKey] = 0;
|
|
});
|
|
|
|
return {
|
|
elapsedYearBucket: bucket,
|
|
elapsedYearLabel: resolveElapsedYearBucketLabel(bucket),
|
|
metrics: emptyMetrics,
|
|
cellFilters: {},
|
|
};
|
|
});
|
|
|
|
return {
|
|
...group,
|
|
rows: normalizedRows,
|
|
};
|
|
});
|
|
};
|
|
|
|
export const toPipeAnalysisDetailViewRows = (
|
|
rows: PipeAnalysisDetailRow[],
|
|
ctpvOptions: SelectDataType[],
|
|
sggOptions: SelectDataType[],
|
|
) =>
|
|
rows.map((row) => ({
|
|
...row,
|
|
ctpvNm: row.ctpvNm || resolveRegionName(row.ctpvCd, ctpvOptions, 2),
|
|
sggNm: row.sggNm || resolveRegionName(row.sggCd, sggOptions, 5),
|
|
useNm: resolveUseName(row.useCd),
|
|
materialName: resolveMaterialName(row.materialCode),
|
|
}));
|
|
|
|
export const buildPipeAnalysisSummaryDownloadHeaders = (
|
|
analysisType: PipeAnalysisType,
|
|
materialColumns: PipeAnalysisMaterialColumn[],
|
|
) => {
|
|
const headers: Record<string, string> = {
|
|
rowNo: "순번",
|
|
ctpvNm: "시도",
|
|
sggNm: "시군구",
|
|
swtrmtZoneNm: "처리구역",
|
|
totalLengthKm: "합계(km)",
|
|
};
|
|
|
|
if (analysisType === "YEAR_MATERIAL") {
|
|
headers.elapsedYearLabel = "경과년도";
|
|
}
|
|
|
|
if (analysisType === "YEAR") {
|
|
PIPE_ANALYSIS_FIXED_YEAR_COLUMNS.forEach((column) => {
|
|
headers[column.key] = column.title;
|
|
});
|
|
return headers;
|
|
}
|
|
|
|
materialColumns.forEach((column) => {
|
|
headers[column.key] = resolveMaterialColumnName(column);
|
|
});
|
|
return headers;
|
|
};
|
|
|
|
export const PIPE_ANALYSIS_DETAIL_EXCEL_HEADERS = {
|
|
rowNo: "순번",
|
|
ctpvNm: "시도",
|
|
sggNm: "시군구",
|
|
fcltMngNo: "시설관리번호",
|
|
instMngNo: "기관관리번호",
|
|
swtrmtZoneNm: "처리구역",
|
|
instlYmd: "설치일자",
|
|
useNm: "용도",
|
|
pdmtVl: "관경(mm)",
|
|
prlgVl: "연장(m)",
|
|
materialName: "관재질",
|
|
};
|
|
|
|
export const resolveMaterialName = (materialCode?: string) => {
|
|
const code = String(materialCode ?? "").trim();
|
|
if (!code) return "-";
|
|
if (code === "OTHER") return "기타";
|
|
return materialMap.get(code) ?? code;
|
|
};
|
|
|
|
/**
|
|
* 관재질 라벨에서 영문 약어/코드를 걷어내고 엑셀 헤더용 한글명을 우선 추출한다.
|
|
* - `염화비닐관(PVC)` -> `염화비닐관`
|
|
* - `PCF관(강판보강 PE 나선형관)` -> `강판보강 나선형관`
|
|
*/
|
|
const toKoreanMaterialLabel = (value?: string) => {
|
|
const normalized = String(value ?? "").trim();
|
|
if (!normalized) return "";
|
|
|
|
const withoutParenthesis = normalized
|
|
.replace(/\s*\([^)]*\)\s*/g, " ")
|
|
.replace(/\s+/g, " ")
|
|
.trim();
|
|
const withoutAsciiToken = withoutParenthesis
|
|
.replace(/[A-Za-z0-9-]+/g, " ")
|
|
.replace(/\s+/g, " ")
|
|
.trim();
|
|
|
|
if (/[가-힣]/.test(withoutAsciiToken)) {
|
|
return withoutAsciiToken;
|
|
}
|
|
|
|
if (/[가-힣]/.test(withoutParenthesis)) {
|
|
return withoutParenthesis;
|
|
}
|
|
|
|
const parenthesisMatches = Array.from(normalized.matchAll(/\(([^)]*)\)/g))
|
|
.map((match) => String(match[1] ?? "").trim())
|
|
.filter(Boolean);
|
|
const koreanParenthesisText = parenthesisMatches.find((text) =>
|
|
/[가-힣]/.test(text),
|
|
);
|
|
|
|
if (koreanParenthesisText) {
|
|
const simplifiedParenthesisText = koreanParenthesisText
|
|
.replace(/[A-Za-z0-9-]+/g, " ")
|
|
.replace(/\s+/g, " ")
|
|
.trim();
|
|
return simplifiedParenthesisText || koreanParenthesisText;
|
|
}
|
|
|
|
return normalized;
|
|
};
|
|
|
|
export const resolveMaterialColumnName = (
|
|
column: PipeAnalysisMaterialColumn,
|
|
) => {
|
|
const rawName = String(column.materialName ?? "").trim();
|
|
const rawCode = String(column.materialCode ?? "").trim();
|
|
const rawKey = String(column.key ?? "").trim();
|
|
const mappedName = resolveMaterialName(rawCode || rawKey);
|
|
const candidates = [
|
|
toKoreanMaterialLabel(mappedName),
|
|
toKoreanMaterialLabel(rawName),
|
|
mappedName,
|
|
rawName,
|
|
rawCode,
|
|
rawKey,
|
|
].filter(Boolean);
|
|
|
|
return candidates[0] ?? "-";
|
|
};
|
|
|
|
export const resolveMaterialKoreanName = (materialCode?: string) => {
|
|
const resolved = resolveMaterialName(materialCode);
|
|
const koreanLabel = toKoreanMaterialLabel(resolved);
|
|
return koreanLabel || resolved;
|
|
};
|
|
|
|
export const resolveElapsedYearBucketLabel = (bucket?: string) => {
|
|
const normalized = String(bucket ?? "").trim();
|
|
return elapsedYearBucketLabelMap[normalized] ?? normalized;
|
|
};
|
|
|
|
export const resolvePipeAnalysisTypeLabel = (analysisType?: string) => {
|
|
const normalized = String(analysisType ?? "").trim();
|
|
return (
|
|
PIPE_ANALYSIS_TYPE_OPTIONS.find((option) => option.value === normalized)
|
|
?.label ?? normalized
|
|
);
|
|
};
|
|
|
|
export const resolveUseName = (useCode?: string) => {
|
|
const code = String(useCode ?? "").trim();
|
|
if (!code) return "-";
|
|
return useMap.get(code) ?? code;
|
|
};
|
|
|
|
export const resolveRegionName = (
|
|
code: string,
|
|
options: SelectDataType[],
|
|
prefixLength: number,
|
|
) => {
|
|
const normalized = String(code ?? "").trim();
|
|
if (!normalized) return "-";
|
|
const prefix = normalized.slice(0, prefixLength);
|
|
return (
|
|
options.find((item) => String(item.val).trim() === prefix)?.text ?? prefix
|
|
);
|
|
};
|
|
|
|
export const normalizeNumberInput = (value: string) =>
|
|
value.replace(/[^0-9]/g, "");
|
|
|
|
export const buildPipeAnalysisFileDate = () =>
|
|
new Date().toISOString().slice(0, 10);
|
|
|
|
export const fetchPipeAnalysisZoneOptions = async ({
|
|
ctpvCd,
|
|
sggCd,
|
|
sggNm,
|
|
scopeRegionCode,
|
|
}: {
|
|
ctpvCd: string;
|
|
sggCd: string;
|
|
sggNm?: string;
|
|
scopeRegionCode?: string;
|
|
}): Promise<PipeAnalysisZoneOption[]> => {
|
|
const filters: string[] = [];
|
|
const selectedSggCd = String(sggCd ?? "").trim();
|
|
const selectedSggNm = String(sggNm ?? "").trim();
|
|
if (sggCd && sggCd !== "0") {
|
|
// 처리구역 원천의 시군구코드가 잘못된 경우도 있어 시도 범위로 받은 뒤
|
|
// 기준코드 또는 시군구명 중 하나가 일치하는 처리구역만 남긴다.
|
|
const selectedCtpvCd =
|
|
ctpvCd && ctpvCd !== "0" ? ctpvCd : selectedSggCd.slice(0, 2);
|
|
filters.push(`ctpv_cd='${selectedCtpvCd}'`);
|
|
} else if (ctpvCd && ctpvCd !== "0") {
|
|
filters.push(`ctpv_cd='${ctpvCd}'`);
|
|
} else if (scopeRegionCode) {
|
|
if (scopeRegionCode.length >= 5) {
|
|
filters.push(`sgg_cd='${scopeRegionCode.slice(0, 5)}'`);
|
|
} else if (scopeRegionCode.length >= 2) {
|
|
filters.push(`ctpv_cd='${scopeRegionCode.slice(0, 2)}'`);
|
|
}
|
|
}
|
|
|
|
const params = new URLSearchParams({
|
|
service: "WFS",
|
|
version: "1.0.0",
|
|
request: "GetFeature",
|
|
typeName: "hasudo:tb_ms_swage_prcs_zone",
|
|
outputFormat: "application/json",
|
|
propertyName: "ctpv_cd,sgg_cd,sgg_nm,swtrmt_zone_nm",
|
|
});
|
|
|
|
if (filters.length > 0) {
|
|
params.set("CQL_FILTER", filters.join(" and "));
|
|
}
|
|
|
|
const response = await fetch(`/geoserver/hasudo/ows?${params.toString()}`);
|
|
if (!response.ok) {
|
|
throw new Error("처리구역 목록을 불러오지 못했습니다.");
|
|
}
|
|
|
|
const data = await response.json();
|
|
const features = Array.isArray(data?.features) ? data.features : [];
|
|
const dedupMap = new Map<string, PipeAnalysisZoneOption>();
|
|
|
|
features.forEach((feature: any) => {
|
|
const properties = feature?.properties ?? {};
|
|
const zoneName = String(properties.swtrmt_zone_nm ?? "").trim();
|
|
const featureSggCd = String(properties.sgg_cd ?? "").trim();
|
|
const featureSggNm = String(properties.sgg_nm ?? "").trim();
|
|
if (!zoneName) return;
|
|
if (
|
|
selectedSggCd &&
|
|
selectedSggCd !== "0" &&
|
|
featureSggCd !== selectedSggCd &&
|
|
(!selectedSggNm || featureSggNm !== selectedSggNm)
|
|
) {
|
|
return;
|
|
}
|
|
dedupMap.set(zoneName, {
|
|
value: zoneName,
|
|
label: zoneName,
|
|
ctpvCd: String(properties.ctpv_cd ?? "").trim(),
|
|
sggCd: selectedSggCd !== "0" ? selectedSggCd : featureSggCd,
|
|
});
|
|
});
|
|
|
|
return Array.from(dedupMap.values()).sort((left, right) =>
|
|
left.label.localeCompare(right.label, "ko"),
|
|
);
|
|
};
|