Files
frontend/apps/sn/src/features/exmn/model/exmn-sample-data.ts
T

461 lines
12 KiB
TypeScript

import type {
ExmnBusiness,
ExmnBusinessDetail,
ExmnImportValidationResult,
ExmnJobType,
ExmnSectionSummaryRow,
} from "./exmn-business-types";
import {
EXMN_DETAIL_SEVERITIES,
EXMN_DETAIL_TABLE_DEFINITIONS,
type ExmnPplUnitDetailRow,
type ExmnSeverityCounts,
type ExmnSurveyDetailRow,
} from "./exmn-detail-table";
import type { TrtdstSettingDto } from "./trtdst-types";
/** 백엔드 데이터가 준비되기 전 명시적 sample=Y 화면 검증에서만 사용하는 조사사업 목록이다. */
export const EXMN_SAMPLE_BUSINESSES: ExmnBusiness[] = [
{
exmnBizId: "SAMPLE-BIZ-001",
bizYr: "2026",
bizNm: "2026년 양평군 노후 하수관로 정밀조사사업",
bizCycl: 3,
ctpvNm: "경기도",
sggNm: "양평군",
trtdstCnt: 10,
prcisExmnPrlg: 18.42,
tpco: 1_250_000_000,
reportYn: "Y",
bizRptpAtchFileCd: "SAMPLE",
bizRptpAtchFileId: "SAMPLE-REPORT-001",
},
{
exmnBizId: "SAMPLE-BIZ-002",
bizYr: "2025",
bizNm: "2025년 이천시 하수관로 CCTV 조사사업",
bizCycl: 2,
ctpvNm: "경기도",
sggNm: "이천시",
trtdstCnt: 10,
prcisExmnPrlg: 12.75,
tpco: 890_000_000,
reportYn: "N",
},
{
exmnBizId: "SAMPLE-BIZ-003",
bizYr: "2024",
bizNm: "2024년 원주시 관로·맨홀 정밀조사사업",
bizCycl: 1,
ctpvNm: "강원특별자치도",
sggNm: "원주시",
trtdstCnt: 10,
prcisExmnPrlg: 15.1,
tpco: 1_030_000_000,
reportYn: "Y",
bizRptpAtchFileCd: "SAMPLE",
bizRptpAtchFileId: "SAMPLE-REPORT-003",
},
{
exmnBizId: "SAMPLE-BIZ-004",
bizYr: "2023",
bizNm: "2023년 청주시 하수관로 정밀조사사업",
bizCycl: 4,
ctpvNm: "충청북도",
sggNm: "청주시",
trtdstCnt: 10,
prcisExmnPrlg: 9.86,
tpco: 760_000_000,
reportYn: "N",
},
];
/** 사업등록 팝업의 체크박스·가나다순 정렬을 검증하는 활성 처리분구 샘플이다. */
export const EXMN_SAMPLE_TRTDST_SETTINGS: TrtdstSettingDto[] = [
"갈산처리분구",
"개군처리분구",
"공흥처리분구",
"국수처리분구",
"단월처리분구",
"덕평처리분구",
"문호처리분구",
"백안처리분구",
"서종처리분구",
"양근처리분구",
"양동처리분구",
"양서처리분구",
"오빈처리분구",
"용문처리분구",
"용천처리분구",
"지평처리분구",
"청운처리분구",
"회현처리분구",
"강상처리분구",
"강하처리분구",
"옥천처리분구",
].map((trtdstNm, index) => ({
trtdstId: `SAMPLE-TRTDST-${String(index + 1).padStart(2, "0")}`,
ctpvNm: "경기도",
sggNm: "양평군",
trtdstNm,
useYn: "Y",
}));
/** 선택 사업의 처리분구와 첨부 영역을 팝업에서 확인할 수 있도록 상세 샘플을 만든다. */
export const getExmnSampleBusinessDetail = (
exmnBizId: string,
fallbackBusiness?: ExmnBusiness,
): ExmnBusinessDetail => {
const business = EXMN_SAMPLE_BUSINESSES.find((item) => item.exmnBizId === exmnBizId)
?? fallbackBusiness
?? EXMN_SAMPLE_BUSINESSES[0];
const districts = EXMN_SAMPLE_TRTDST_SETTINGS.slice(0, business.trtdstCnt).map((item) => ({
trtdstId: item.trtdstId,
trtdstNm: item.trtdstNm,
pplExmnSheetNm: `${item.trtdstNm}_PPL`,
mnhlExmnSheetNm: `${item.trtdstNm}_MNHL`,
}));
return {
...business,
trtdstIds: districts.map((item) => item.trtdstId),
districts,
};
};
/** 결과등록 팝업의 데이터 검토·저장 활성 상태를 확인하는 정상 샘플 결과다. */
export const EXMN_SAMPLE_IMPORT_VALIDATION: ExmnImportValidationResult = {
valid: true,
fileHash: "SAMPLE-FILE-HASH-PREVIEW",
summary: {
pplCount: 24,
mnhlCount: 12,
},
errors: [],
};
/** 조사현황의 사업 행 선택과 관로·맨홀 탭 전환을 검증하는 처리분구 집계 샘플이다. */
export const getExmnSampleSectionSummaries = (
exmnBizId: string,
jobType: ExmnJobType,
): ExmnSectionSummaryRow[] => {
const business = EXMN_SAMPLE_BUSINESSES.find((item) => item.exmnBizId === exmnBizId)
?? EXMN_SAMPLE_BUSINESSES[0];
const districts = EXMN_SAMPLE_TRTDST_SETTINGS.slice(0, business.trtdstCnt);
return districts.map((district, index) => {
const scale = index + 1;
const pplStructural = 21 * scale;
const pplOperational = 7 * scale;
const pplEtc = 2 * scale;
const mnhlStructural = 5 * scale;
const mnhlOperational = 3 * scale;
const mnhlEtc = scale;
return {
trtdstId: district.trtdstId,
trtdstNm: district.trtdstNm,
trgtPrlg: Number((business.prcisExmnPrlg * 1000 / districts.length).toFixed(1)),
drvngDstnc: Number((business.prcisExmnPrlg * 930 / districts.length).toFixed(1)),
pplExmnWholAnmlyArtclCnt: jobType === "PPL" ? pplStructural + pplOperational + pplEtc : 0,
pplExmnStrctDfcCnt: jobType === "PPL" ? pplStructural : 0,
pplExmnOperDfcCnt: jobType === "PPL" ? pplOperational : 0,
pplExmnEtcCnt: jobType === "PPL" ? pplEtc : 0,
mnhlExmnWholAnmlyArtclCnt: jobType === "MNHL" ? mnhlStructural + mnhlOperational + mnhlEtc : 0,
mnhlExmnStrctDfcCnt: jobType === "MNHL" ? mnhlStructural : 0,
mnhlExmnOperDfcCnt: jobType === "MNHL" ? mnhlOperational : 0,
mnhlExmnEtcCnt: jobType === "MNHL" ? mnhlEtc : 0,
};
});
};
const EMPTY_SEVERITY_COUNTS: ExmnSeverityCounts = { L: 0, M: 0, S: 0 };
/** 열 코드와 행 순번으로 화면 확인용 희소 결함 건수를 일관되게 만든다. */
const buildSampleDefectCounts = (
jobType: ExmnJobType,
rowIndex: number,
): Record<string, ExmnSeverityCounts> => Object.fromEntries(
EXMN_DETAIL_TABLE_DEFINITIONS[jobType].defectColumns.map((column, columnIndex) => {
const counts = { ...EMPTY_SEVERITY_COUNTS };
EXMN_DETAIL_SEVERITIES.forEach((severity, severityIndex) => {
const seed = columnIndex + rowIndex * 3 + severityIndex;
if (seed % 11 === 0) {
counts[severity] = rowIndex + severityIndex + 1;
}
});
return [column.code, counts];
}),
);
/** 코드별 건수를 합산해 엑셀의 이상항목 합계 대·중·소 샘플을 만든다. */
const sumSampleDefects = (
defectCounts: Record<string, ExmnSeverityCounts>,
): ExmnSeverityCounts => EXMN_DETAIL_SEVERITIES.reduce(
(totals, severity) => {
totals[severity] = Object.values(defectCounts).reduce(
(sum, counts) => sum + counts[severity],
0,
);
return totals;
},
{ ...EMPTY_SEVERITY_COUNTS },
);
const SAMPLE_FACILITIES = [
{
reportNo: "118-0026",
pipeNo: "118-0026",
upstreamManhole: "118-0026M",
downstreamManhole: "118-0027M",
extensionM: 25.5,
drivingDistanceM: 25.5,
},
{
reportNo: "118-0027",
pipeNo: "118-0027",
upstreamManhole: "118-0027M",
downstreamManhole: "118-0028M",
extensionM: 27,
drivingDistanceM: 27,
},
{
reportNo: "118-0027-1",
pipeNo: "118-0027-1",
upstreamManhole: "118-0027-1M",
downstreamManhole: "118-0028M",
extensionM: 13.9,
drivingDistanceM: 13.9,
},
] as const;
/** 상세보기 WindowPopup에서 관로·맨홀 엑셀형 표를 확인하는 샘플 3행이다. */
export const getExmnSampleSurveyDetails = (
jobType: ExmnJobType,
): ExmnSurveyDetailRow[] => SAMPLE_FACILITIES.map((facility, rowIndex) => {
const defectCounts = buildSampleDefectCounts(jobType, rowIndex);
const anomalyTotals = sumSampleDefects(defectCounts);
return {
...facility,
drainageType: "합류",
pipeType: "HP",
specification: 600,
anomalyTotals,
defectCounts,
};
});
/** 엑셀 `본단위등급표`의 한 관로를 단위구간별 병합 표로 확인하는 샘플 원형이다. */
const EXMN_SAMPLE_PPL_UNIT_DETAIL_TEMPLATE: ExmnPplUnitDetailRow[] = [
{
pipeNo: "118-0026",
unitSeq: 1,
unitLengthM: 6,
defectPositionM: 0,
defectName: "(상태)조사시작(순주행)",
code: "IS",
recordType: "STATUS",
defectType: null,
severity: null,
occurrencePosition: "0/0",
infiltrationBonus: 0,
continuous: null,
structuralScore: null,
operationalScore: null,
surveyStopReason: null,
remark: null,
},
{
pipeNo: "118-0026",
unitSeq: 1,
unitLengthM: 6,
defectPositionM: 5.4,
defectName: "(관로)변형",
code: "DF",
recordType: "DEFECT",
defectType: "STRUCTURAL",
severity: "S",
occurrencePosition: "10/2",
infiltrationBonus: 0,
continuous: false,
structuralScore: 25,
operationalScore: null,
surveyStopReason: null,
remark: null,
},
{
pipeNo: "118-0026",
unitSeq: 1,
unitLengthM: 6,
defectPositionM: 6,
defectName: "(관로)이음부이탈",
code: "JS",
recordType: "DEFECT",
defectType: "STRUCTURAL",
severity: "L",
occurrencePosition: "4/6",
infiltrationBonus: 0,
continuous: null,
structuralScore: 35,
operationalScore: null,
surveyStopReason: null,
remark: null,
},
{
pipeNo: "118-0026",
unitSeq: 1,
unitLengthM: 6,
defectPositionM: 6,
defectName: "(상태)이음부(접합부)존재",
code: "JE",
recordType: "STATUS",
defectType: null,
severity: null,
occurrencePosition: "0/0",
infiltrationBonus: 0,
continuous: null,
structuralScore: null,
operationalScore: null,
surveyStopReason: null,
remark: null,
},
{
pipeNo: "118-0026",
unitSeq: 2,
unitLengthM: 6,
defectPositionM: 6,
defectName: "(상태)이음부(접합부)존재",
code: "JE",
recordType: "STATUS",
defectType: null,
severity: null,
occurrencePosition: "0/0",
infiltrationBonus: 0,
continuous: null,
structuralScore: null,
operationalScore: null,
surveyStopReason: null,
remark: null,
},
{
pipeNo: "118-0026",
unitSeq: 2,
unitLengthM: 6,
defectPositionM: 6,
defectName: "(관로)이음부이탈",
code: "JS",
recordType: "DEFECT",
defectType: "STRUCTURAL",
severity: "L",
occurrencePosition: "4/6",
infiltrationBonus: 0,
continuous: null,
structuralScore: 35,
operationalScore: null,
surveyStopReason: null,
remark: null,
},
{
pipeNo: "118-0026",
unitSeq: 2,
unitLengthM: 6,
defectPositionM: 7,
defectName: "(관로)균열원주",
code: "CC",
recordType: "DEFECT",
defectType: "STRUCTURAL",
severity: "L",
occurrencePosition: "11/12",
infiltrationBonus: 0,
continuous: null,
structuralScore: 40,
operationalScore: null,
surveyStopReason: null,
remark: null,
},
{
pipeNo: "118-0026",
unitSeq: 2,
unitLengthM: 6,
defectPositionM: 9.7,
defectName: "(관로)균열원주",
code: "CC",
recordType: "DEFECT",
defectType: "STRUCTURAL",
severity: "L",
occurrencePosition: "7/8",
infiltrationBonus: 0,
continuous: null,
structuralScore: 40,
operationalScore: null,
surveyStopReason: null,
remark: null,
},
{
pipeNo: "118-0026",
unitSeq: 2,
unitLengthM: 6,
defectPositionM: 11.6,
defectName: "(관로)변형",
code: "DF",
recordType: "DEFECT",
defectType: "STRUCTURAL",
severity: "S",
occurrencePosition: "10/12",
infiltrationBonus: 0,
continuous: false,
structuralScore: 25,
operationalScore: null,
surveyStopReason: null,
remark: null,
},
{
pipeNo: "118-0026",
unitSeq: 2,
unitLengthM: 6,
defectPositionM: 12,
defectName: "(관로)이음부이탈",
code: "JS",
recordType: "DEFECT",
defectType: "STRUCTURAL",
severity: "L",
occurrencePosition: "5/7",
infiltrationBonus: 0,
continuous: null,
structuralScore: 35,
operationalScore: null,
surveyStopReason: null,
remark: null,
},
{
pipeNo: "118-0026",
unitSeq: 2,
unitLengthM: 6,
defectPositionM: 12,
defectName: "(상태)이음부(접합부)존재",
code: "JE",
recordType: "STATUS",
defectType: null,
severity: null,
occurrencePosition: "0/0",
infiltrationBonus: 0,
continuous: null,
structuralScore: null,
operationalScore: null,
surveyStopReason: null,
remark: null,
},
];
/**
* 본단위구간 모드 최초 진입 시 처리분구의 관리번호 전체가 보이도록
* 관로 상세 샘플 3건에 동일한 단위구간 원형을 적용한다.
*/
export const EXMN_SAMPLE_PPL_UNIT_DETAILS: ExmnPplUnitDetailRow[] =
SAMPLE_FACILITIES.flatMap((facility) =>
EXMN_SAMPLE_PPL_UNIT_DETAIL_TEMPLATE.map((row) => ({
...row,
pipeNo: facility.pipeNo,
})),
);