feat(insta): trends 카드 생성 시 progress 배너 + 자동 미리보기 전환
Trends 탭의 🎴 버튼 클릭이 silent로 끝나 사용자에게 무동작처럼 보이던
이슈 fix. handleCreateSlate를 3초 간격 폴링으로 확장 (최대 8분):
- 시작/진행/성공/실패 상단 배너로 시각화
- 카드 생성 완료 시 자동으로 Cards 탭 전환 + 새 슬레이트 자동 선택
→ SlateDetail이 카피·이미지 미리보기 즉시 표시
- 실패 시 에러 메시지 + 클릭으로 dismiss
- "Claude 카피 추론 + Playwright 카드 10장 생성 중 (3~7분)" 안내 문구
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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</button>
|
||||
</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-패널) ── */}
|
||||
{activeTab === 'cards' && (
|
||||
<>
|
||||
|
||||
Reference in New Issue
Block a user