/** 시도별 노후관로 현황의 조회 연도와 그룹 컬럼을 구성한다. */ "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 | 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 => { 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 | 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; };