diff --git a/src/App.jsx b/src/App.jsx index 64cf6ba..b0bff02 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -213,6 +213,35 @@ const programTypeOptions = { const getProgramType = (type) => (type === 'commercial' ? 'commercial' : 'internal'); const getProgramTypeMeta = (type) => programTypeOptions[getProgramType(type)]; +const programRoleOptions = { + flow: { + label: '업무 프로그램', + shortLabel: '업무', + badgeClass: 'bg-sky-50 text-sky-700 ring-sky-100' + }, + reference: { + label: '참고 프로그램', + shortLabel: '참고', + badgeClass: 'bg-slate-100 text-slate-600 ring-slate-200' + } +}; +const getProgramRole = (role) => (role === 'reference' ? 'reference' : 'flow'); +const getProgramRoleMeta = (role) => programRoleOptions[getProgramRole(role)]; +const normalizeAuxiliaryTargets = (program) => { + const targets = Array.isArray(program?.auxiliaryTargets) ? program.auxiliaryTargets : []; + const legacyTargets = targets.length === 0 && Array.isArray(program?.auxiliaryTargetIds) + ? program.auxiliaryTargetIds.map((programId) => ({ programId, stepId: '' })) + : []; + const mergedTargets = [...targets, ...legacyTargets] + .map((target) => ({ + programId: target?.programId ?? target, + stepId: target?.stepId ?? '' + })) + .filter((target) => target.programId); + return mergedTargets.filter((target, index, list) => + list.findIndex((item) => item.programId === target.programId && (item.stepId ?? '') === (target.stepId ?? '')) === index + ); +}; const defaultContent = { cheonjiin: { @@ -223,9 +252,12 @@ const defaultContent = { format: 'glb', engine: '', programType: 'internal', + programRole: 'flow', programNote: '', predecessors: [], successors: [], + auxiliaryTargetIds: [], + auxiliaryTargets: [], mergeGroup: '' }, wayPrimal: { @@ -236,9 +268,12 @@ const defaultContent = { engine: '', deliverables: ['기본설계 모델'], programType: 'internal', + programRole: 'flow', programNote: '', predecessors: [], successors: [], + auxiliaryTargetIds: [], + auxiliaryTargets: [], mergeGroup: '', linkLabel: '천지인 산출 모델을 WayPrimal 설계 입력으로 연계' }, @@ -253,10 +288,13 @@ function normalizeStoredContent(parsed) { ...defaultContent.cheonjiin, ...(parsed.cheonjiin ?? {}), programType: getProgramType(parsed.cheonjiin?.programType ?? defaultContent.cheonjiin.programType), + programRole: getProgramRole(parsed.cheonjiin?.programRole ?? defaultContent.cheonjiin.programRole), programNote: parsed.cheonjiin?.programNote ?? '', engine: parsed.cheonjiin?.engine ?? '', predecessors: parsed.cheonjiin?.predecessors ?? [], successors: parsed.cheonjiin?.successors ?? [], + auxiliaryTargetIds: parsed.cheonjiin?.auxiliaryTargetIds ?? [], + auxiliaryTargets: normalizeAuxiliaryTargets(parsed.cheonjiin), mergeGroup: parsed.cheonjiin?.mergeGroup ?? '', format: !parsed.cheonjiin?.format || parsed.cheonjiin.format === '예: DXF, SHP, GeoTIFF, 수치지형도 v2 등' @@ -267,10 +305,13 @@ function normalizeStoredContent(parsed) { ...defaultContent.wayPrimal, ...(parsed.wayPrimal ?? {}), programType: getProgramType(parsed.wayPrimal?.programType ?? defaultContent.wayPrimal.programType), + programRole: getProgramRole(parsed.wayPrimal?.programRole ?? defaultContent.wayPrimal.programRole), programNote: parsed.wayPrimal?.programNote ?? '', engine: parsed.wayPrimal?.engine ?? '', predecessors: parsed.wayPrimal?.predecessors ?? [], successors: parsed.wayPrimal?.successors ?? [], + auxiliaryTargetIds: parsed.wayPrimal?.auxiliaryTargetIds ?? [], + auxiliaryTargets: normalizeAuxiliaryTargets(parsed.wayPrimal), mergeGroup: parsed.wayPrimal?.mergeGroup ?? '', format: parsed.wayPrimal?.format === '예: DWG, LandXML, XLSX, 도공계산서, 기본설계 모델 등' @@ -285,10 +326,13 @@ function normalizeStoredContent(parsed) { extraPrograms: (parsed.extraPrograms ?? []).map((program, index, programs) => ({ ...program, programType: getProgramType(program.programType), + programRole: getProgramRole(program.programRole), programNote: program.programNote ?? '', engine: program.engine ?? '', predecessors: program.predecessors ?? [], successors: program.successors ?? [], + auxiliaryTargetIds: program.auxiliaryTargetIds ?? [], + auxiliaryTargets: normalizeAuxiliaryTargets(program), mergeGroup: program.mergeGroup ?? '', linkLabel: program.linkLabel ?? `이전 프로그램 산출물을 ${program.name} 입력으로 연계` })) @@ -484,14 +528,16 @@ function splitDeliverableText(item) { .filter(Boolean); } -function FlowCard({ step, index, total, accent, status, isEditing, onChange, searchMatched = false }) { +function FlowCard({ step, index, total, accent, status, isEditing, onChange, searchMatched = false, searchTargetMatched = false, auxiliaryPrograms = [] }) { const Icon = step.icon ?? pickStepIcon(step); return (
{program.name}
+{program.description}
+프로그램
선행
후행
하나로 인식
-하나로 인식
+- 같은 단계로 보는 프로그램을 묶으면 연결도에서는 하나의 박스로 표시됩니다. + 체크한 프로그램들이 연결도에서 하나의 비교 프로그램 박스로 표시됩니다.
같은 비교 프로그램 묶음
++ 묶음 안 프로그램이 3개 이상이면 비교표에 모두 함께 표시됩니다. +
+비교 보고서
두 프로그램의 동일/연계 스텝에서 사내 한계와 상용 프로그램 활용 사유를 정리합니다. @@ -1794,9 +2052,15 @@ function ProgramComparePopup({ programs, comparisons, initialPair, onComparisonS
- 비교 행을 만들고, 왼쪽/오른쪽 프로그램에서 비교할 스텝을 각각 선택합니다. + 비교 행을 만들고, 각 프로그램에서 비교할 스텝을 선택합니다.
- {leftStep?.feature || '해당 스텝 없음'} -
- {leftStep?.note && ( -{leftStep.note}
- )} -