feat(insta): trends 카드 생성 시 progress 배너 + 자동 미리보기 전환

Trends 탭의 🎴 버튼 클릭이 silent로 끝나 사용자에게 무동작처럼 보이던
이슈 fix. handleCreateSlate를 3초 간격 폴링으로 확장 (최대 8분):

- 시작/진행/성공/실패 상단 배너로 시각화
- 카드 생성 완료 시 자동으로 Cards 탭 전환 + 새 슬레이트 자동 선택
  → SlateDetail이 카피·이미지 미리보기 즉시 표시
- 실패 시 에러 메시지 + 클릭으로 dismiss
- "Claude 카피 추론 + Playwright 카드 10장 생성 중 (3~7분)" 안내 문구
This commit is contained in:
2026-05-17 12:41:04 +09:00
parent 9e9eed2162
commit 6222b56716
2 changed files with 100 additions and 4 deletions

View File

@@ -167,3 +167,39 @@
} }
.ic-impact__cat { font-weight: 600; text-transform: capitalize; color: rgba(255,255,255,.6); font-size: 0.82rem; } .ic-impact__cat { font-weight: 600; text-transform: capitalize; color: rgba(255,255,255,.6); font-size: 0.82rem; }
.ic-impact__count { color: #ec4899; font-weight: 700; font-size: 0.82rem; } .ic-impact__count { color: #ec4899; font-weight: 700; font-size: 0.82rem; }
/* ── slate creation progress banner (양 탭 공통) ── */
.ic-slate-progress {
margin: 8px 0 16px;
padding: 12px 16px;
border-radius: 8px;
font-size: 0.88rem;
display: flex;
align-items: center;
gap: 6px;
flex-wrap: wrap;
line-height: 1.5;
}
.ic-slate-progress--starting,
.ic-slate-progress--processing {
background: rgba(245, 158, 11, 0.12);
color: #fbbf24;
border-left: 4px solid #f59e0b;
}
.ic-slate-progress--succeeded {
background: rgba(16, 185, 129, 0.12);
color: #34d399;
border-left: 4px solid #10b981;
cursor: pointer;
}
.ic-slate-progress--failed {
background: rgba(239, 68, 68, 0.12);
color: #f87171;
border-left: 4px solid #ef4444;
cursor: pointer;
}
.ic-slate-progress__hint {
opacity: 0.7;
font-size: 0.78rem;
margin-left: 6px;
}

View File

@@ -300,6 +300,10 @@ function PreferenceImpactPanel() {
export default function InstaCards() { export default function InstaCards() {
const [status, setStatus] = useState(null); const [status, setStatus] = useState(null);
const [selectedSlateId, setSelectedSlateId] = useState(null); const [selectedSlateId, setSelectedSlateId] = useState(null);
/* ── 카드 생성 progress (Trends 탭 클릭 + Cards 탭 양쪽 모두 사용) ──
* null = idle
* { keyword, status: 'starting'|'processing'|'succeeded'|'failed', message?, slate_id?, error? } */
const [slateProgress, setSlateProgress] = useState(null);
/* ── 탭 상태 (URL 동기화) ── */ /* ── 탭 상태 (URL 동기화) ── */
const [activeTab, setActiveTab] = useState(() => { const [activeTab, setActiveTab] = useState(() => {
@@ -323,13 +327,47 @@ export default function InstaCards() {
loadStatus(); loadStatus();
}, [loadStatus]); }, [loadStatus]);
/* ── handleCreateSlate: 키워드 → 슬레이트 생성 (Trends 탭에서도 공유) ── */ /* ── handleCreateSlate: 키워드 → 카피 + 이미지 추론 → 자동 미리보기 ──
* 1. createInstaSlate 호출 → task_id
* 2. getInstaTask로 폴링 (3초 간격, 최대 8분 = Claude 카피 + Playwright 10장 렌더)
* 3. 완료 시 Cards 탭으로 자동 전환 + 슬레이트 선택 → SlateDetail이 카피·이미지 미리보기 */
const handleCreateSlate = useCallback(async ({ keyword, category, keyword_id } = {}) => { const handleCreateSlate = useCallback(async ({ keyword, category, keyword_id } = {}) => {
if (!keyword || !category) {
alert('keyword + category 필수');
return;
}
setSlateProgress({ keyword, status: 'starting', message: '카드 생성 시작...' });
try { try {
await createInstaSlate({ keyword, category, keyword_id }); const { task_id } = await createInstaSlate({ keyword, category, keyword_id });
setSelectedSlateId(null); let st = null;
// 최대 8분 (3초 × 160) 폴링
for (let i = 0; i < 160; i++) {
st = await getInstaTask(task_id);
setSlateProgress({
keyword,
status: st.status,
message: st.message || `진행률 ${st.progress}%`,
});
if (st.status === 'succeeded' || st.status === 'failed') break;
await new Promise(r => setTimeout(r, 3000));
}
if (st && st.status === 'succeeded' && st.result_id) {
// 완료 — Cards 탭으로 자동 이동해서 SlateDetail 보여주기
setSlateProgress({
keyword, status: 'succeeded', message: '완료', slate_id: st.result_id,
});
setSelectedSlateId(st.result_id);
switchTab('cards');
// 3초 후 progress 배너 자동 dismiss
setTimeout(() => setSlateProgress(null), 3000);
} else {
setSlateProgress({
keyword, status: 'failed',
error: (st && st.error) || '시간 초과 또는 알 수 없는 오류',
});
}
} catch (e) { } catch (e) {
alert('카드 생성 실패: ' + e.message); setSlateProgress({ keyword, status: 'failed', error: e.message });
} }
}, []); }, []);
@@ -347,6 +385,28 @@ export default function InstaCards() {
>📈 Trends</button> >📈 Trends</button>
</div> </div>
{/* ── 카드 생성 progress 배너 (양 탭 공통) ── */}
{slateProgress && (
<div
className={`ic-slate-progress ic-slate-progress--${slateProgress.status}`}
onClick={() => slateProgress.status !== 'processing' && slateProgress.status !== 'starting' && setSlateProgress(null)}
>
{slateProgress.status === 'starting' && '⏳'}
{slateProgress.status === 'processing' && '🎨'}
{slateProgress.status === 'succeeded' && '✅'}
{slateProgress.status === 'failed' && '⚠️'}
{' '}
<strong>{slateProgress.keyword}</strong>
{' — '}
{slateProgress.status === 'failed'
? `실패: ${slateProgress.error}`
: slateProgress.message}
{(slateProgress.status === 'starting' || slateProgress.status === 'processing') && (
<span className="ic-slate-progress__hint"> · Claude로 10페이지 카피 추론 + Playwright로 카드 10 생성 (3~7)</span>
)}
</div>
)}
{/* ── Cards 탭 (기존 5-패널) ── */} {/* ── Cards 탭 (기존 5-패널) ── */}
{activeTab === 'cards' && ( {activeTab === 'cards' && (
<> <>