Persist program comparison notes

This commit is contained in:
2026-06-24 10:37:19 +09:00
parent 28bfe4f96f
commit 7d3fa78f1e

View File

@@ -1230,14 +1230,24 @@ function RelationPopup({ programs, onToggleRelation, onClose }) {
function ProgramComparePopup({ programs, comparisons, onComparisonChange, onClose }) {
const internalProgram = programs.find((program) => getProgramType(program.programType) === 'internal');
const commercialProgram = programs.find((program) => getProgramType(program.programType) === 'commercial');
const [leftProgramId, setLeftProgramId] = useState(internalProgram?.id ?? programs[0]?.id ?? '');
const savedComparisons = comparisons.filter(
(comparison) =>
programs.some((program) => program.id === comparison.leftProgramId) &&
programs.some((program) => program.id === comparison.rightProgramId) &&
((comparison.stepMatches ?? []).some((match) => match.reason?.trim()) || comparison.note?.trim())
).sort((left, right) => (right.updatedAt ?? '').localeCompare(left.updatedAt ?? ''));
const [leftProgramId, setLeftProgramId] = useState(savedComparisons[0]?.leftProgramId ?? internalProgram?.id ?? programs[0]?.id ?? '');
const [rightProgramId, setRightProgramId] = useState(
commercialProgram?.id ?? programs.find((program) => program.id !== (internalProgram?.id ?? programs[0]?.id))?.id ?? ''
savedComparisons[0]?.rightProgramId ??
commercialProgram?.id ??
programs.find((program) => program.id !== (internalProgram?.id ?? programs[0]?.id))?.id ??
''
);
const leftProgram = programs.find((program) => program.id === leftProgramId);
const rightProgram = programs.find((program) => program.id === rightProgramId);
const comparison =
comparisons.find((item) => item.leftProgramId === leftProgramId && item.rightProgramId === rightProgramId) ?? {};
const hasSavedComparison = Boolean((comparison.stepMatches ?? []).some((match) => match.reason?.trim()) || comparison.note?.trim());
const leftSteps = leftProgram?.steps ?? [];
const rightSteps = rightProgram?.steps ?? [];
const stepMatches = comparison.stepMatches ?? [];
@@ -1255,6 +1265,11 @@ function ProgramComparePopup({ programs, comparisons, onComparisonChange, onClos
const existingIndex = stepMatches.findIndex(
(match) => String(match.leftStepIndex) === String(stepIndex) && String(match.rightStepIndex) === String(stepIndex)
);
if (!value.trim() && existingIndex >= 0) {
updateComparison('stepMatches', stepMatches.filter((_, index) => index !== existingIndex));
return;
}
if (!value.trim()) return;
const nextMatch = {
...(existingIndex >= 0 ? stepMatches[existingIndex] : {}),
id: `step-${stepIndex}`,
@@ -1270,6 +1285,9 @@ function ProgramComparePopup({ programs, comparisons, onComparisonChange, onClos
);
};
const getStepLabel = (step, index) => `${index + 1}. ${step?.title ?? '-'}`;
const getProgramName = (programId) => programs.find((program) => program.id === programId)?.name ?? programId;
const getComparisonMemoCount = (savedComparison) =>
(savedComparison.stepMatches ?? []).filter((match) => match.reason?.trim()).length;
const renderProgramSummary = (program) => {
if (!program) return null;
@@ -1357,6 +1375,38 @@ function ProgramComparePopup({ programs, comparisons, onComparisonChange, onClos
</label>
</div>
{savedComparisons.length > 0 && (
<div className="mt-3 rounded-3xl bg-white px-4 py-3 ring-1 ring-slate-100">
<div className="flex flex-wrap items-center gap-2">
<span className="mr-1 text-[12px] font-black text-slate-500">저장된 비교</span>
{savedComparisons.map((savedComparison) => {
const active =
savedComparison.leftProgramId === leftProgramId &&
savedComparison.rightProgramId === rightProgramId;
const memoCount = getComparisonMemoCount(savedComparison);
return (
<button
key={`${savedComparison.leftProgramId}-${savedComparison.rightProgramId}`}
type="button"
onClick={() => {
setLeftProgramId(savedComparison.leftProgramId);
setRightProgramId(savedComparison.rightProgramId);
}}
className={`rounded-full px-3 py-1.5 text-[12px] font-black ring-1 transition ${
active
? 'bg-slate-950 text-white ring-slate-950'
: 'bg-slate-50 text-slate-600 ring-slate-200 hover:bg-white'
}`}
>
{getProgramName(savedComparison.leftProgramId)} {getProgramName(savedComparison.rightProgramId)}
{memoCount > 0 ? ` · ${memoCount}개 메모` : ''}
</button>
);
})}
</div>
</div>
)}
<div className="mt-4 grid gap-4 lg:grid-cols-2">
{renderProgramSummary(leftProgram)}
{renderProgramSummary(rightProgram)}
@@ -1368,6 +1418,7 @@ function ProgramComparePopup({ programs, comparisons, onComparisonChange, onClos
<h3 className="text-base font-black text-slate-950">전체 스텝 1:1 비교</h3>
<p className="mt-1 text-[12px] font-bold text-slate-500">
프로그램의 스텝을 전부 펼친 , 가운데에 해당 스텝에서 상용 프로그램을 쓰는 이유를 적습니다.
{hasSavedComparison ? ' 저장된 비교 내용을 불러왔습니다.' : ''}
</p>
</div>
</div>
@@ -2162,11 +2213,18 @@ export default function App() {
...(comparisonIndex >= 0 ? comparisons[comparisonIndex] : {}),
leftProgramId,
rightProgramId,
updatedAt: new Date().toISOString(),
[field]: value
};
const hasComparisonContent =
(nextComparison.stepMatches ?? []).some((match) => match.reason?.trim()) ||
nextComparison.note?.trim();
return {
...current,
comparisons:
!hasComparisonContent
? comparisons.filter((_, index) => index !== comparisonIndex)
:
comparisonIndex >= 0
? comparisons.map((comparison, index) => (index === comparisonIndex ? nextComparison : comparison))
: [...comparisons, nextComparison]