diff --git a/src/App.jsx b/src/App.jsx index a307b6b..e995f06 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -176,6 +176,25 @@ const contentStorageKey = 'program-flow-content'; const programStateStorageKey = 'program-flow-gates'; const serverStateEndpoint = '/api/state'; const disabledFlowStep = '__disabled__'; +const programTypeOptions = { + internal: { + label: '사내프로그램', + shortLabel: '사내', + rowClass: 'border-emerald-100 bg-emerald-50/55', + badgeClass: 'bg-emerald-100 text-emerald-700 ring-emerald-200', + dotClass: 'bg-emerald-500' + }, + commercial: { + label: '상용프로그램', + shortLabel: '상용', + rowClass: 'border-violet-100 bg-violet-50/55', + badgeClass: 'bg-violet-100 text-violet-700 ring-violet-200', + dotClass: 'bg-violet-500' + } +}; + +const getProgramType = (type) => (type === 'commercial' ? 'commercial' : 'internal'); +const getProgramTypeMeta = (type) => programTypeOptions[getProgramType(type)]; const defaultContent = { cheonjiin: { @@ -184,6 +203,7 @@ const defaultContent = { steps: cheonjiinFlow.map(({ title, feature, note }) => ({ title, feature, note })), deliverables: cheonjiinDeliverables, format: 'glb', + programType: 'internal', predecessors: [], successors: [] }, @@ -193,6 +213,7 @@ const defaultContent = { steps: wayPrimalFlow.map(({ title, feature, note }) => ({ title, feature, note })), format: '', deliverables: ['기본설계 모델'], + programType: 'internal', predecessors: [], successors: [], linkLabel: '천지인 산출 모델을 WayPrimal 설계 입력으로 연계' @@ -206,6 +227,7 @@ function normalizeStoredContent(parsed) { cheonjiin: { ...defaultContent.cheonjiin, ...(parsed.cheonjiin ?? {}), + programType: getProgramType(parsed.cheonjiin?.programType ?? defaultContent.cheonjiin.programType), predecessors: parsed.cheonjiin?.predecessors ?? [], successors: parsed.cheonjiin?.successors ?? [], format: @@ -216,6 +238,7 @@ function normalizeStoredContent(parsed) { wayPrimal: { ...defaultContent.wayPrimal, ...(parsed.wayPrimal ?? {}), + programType: getProgramType(parsed.wayPrimal?.programType ?? defaultContent.wayPrimal.programType), predecessors: parsed.wayPrimal?.predecessors ?? [], successors: parsed.wayPrimal?.successors ?? [], format: @@ -225,6 +248,7 @@ function normalizeStoredContent(parsed) { }, extraPrograms: (parsed.extraPrograms ?? []).map((program, index, programs) => ({ ...program, + programType: getProgramType(program.programType), predecessors: program.predecessors ?? [], successors: program.successors ?? [], linkLabel: program.linkLabel ?? `이전 프로그램 산출물을 ${program.name} 입력으로 연계` @@ -508,6 +532,8 @@ function FlowRow({ onDeliverableChange, onLabelChange, onDescriptionChange, + programType, + onProgramTypeChange, onAddStep, onRemoveStep, onMoveStep, @@ -523,6 +549,8 @@ function FlowRow({ return 'disabled'; }; const isRowClickable = Boolean(onLabelClick) && !isEditing; + const programTypeKey = getProgramType(programType); + const programTypeMeta = getProgramTypeMeta(programTypeKey); const maxVisibleSteps = 4; const [stepWindowStart, setStepWindowStart] = useState(0); const maxWindowStart = Math.max(0, steps.length - maxVisibleSteps); @@ -591,7 +619,7 @@ function FlowRow({ return (
@@ -628,6 +656,31 @@ function FlowRow({ 프로그램 삭제 )} + {isEditing && onProgramTypeChange ? ( +
+ {Object.entries(programTypeOptions).map(([typeKey, typeMeta]) => ( + + ))} +
+ ) : ( + + {programTypeMeta.label} + + )} {isEditing && onDescriptionChange ? (