Files
frontend/apps/sn/app/(map-layout)/map/components/agedPipeStatusByCtpv/agedPipeStatusByCtpvUtils.ts
T

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;
};