diff --git a/src/pages/insta/InstaCards.css b/src/pages/insta/InstaCards.css index 367c8d0..2100a84 100644 --- a/src/pages/insta/InstaCards.css +++ b/src/pages/insta/InstaCards.css @@ -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__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; +} diff --git a/src/pages/insta/InstaCards.jsx b/src/pages/insta/InstaCards.jsx index 975cb24..15b0e68 100644 --- a/src/pages/insta/InstaCards.jsx +++ b/src/pages/insta/InstaCards.jsx @@ -300,6 +300,10 @@ function PreferenceImpactPanel() { export default function InstaCards() { const [status, setStatus] = 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 동기화) ── */ const [activeTab, setActiveTab] = useState(() => { @@ -323,13 +327,47 @@ export default function InstaCards() { 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 } = {}) => { + if (!keyword || !category) { + alert('keyword + category 필수'); + return; + } + setSlateProgress({ keyword, status: 'starting', message: '카드 생성 시작...' }); try { - await createInstaSlate({ keyword, category, keyword_id }); - setSelectedSlateId(null); + const { task_id } = await createInstaSlate({ keyword, category, keyword_id }); + 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) { - alert('카드 생성 실패: ' + e.message); + setSlateProgress({ keyword, status: 'failed', error: e.message }); } }, []); @@ -347,6 +385,28 @@ export default function InstaCards() { >📈 Trends + {/* ── 카드 생성 progress 배너 (양 탭 공통) ── */} + {slateProgress && ( +