186 lines
5.3 KiB
TypeScript
186 lines
5.3 KiB
TypeScript
/** 시도별 노후관로 현황의 조회 연도와 그룹 컬럼을 구성한다. */
|
|
"use client";
|
|
|
|
import type { UiHeadProps } from "@common/business_components/ui";
|
|
|
|
export const AGED_PIPE_STATUS_BY_CTPV_DEFAULT_ELAPSED_YEARS = 30;
|
|
export const AGED_PIPE_STATUS_BY_CTPV_MAX_YEAR_SPAN = 3;
|
|
export const AGED_PIPE_STATUS_BY_CTPV_PAGE_SIZE = 50;
|
|
|
|
export type AgedPipeStatusByCtpvFilters = {
|
|
startYear: number;
|
|
endYear: number;
|
|
elapsedYears: number;
|
|
};
|
|
|
|
export type AgedPipeStatusByCtpvYearStat = {
|
|
year: number;
|
|
totalLengthKm: number;
|
|
agedLengthKm: number;
|
|
agedRate: number;
|
|
};
|
|
|
|
export type AgedPipeStatusByCtpvRow = {
|
|
rowNumber: number;
|
|
ctpvCd: string;
|
|
ctpvNm: string;
|
|
yearStats: AgedPipeStatusByCtpvYearStat[];
|
|
[key: string]: string | number | AgedPipeStatusByCtpvYearStat[];
|
|
};
|
|
|
|
export type AgedPipeStatusByCtpvResponse = {
|
|
startYear: number;
|
|
endYear: number;
|
|
elapsedYears: number;
|
|
years: number[];
|
|
rows: Array<{
|
|
ctpvCd: string;
|
|
ctpvNm: string;
|
|
yearStats: AgedPipeStatusByCtpvYearStat[];
|
|
}>;
|
|
};
|
|
|
|
export const normalizeAgedPipeStatusByCtpvResponse = (
|
|
response: Partial<AgedPipeStatusByCtpvResponse> | null | undefined,
|
|
): AgedPipeStatusByCtpvResponse => ({
|
|
startYear: Number(response?.startYear ?? 0),
|
|
endYear: Number(response?.endYear ?? 0),
|
|
elapsedYears: Number(
|
|
response?.elapsedYears ?? AGED_PIPE_STATUS_BY_CTPV_DEFAULT_ELAPSED_YEARS,
|
|
),
|
|
years: Array.isArray(response?.years)
|
|
? response.years.map((year) => Number(year))
|
|
: [],
|
|
rows: Array.isArray(response?.rows) ? response.rows : [],
|
|
});
|
|
|
|
export const buildAgedPipeStatusYearOptions = () => {
|
|
const currentYear = new Date().getFullYear();
|
|
return Array.from(
|
|
{ length: currentYear - 1900 + 1 },
|
|
(_, index) => currentYear - index,
|
|
);
|
|
};
|
|
|
|
export const normalizeAgedPipeStatusByCtpvFilters = (
|
|
filters: Partial<AgedPipeStatusByCtpvFilters>,
|
|
): AgedPipeStatusByCtpvFilters => {
|
|
const currentYear = new Date().getFullYear();
|
|
let startYear = Math.trunc(Number(filters.startYear ?? currentYear - 2));
|
|
let endYear = Math.trunc(Number(filters.endYear ?? currentYear));
|
|
let elapsedYears = Math.trunc(
|
|
Number(
|
|
filters.elapsedYears ?? AGED_PIPE_STATUS_BY_CTPV_DEFAULT_ELAPSED_YEARS,
|
|
),
|
|
);
|
|
|
|
if (!Number.isFinite(startYear)) startYear = currentYear - 2;
|
|
if (!Number.isFinite(endYear)) endYear = currentYear;
|
|
if (
|
|
!Number.isFinite(elapsedYears) ||
|
|
elapsedYears < 1 ||
|
|
elapsedYears > 100
|
|
) {
|
|
elapsedYears = AGED_PIPE_STATUS_BY_CTPV_DEFAULT_ELAPSED_YEARS;
|
|
}
|
|
if (startYear > endYear) {
|
|
const swap = startYear;
|
|
startYear = endYear;
|
|
endYear = swap;
|
|
}
|
|
|
|
return {
|
|
startYear,
|
|
endYear,
|
|
elapsedYears,
|
|
};
|
|
};
|
|
|
|
/** 입력한 기준년도·경과년도가 조회 가능한 범위인지 사용자 안내 문구로 반환한다. */
|
|
export const getAgedPipeStatusByCtpvValidationMessage = (
|
|
filters: AgedPipeStatusByCtpvFilters,
|
|
) => {
|
|
if (filters.startYear > filters.endYear) {
|
|
return "기준년도 시작은 종료보다 늦을 수 없습니다.";
|
|
}
|
|
if (
|
|
filters.endYear - filters.startYear + 1 >
|
|
AGED_PIPE_STATUS_BY_CTPV_MAX_YEAR_SPAN
|
|
) {
|
|
return `기준년도 조회 범위는 최대 ${AGED_PIPE_STATUS_BY_CTPV_MAX_YEAR_SPAN}년까지 가능합니다.`;
|
|
}
|
|
if (filters.elapsedYears < 1 || filters.elapsedYears > 100) {
|
|
return "노후관로 기준은 1년부터 100년까지 입력할 수 있습니다.";
|
|
}
|
|
return null;
|
|
};
|
|
|
|
export const validateAgedPipeStatusByCtpvFilters = (
|
|
filters: AgedPipeStatusByCtpvFilters,
|
|
) => getAgedPipeStatusByCtpvValidationMessage(filters) === null;
|
|
|
|
const formatMetric = (value: number) =>
|
|
Number(value ?? 0).toLocaleString("ko-KR", {
|
|
minimumFractionDigits: 2,
|
|
maximumFractionDigits: 2,
|
|
});
|
|
|
|
export const toAgedPipeStatusByCtpvRows = (
|
|
response: Partial<AgedPipeStatusByCtpvResponse> | null | undefined,
|
|
): AgedPipeStatusByCtpvRow[] =>
|
|
normalizeAgedPipeStatusByCtpvResponse(response).rows.map((row, index) => {
|
|
const displayRow: AgedPipeStatusByCtpvRow = {
|
|
rowNumber: index + 1,
|
|
ctpvCd: String(row.ctpvCd ?? "").trim(),
|
|
ctpvNm: String(row.ctpvNm ?? "").trim(),
|
|
yearStats: row.yearStats ?? [],
|
|
};
|
|
|
|
(row.yearStats ?? []).forEach((yearStat) => {
|
|
displayRow[`year${yearStat.year}TotalLengthKm`] = formatMetric(
|
|
Number(yearStat.totalLengthKm ?? 0),
|
|
);
|
|
displayRow[`year${yearStat.year}AgedLengthKm`] = formatMetric(
|
|
Number(yearStat.agedLengthKm ?? 0),
|
|
);
|
|
displayRow[`year${yearStat.year}AgedRate`] = formatMetric(
|
|
Number(yearStat.agedRate ?? 0),
|
|
);
|
|
});
|
|
|
|
return displayRow;
|
|
});
|
|
|
|
export const buildAgedPipeStatusByCtpvHeadList = (
|
|
years: number[],
|
|
): UiHeadProps[] => {
|
|
const heads: UiHeadProps[] = [
|
|
{ id: "ctpvNm", title: "시도", widthPercent: 14 },
|
|
];
|
|
|
|
years.forEach((year) => {
|
|
heads.push(
|
|
{
|
|
id: `year${year}TotalLengthKm`,
|
|
title: `${year} 전체관로연장(km)`,
|
|
widthPercent: 13,
|
|
align: "right",
|
|
},
|
|
{
|
|
id: `year${year}AgedLengthKm`,
|
|
title: `${year} 노후관로연장(km)`,
|
|
widthPercent: 13,
|
|
align: "right",
|
|
},
|
|
{
|
|
id: `year${year}AgedRate`,
|
|
title: `${year} 노후화율(%)`,
|
|
widthPercent: 11,
|
|
align: "right",
|
|
},
|
|
);
|
|
});
|
|
|
|
return heads;
|
|
};
|