Persist program comparison notes
This commit is contained in:
62
src/App.jsx
62
src/App.jsx
@@ -1230,14 +1230,24 @@ function RelationPopup({ programs, onToggleRelation, onClose }) {
|
|||||||
function ProgramComparePopup({ programs, comparisons, onComparisonChange, onClose }) {
|
function ProgramComparePopup({ programs, comparisons, onComparisonChange, onClose }) {
|
||||||
const internalProgram = programs.find((program) => getProgramType(program.programType) === 'internal');
|
const internalProgram = programs.find((program) => getProgramType(program.programType) === 'internal');
|
||||||
const commercialProgram = programs.find((program) => getProgramType(program.programType) === 'commercial');
|
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(
|
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 leftProgram = programs.find((program) => program.id === leftProgramId);
|
||||||
const rightProgram = programs.find((program) => program.id === rightProgramId);
|
const rightProgram = programs.find((program) => program.id === rightProgramId);
|
||||||
const comparison =
|
const comparison =
|
||||||
comparisons.find((item) => item.leftProgramId === leftProgramId && item.rightProgramId === rightProgramId) ?? {};
|
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 leftSteps = leftProgram?.steps ?? [];
|
||||||
const rightSteps = rightProgram?.steps ?? [];
|
const rightSteps = rightProgram?.steps ?? [];
|
||||||
const stepMatches = comparison.stepMatches ?? [];
|
const stepMatches = comparison.stepMatches ?? [];
|
||||||
@@ -1255,6 +1265,11 @@ function ProgramComparePopup({ programs, comparisons, onComparisonChange, onClos
|
|||||||
const existingIndex = stepMatches.findIndex(
|
const existingIndex = stepMatches.findIndex(
|
||||||
(match) => String(match.leftStepIndex) === String(stepIndex) && String(match.rightStepIndex) === String(stepIndex)
|
(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 = {
|
const nextMatch = {
|
||||||
...(existingIndex >= 0 ? stepMatches[existingIndex] : {}),
|
...(existingIndex >= 0 ? stepMatches[existingIndex] : {}),
|
||||||
id: `step-${stepIndex}`,
|
id: `step-${stepIndex}`,
|
||||||
@@ -1270,6 +1285,9 @@ function ProgramComparePopup({ programs, comparisons, onComparisonChange, onClos
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
const getStepLabel = (step, index) => `${index + 1}. ${step?.title ?? '-'}`;
|
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) => {
|
const renderProgramSummary = (program) => {
|
||||||
if (!program) return null;
|
if (!program) return null;
|
||||||
@@ -1357,6 +1375,38 @@ function ProgramComparePopup({ programs, comparisons, onComparisonChange, onClos
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</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">
|
<div className="mt-4 grid gap-4 lg:grid-cols-2">
|
||||||
{renderProgramSummary(leftProgram)}
|
{renderProgramSummary(leftProgram)}
|
||||||
{renderProgramSummary(rightProgram)}
|
{renderProgramSummary(rightProgram)}
|
||||||
@@ -1368,6 +1418,7 @@ function ProgramComparePopup({ programs, comparisons, onComparisonChange, onClos
|
|||||||
<h3 className="text-base font-black text-slate-950">전체 스텝 1:1 비교</h3>
|
<h3 className="text-base font-black text-slate-950">전체 스텝 1:1 비교</h3>
|
||||||
<p className="mt-1 text-[12px] font-bold text-slate-500">
|
<p className="mt-1 text-[12px] font-bold text-slate-500">
|
||||||
두 프로그램의 스텝을 전부 펼친 뒤, 가운데에 해당 스텝에서 상용 프로그램을 쓰는 이유를 적습니다.
|
두 프로그램의 스텝을 전부 펼친 뒤, 가운데에 해당 스텝에서 상용 프로그램을 쓰는 이유를 적습니다.
|
||||||
|
{hasSavedComparison ? ' 저장된 비교 내용을 불러왔습니다.' : ''}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -2162,11 +2213,18 @@ export default function App() {
|
|||||||
...(comparisonIndex >= 0 ? comparisons[comparisonIndex] : {}),
|
...(comparisonIndex >= 0 ? comparisons[comparisonIndex] : {}),
|
||||||
leftProgramId,
|
leftProgramId,
|
||||||
rightProgramId,
|
rightProgramId,
|
||||||
|
updatedAt: new Date().toISOString(),
|
||||||
[field]: value
|
[field]: value
|
||||||
};
|
};
|
||||||
|
const hasComparisonContent =
|
||||||
|
(nextComparison.stepMatches ?? []).some((match) => match.reason?.trim()) ||
|
||||||
|
nextComparison.note?.trim();
|
||||||
return {
|
return {
|
||||||
...current,
|
...current,
|
||||||
comparisons:
|
comparisons:
|
||||||
|
!hasComparisonContent
|
||||||
|
? comparisons.filter((_, index) => index !== comparisonIndex)
|
||||||
|
:
|
||||||
comparisonIndex >= 0
|
comparisonIndex >= 0
|
||||||
? comparisons.map((comparison, index) => (index === comparisonIndex ? nextComparison : comparison))
|
? comparisons.map((comparison, index) => (index === comparisonIndex ? nextComparison : comparison))
|
||||||
: [...comparisons, nextComparison]
|
: [...comparisons, nextComparison]
|
||||||
|
|||||||
Reference in New Issue
Block a user