From 2ef02f5f1893c63436658d2afa239937611e80f0 Mon Sep 17 00:00:00 2001 From: kyeongmin Date: Fri, 22 May 2026 00:34:32 +0900 Subject: [PATCH] feat(#76): IMP-47B u11 frontend human_review surfacing (hunk-split from IMP-41) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - AiRepairStatus interface mirrors backend step20 u8 schema - formatAiRepairHumanReviewMessage(): pure helper for the three failure axes (error / coverage_violated / unsupported_kind) — null on success/no-AI - Home.tsx: toast.error(aiReviewMsg) after run completion - FramePanel.tsx: reject-click window.confirm guard ("frame 유지 + AI 재구성") - imp47b_human_review_toast.test.tsx: 6 vitest cases (null/false/3 axes/other) Verification (frontend node_modules junction from main worktree): - vitest imp47b_human_review_toast.test.tsx: 6/6 passed - vitest full suite: 19/19 passed (imp41_application_mode 13 + u11 6, zero regression) Hunk-split rationale: - stash@{0} (imp47b-frontend-u11-pre-rebase, captured before IMP-41 merged) contained inline IMP-41 helpers alongside u11 changes - HEAD already has IMP-41 helper-based implementation (buildBadgeTitle / mergeApplicationCandidates from services/applicationMode.ts, f358604) - This commit adds ONLY the u11 surface on top of HEAD's IMP-41 baseline - No IMP-41 hunk regression: buildBadgeTitle / mergeApplicationCandidates / applicationMode forwarding preserved verbatim Co-Authored-By: Claude Opus 4.7 --- Front/client/src/components/FramePanel.tsx | 17 ++- Front/client/src/pages/Home.tsx | 3 + Front/client/src/services/designAgentApi.ts | 46 ++++++ .../tests/imp47b_human_review_toast.test.tsx | 135 ++++++++++++++++++ 4 files changed, 200 insertions(+), 1 deletion(-) create mode 100644 Front/client/tests/imp47b_human_review_toast.test.tsx diff --git a/Front/client/src/components/FramePanel.tsx b/Front/client/src/components/FramePanel.tsx index 9168c57..5b971c6 100644 --- a/Front/client/src/components/FramePanel.tsx +++ b/Front/client/src/components/FramePanel.tsx @@ -47,6 +47,21 @@ export default function FramePanel({ return userSelection.overrides.zone_frames[targetRegion.id] || targetRegion.frame_match_strategy.frame_id; }, [selectedZone, selectedRegion, userSelection.overrides.zone_frames]); + const handleFrameSelect = React.useCallback( + (candidate: FrameCandidate) => { + const isReject = candidate.label === "reject"; + const alreadyApplied = currentFrameId === candidate.id; + if (isReject && !alreadyApplied) { + const ok = window.confirm( + `"${candidate.name}" 은 V4 reject 라벨입니다.\n선택 시 frame 은 유지되고 AI 가 콘텐츠를 frame 구조에 맞게 재구성합니다.\n계속하시겠습니까?`, + ); + if (!ok) return; + } + onFrameSelect(candidate.id); + }, + [currentFrameId, onFrameSelect], + ); + if (!selectedZone) { return (
@@ -152,7 +167,7 @@ export default function FramePanel({ className="w-full" >