refactor(insta): Trends 3패널+PromptTemplatesEditor 추출 → InstaCards shell 확정
This commit is contained in:
104
src/pages/insta/components/ExternalTrendsPanel.jsx
Normal file
104
src/pages/insta/components/ExternalTrendsPanel.jsx
Normal file
@@ -0,0 +1,104 @@
|
||||
import React, { useState, useCallback, useEffect } from 'react';
|
||||
import TaskStatusBox from './TaskStatusBox';
|
||||
import { fmtDate } from '../instaUtils';
|
||||
import { getInstaTrends, instaCollectTrends, getInstaTask } from '../../../api';
|
||||
|
||||
/* ══════════════════════ Trends 탭 패널 2: ExternalTrendsPanel ══════════ */
|
||||
const CATEGORY_COLORS = {
|
||||
economy: '#0F62FE', psychology: '#A66CFF',
|
||||
celebrity: '#FF5C8A', uncategorized: '#6B7280',
|
||||
};
|
||||
|
||||
function ExternalTrendsPanel({ onCreateSlate }) {
|
||||
const [naver, setNaver] = useState([]);
|
||||
const [google, setGoogle] = useState([]);
|
||||
const [lastFetched, setLastFetched] = useState(null);
|
||||
const [collecting, setCollecting] = useState(false);
|
||||
const [task, setTask] = useState(null);
|
||||
|
||||
const load = useCallback(async () => {
|
||||
const [n, g] = await Promise.all([
|
||||
getInstaTrends({ source: 'naver_popular', days: 2 }),
|
||||
getInstaTrends({ source: 'youtube_trending', days: 2 }),
|
||||
]);
|
||||
setNaver(n.items || []);
|
||||
setGoogle(g.items || []);
|
||||
const all = [...(n.items || []), ...(g.items || [])];
|
||||
if (all.length) {
|
||||
const latest = all.map(t => t.suggested_at).sort().reverse()[0];
|
||||
setLastFetched(latest);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => { load(); }, [load]);
|
||||
|
||||
const trigger = async () => {
|
||||
setCollecting(true);
|
||||
try {
|
||||
const { task_id } = await instaCollectTrends();
|
||||
let st = null;
|
||||
for (let i = 0; i < 60; i++) {
|
||||
st = await getInstaTask(task_id);
|
||||
setTask(st);
|
||||
if (st.status === 'succeeded' || st.status === 'failed') break;
|
||||
await new Promise(r => setTimeout(r, 3000));
|
||||
}
|
||||
await load();
|
||||
} finally { setCollecting(false); }
|
||||
};
|
||||
|
||||
const groupByCat = (items) => {
|
||||
const g = {};
|
||||
items.forEach(it => { (g[it.category] = g[it.category] || []).push(it); });
|
||||
return g;
|
||||
};
|
||||
|
||||
const renderRow = (t) => (
|
||||
<div className="ic-trend__row" key={`${t.source}-${t.id}`}>
|
||||
<span className="ic-trend__cat-dot" style={{ background: CATEGORY_COLORS[t.category] || '#6B7280' }} />
|
||||
<span className="ic-trend__kw">{t.keyword}</span>
|
||||
<span className="ic-trend__score">{(t.score || 0).toFixed(2)}</span>
|
||||
<button
|
||||
className="ic-trend__make"
|
||||
onClick={() => onCreateSlate?.({ keyword: t.keyword, category: t.category })}
|
||||
>🎴</button>
|
||||
</div>
|
||||
);
|
||||
|
||||
const naverGrouped = groupByCat(naver);
|
||||
return (
|
||||
<section className="ic-panel ic-panel--trends">
|
||||
<div className="ic-panel__head">
|
||||
<h3 className="ic-panel__title">📈 외부 트렌드</h3>
|
||||
<div className="ic-panel__actions">
|
||||
<span className="ic-panel__hint">
|
||||
{lastFetched ? `마지막 수집: ${fmtDate(lastFetched)}` : '아직 수집 없음'}
|
||||
</span>
|
||||
<button onClick={trigger} disabled={collecting}>
|
||||
{collecting ? '수집 중...' : '🔄 수동 수집'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{task && <TaskStatusBox task={task} />}
|
||||
<div className="ic-trends__cols">
|
||||
<div className="ic-trends__col">
|
||||
<h4>🔥 NAVER 인기</h4>
|
||||
{Object.keys(naverGrouped).length === 0 && <p className="ic-empty">없음</p>}
|
||||
{Object.entries(naverGrouped).map(([cat, items]) => (
|
||||
<div key={cat} className="ic-trend__group">
|
||||
<div className="ic-trend__group-head" style={{ color: CATEGORY_COLORS[cat] || '#6B7280' }}>{cat}</div>
|
||||
{items.map(renderRow)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="ic-trends__col">
|
||||
<h4>📺 YouTube 인기</h4>
|
||||
{google.length === 0 && <p className="ic-empty">없음</p>}
|
||||
{google.map(renderRow)}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
export default ExternalTrendsPanel;
|
||||
Reference in New Issue
Block a user