324 lines
10 KiB
TypeScript
324 lines
10 KiB
TypeScript
"use client";
|
|
|
|
import type { SelectDataType } from "@common/business_components";
|
|
import { formatDisplayData } from "@/components/lib/utils/formatDisplayValue";
|
|
import {
|
|
MS_SWAGE_PPL_CODES,
|
|
MS_SWAGE_PPL_USE_CODES,
|
|
} from "../common/table";
|
|
|
|
export const AGED_PIPE_LAYER_TYPE = "tb_ms_swage_ppl";
|
|
export const AGED_PIPE_DEFAULT_ELAPSED_YEARS = 30;
|
|
export const AGED_PIPE_PAGE_SIZE = 10;
|
|
export const AGED_PIPE_MIN_ZOOM = 13;
|
|
export const AGED_PIPE_GUIDE_MIN_ZOOM = 13.1;
|
|
export const AGED_PIPE_INVALID_INSTALL_YMD_PREFIXES = ["19000101"];
|
|
|
|
export type AgedPipeFilters = {
|
|
elapsedYears: number;
|
|
ctpvCd: string;
|
|
sggCd: string;
|
|
};
|
|
|
|
export type AgedPipeRow = {
|
|
fid: string;
|
|
lyrTypeNm: string;
|
|
rowNumber: number;
|
|
fcltMngNo: string;
|
|
instlYmd: string;
|
|
pdmtVl: string | number;
|
|
prlgVl: string | number;
|
|
usgCd: string;
|
|
mqltCd: string;
|
|
elapsedYears: number | string;
|
|
ctpvNm?: string;
|
|
sggNm?: string;
|
|
[key: string]: unknown;
|
|
};
|
|
|
|
type PipeSource = Record<string, unknown>;
|
|
|
|
const useMap = new Map(MS_SWAGE_PPL_USE_CODES.map((item) => [item.val.trim(), item.text]));
|
|
const materialMap = new Map(MS_SWAGE_PPL_CODES.map((item) => [item.val.trim(), item.text]));
|
|
|
|
export const AGED_PIPE_DEFAULT_FILTERS: AgedPipeFilters = {
|
|
elapsedYears: AGED_PIPE_DEFAULT_ELAPSED_YEARS,
|
|
ctpvCd: "0",
|
|
sggCd: "0",
|
|
};
|
|
|
|
const normalizeRegionFilterCode = (value: unknown, prefixLength: number) => {
|
|
const digits = String(value ?? "").replace(/[^0-9]/g, "");
|
|
if (digits === "" || digits === "0") return "0";
|
|
return digits.length > prefixLength ? digits.slice(0, prefixLength) : digits;
|
|
};
|
|
|
|
export const AGED_PIPE_LIST_HEADLIST = [
|
|
{ id: "rowNumber", title: "순번", widthPercent: 6 },
|
|
{ id: "fcltMngNo", title: "시설관리번호", widthPercent: 18 },
|
|
{ id: "instlYmd", title: "설치일자", widthPercent: 12 },
|
|
{ id: "pdmtVl", title: "관경(mm)", widthPercent: 10 },
|
|
{ id: "prlgVl", title: "연장(m)", widthPercent: 10 },
|
|
{ id: "usgCd", title: "용도", widthPercent: 12 },
|
|
{ id: "mqltCd", title: "재질", widthPercent: 18 },
|
|
{ id: "elapsedYears", title: "경과년도", widthPercent: 8, align: "right" },
|
|
];
|
|
|
|
export const AGED_PIPE_EXCEL_FIELDS = [
|
|
"ftreSmbolCd",
|
|
"fcltMngNo",
|
|
"pbadmsEmdCd",
|
|
"mapshtNo",
|
|
"mngInstCd",
|
|
"instlYmd",
|
|
"usgCd",
|
|
"mqltCd",
|
|
"fcltyShpCd",
|
|
"pdmtVl",
|
|
"wdthLen",
|
|
"vrtcLen",
|
|
"prlgVl",
|
|
"tmptDpth",
|
|
"scendDpth",
|
|
"avgGrdn",
|
|
"trflnCnt",
|
|
"exclnMltvArea",
|
|
"drtwMltvArea",
|
|
"rnyssnFvlcVl",
|
|
"clarFvlcVl",
|
|
"tmptMhlMngNo",
|
|
"scendMhlMngNo",
|
|
"instMngNo",
|
|
"fid",
|
|
"lyrTypeNm",
|
|
"swtrmtFcltCd",
|
|
"elapsedYears",
|
|
"ctpvNm",
|
|
"sggNm",
|
|
] as const;
|
|
|
|
export const AGED_PIPE_EXCEL_HEADER = {
|
|
ftreSmbolCd: "지형지물부호코드",
|
|
pbadmsEmdCd: "행정읍면동코드",
|
|
mapshtNo: "도엽번호",
|
|
mngInstCd: "관리기관코드",
|
|
wdthLen: "가로길이",
|
|
vrtcLen: "세로길이",
|
|
tmptDpth: "시점깊이",
|
|
scendDpth: "종점깊이",
|
|
avgGrdn: "평균구배",
|
|
trflnCnt: "차선수",
|
|
exclnMltvArea: "우수배수면적",
|
|
drtwMltvArea: "오수배수면적",
|
|
rnyssnFvlcVl: "우기유속값",
|
|
clarFvlcVl: "맑음유속값",
|
|
tmptMhlMngNo: "시점맨홀관리번호",
|
|
scendMhlMngNo: "종점맨홀관리번호",
|
|
instMngNo: "기관관리번호",
|
|
fid: "FID",
|
|
lyrTypeNm: "레이어타입명",
|
|
swtrmtFcltCd: "시설코드",
|
|
fcltyShpCd: "시설물형태코드",
|
|
fcltMngNo: "시설관리번호",
|
|
instlYmd: "설치일자",
|
|
pdmtVl: "관경(mm)",
|
|
prlgVl: "연장(m)",
|
|
usgCd: "용도",
|
|
mqltCd: "재질",
|
|
elapsedYears: "경과년도",
|
|
ctpvNm: "시도",
|
|
sggNm: "시군구",
|
|
};
|
|
|
|
const getExcelColumnLetter = (columnIndex: number) => {
|
|
let current = columnIndex;
|
|
let result = "";
|
|
|
|
while (current > 0) {
|
|
const remainder = (current - 1) % 26;
|
|
result = String.fromCharCode(65 + remainder) + result;
|
|
current = Math.floor((current - 1) / 26);
|
|
}
|
|
|
|
return result || "A";
|
|
};
|
|
|
|
export const AGED_PIPE_EXCEL_HEADER_RANGE = `A1:${getExcelColumnLetter(AGED_PIPE_EXCEL_FIELDS.length)}1`;
|
|
|
|
const pad2 = (value: number) => String(value).padStart(2, "0");
|
|
|
|
const normalizeYmd = (value: unknown) =>
|
|
String(value ?? "").replace(/[^0-9]/g, "").slice(0, 8);
|
|
|
|
const getToday = () => new Date();
|
|
|
|
export const normalizeAgedPipeFilters = (filters: AgedPipeFilters): AgedPipeFilters => {
|
|
const elapsedYears = Math.trunc(Number(filters.elapsedYears));
|
|
|
|
return {
|
|
...filters,
|
|
ctpvCd: normalizeRegionFilterCode(filters.ctpvCd, 2),
|
|
sggCd: normalizeRegionFilterCode(filters.sggCd, 5),
|
|
elapsedYears:
|
|
Number.isFinite(elapsedYears) && elapsedYears >= 1 && elapsedYears <= 100
|
|
? elapsedYears
|
|
: AGED_PIPE_DEFAULT_ELAPSED_YEARS,
|
|
};
|
|
};
|
|
|
|
export const validateElapsedYears = (value: number) =>
|
|
Number.isInteger(value) && value >= 1 && value <= 100;
|
|
|
|
export const getCutoffYmd = (elapsedYears: number) => {
|
|
const today = getToday();
|
|
const cutoff = new Date(today.getFullYear() - elapsedYears, today.getMonth(), today.getDate());
|
|
return `${cutoff.getFullYear()}${pad2(cutoff.getMonth() + 1)}${pad2(cutoff.getDate())}`;
|
|
};
|
|
|
|
export const getElapsedYears = (value: unknown) => {
|
|
const normalized = normalizeYmd(value);
|
|
if (normalized.length < 4 || isInvalidInstallYmd(value)) return null;
|
|
|
|
const year = Number(normalized.slice(0, 4));
|
|
const month = Number(normalized.slice(4, 6) || "1");
|
|
const day = Number(normalized.slice(6, 8) || "1");
|
|
if (!year) return null;
|
|
|
|
const today = getToday();
|
|
let elapsed = today.getFullYear() - year;
|
|
if (
|
|
today.getMonth() + 1 < month ||
|
|
(today.getMonth() + 1 === month && today.getDate() < day)
|
|
) {
|
|
elapsed -= 1;
|
|
}
|
|
|
|
return Math.max(elapsed, 0);
|
|
};
|
|
|
|
export const isInvalidInstallYmd = (value: unknown) => {
|
|
const normalized = normalizeYmd(value);
|
|
return (
|
|
normalized.length < 8 ||
|
|
AGED_PIPE_INVALID_INSTALL_YMD_PREFIXES.some((prefix) => normalized.startsWith(prefix))
|
|
);
|
|
};
|
|
|
|
export const getElapsedYearsDisplayText = (value: unknown) => {
|
|
const elapsedYears = getElapsedYears(value);
|
|
return elapsedYears == null ? "확인불가" : `${elapsedYears}년`;
|
|
};
|
|
|
|
export const buildAgedPipeCqlFilters = (filters: AgedPipeFilters) => {
|
|
const normalized = normalizeAgedPipeFilters(filters);
|
|
const cqlFilters = [
|
|
"instl_ymd IS NOT NULL",
|
|
...AGED_PIPE_INVALID_INSTALL_YMD_PREFIXES.map((prefix) => `instl_ymd NOT LIKE '${prefix}%'`),
|
|
`instl_ymd <= '${getCutoffYmd(normalized.elapsedYears)}'`,
|
|
];
|
|
|
|
if (normalized.sggCd !== "0") {
|
|
cqlFilters.push(`pbadms_emd_cd LIKE '${normalized.sggCd}%'`);
|
|
} else if (normalized.ctpvCd !== "0") {
|
|
cqlFilters.push(`pbadms_emd_cd LIKE '${normalized.ctpvCd}%'`);
|
|
}
|
|
|
|
return cqlFilters;
|
|
};
|
|
|
|
const getRegionCode = (source: PipeSource) =>
|
|
String(source.pbadmsEmdCd ?? source.pbadms_emd_cd ?? "").trim();
|
|
|
|
export const matchesAgedPipeFilters = (source: PipeSource, filters: AgedPipeFilters) => {
|
|
const normalized = normalizeAgedPipeFilters(filters);
|
|
const instlYmd = normalizeYmd(source.instl_ymd ?? source.instlYmd);
|
|
if (!instlYmd || isInvalidInstallYmd(instlYmd) || instlYmd > getCutoffYmd(normalized.elapsedYears)) return false;
|
|
|
|
const regionCode = getRegionCode(source);
|
|
if (normalized.sggCd !== "0") return regionCode.startsWith(normalized.sggCd);
|
|
if (normalized.ctpvCd !== "0") return regionCode.startsWith(normalized.ctpvCd);
|
|
return true;
|
|
};
|
|
|
|
const resolveRegionName = (
|
|
code: string,
|
|
directName: unknown,
|
|
options: SelectDataType[],
|
|
prefixLength: number,
|
|
) => {
|
|
const direct = String(directName ?? "").trim();
|
|
if (direct) return direct;
|
|
|
|
const prefix = code.slice(0, prefixLength);
|
|
return options.find((item) => String(item.val).trim() === prefix)?.text ?? "";
|
|
};
|
|
|
|
export const toDisplayPipeRow = (
|
|
source: PipeSource,
|
|
index: number,
|
|
page: number,
|
|
ctpvOptions: SelectDataType[],
|
|
sggOptions: SelectDataType[],
|
|
): AgedPipeRow => {
|
|
const formatted = formatDisplayData(source) as PipeSource;
|
|
const codePrefix = getRegionCode(source);
|
|
const instlYmd = formatted.instlYmd ?? source.instlYmd ?? formatted.instl_ymd ?? source.instl_ymd;
|
|
const invalidInstallYmd = isInvalidInstallYmd(source.instlYmd ?? source.instl_ymd ?? instlYmd);
|
|
const trimmedUse = String(source.usgCd ?? source.usg_cd ?? "").trim();
|
|
const trimmedMaterial = String(source.mqltCd ?? source.mqlt_cd ?? "").trim();
|
|
const pdmtVl = formatted.pdmtVl ?? source.pdmtVl ?? source.pdmt_vl ?? "-";
|
|
const prlgVl = formatted.prlgVl ?? source.prlgVl ?? source.prlg_vl ?? "-";
|
|
const fcltMngNo = String(formatted.fcltMngNo ?? source.fcltMngNo ?? source.fclt_mng_no ?? "-");
|
|
|
|
return {
|
|
...source,
|
|
...formatted,
|
|
fid: String(source.fid ?? ""),
|
|
lyrTypeNm: String(source.lyrTypeNm ?? AGED_PIPE_LAYER_TYPE),
|
|
rowNumber: (page - 1) * AGED_PIPE_PAGE_SIZE + index + 1,
|
|
fcltMngNo,
|
|
swtrmtFcltCd: source.swtrmtFcltCd ?? fcltMngNo,
|
|
instlYmd: invalidInstallYmd ? "확인불가" : String(instlYmd ?? "-"),
|
|
pdmtVl: typeof pdmtVl === "number" ? pdmtVl : String(pdmtVl),
|
|
prlgVl: typeof prlgVl === "number" ? prlgVl : String(prlgVl),
|
|
usgCd: useMap.get(trimmedUse) ?? String(formatted.usgCd ?? source.usgCd ?? source.usg_cd ?? "-"),
|
|
mqltCd:
|
|
materialMap.get(trimmedMaterial) ??
|
|
String(formatted.mqltCd ?? source.mqltCd ?? source.mqlt_cd ?? "-"),
|
|
elapsedYears: getElapsedYears(source.instlYmd ?? source.instl_ymd) ?? "확인불가",
|
|
ctpvNm: resolveRegionName(
|
|
codePrefix,
|
|
formatted.ctpvNm ?? source.ctpvNm ?? source.ctpv_nm,
|
|
ctpvOptions,
|
|
2,
|
|
),
|
|
sggNm: resolveRegionName(
|
|
codePrefix,
|
|
formatted.sggNm ?? source.sggNm ?? source.sgg_nm,
|
|
sggOptions,
|
|
5,
|
|
),
|
|
};
|
|
};
|
|
|
|
export const toAgedPipeExcelRow = (row: AgedPipeRow) => {
|
|
const exportRow: Record<string, unknown> = {};
|
|
|
|
AGED_PIPE_EXCEL_FIELDS.forEach((field) => {
|
|
exportRow[field] = row[field] ?? "-";
|
|
});
|
|
|
|
return exportRow;
|
|
};
|
|
|
|
export const isAgedPipeLayerNode = (node: any) => {
|
|
const data = node?.data ?? node;
|
|
const layerName = String(data?.lyrNm ?? node?.text ?? "");
|
|
const layerTypeName = String(data?.lyrTyNm ?? "");
|
|
|
|
return (
|
|
layerTypeName === "tb_old_pipe" ||
|
|
layerName.includes("노후관로")
|
|
);
|
|
};
|