diff --git a/src/api.js b/src/api.js
index 3984890..d669ee7 100644
--- a/src/api.js
+++ b/src/api.js
@@ -544,6 +544,27 @@ export function putInstaPrompt(name, template, description = '') {
return apiPut(`/api/insta/templates/prompts/${encodeURIComponent(name)}`, { template, description });
}
+// ── insta-lab trends ──
+export function getInstaTrends({ source, category, days = 1 } = {}) {
+ const q = new URLSearchParams();
+ if (source) q.set('source', source);
+ if (category) q.set('category', category);
+ q.set('days', String(days));
+ return apiGet(`/api/insta/trends?${q.toString()}`);
+}
+
+export function instaCollectTrends(categories) {
+ return apiPost('/api/insta/trends/collect', categories ? { categories } : {});
+}
+
+export function getInstaPreferences() {
+ return apiGet('/api/insta/preferences');
+}
+
+export function putInstaPreferences(categories) {
+ return apiPut('/api/insta/preferences', { categories });
+}
+
// ── Agent Office ──────────────────────────────────
export const getAgents = () => apiGet('/api/agent-office/agents');
export const getAgentDetail = (id) => apiGet(`/api/agent-office/agents/${id}`);
diff --git a/src/pages/insta/InstaCards.css b/src/pages/insta/InstaCards.css
index 624d968..367c8d0 100644
--- a/src/pages/insta/InstaCards.css
+++ b/src/pages/insta/InstaCards.css
@@ -100,3 +100,70 @@
/* 빈 상태 */
.ic-empty { text-align: center; padding: 40px 20px; color: rgba(255,255,255,.3); font-size: 0.85rem; }
+
+/* ── tabs ── */
+.ic-tabbar { display: flex; gap: 8px; border-bottom: 1px solid #e2e8f0; margin-bottom: 16px; }
+.ic-tab {
+ background: transparent; border: 0; padding: 10px 16px;
+ cursor: pointer; font-size: 14px; font-weight: 600;
+ color: #64748b; border-bottom: 2px solid transparent;
+}
+.ic-tab.is-active { color: #ec4899; border-bottom-color: #ec4899; }
+
+/* ── trends grid ── */
+.ic-trends-grid { display: grid; gap: 16px; grid-template-columns: 1fr; }
+@media (min-width: 1024px) { .ic-trends-grid { grid-template-columns: 320px 1fr; } }
+
+/* ── ic-panel base ── */
+.ic-panel { background: rgba(255,255,255,.04); border: 1px solid rgba(255,255,255,.06); border-radius: 12px; padding: 16px; }
+.ic-panel__title { font-size: 0.95rem; font-weight: 700; color: var(--text-primary, #e4e4e7); margin: 0 0 8px; }
+.ic-panel__hint { font-size: 0.78rem; color: rgba(255,255,255,.4); margin: 0 0 10px; }
+
+/* ── focus panel ── */
+.ic-panel--focus .ic-focus__list { display: flex; flex-direction: column; gap: 10px; margin: 12px 0; }
+.ic-focus__row { display: grid; grid-template-columns: 110px 1fr 50px; align-items: center; gap: 8px; }
+.ic-focus__label { font-weight: 600; color: #475569; text-transform: capitalize; }
+.ic-focus__slider { width: 100%; accent-color: #ec4899; }
+.ic-focus__num { text-align: right; font-variant-numeric: tabular-nums; color: #475569; }
+.ic-focus__add { display: flex; gap: 8px; margin-top: 12px; }
+.ic-focus__add input { flex: 1; padding: 8px; border: 1px solid #cbd5e1; border-radius: 6px; background: rgba(255,255,255,.06); color: var(--text-primary, #e4e4e7); }
+.ic-focus__add button { padding: 8px 14px; background: rgba(255,255,255,.08); border: 1px solid rgba(255,255,255,.12); border-radius: 6px; color: rgba(255,255,255,.7); cursor: pointer; font-size: 0.85rem; }
+.ic-focus__save {
+ width: 100%; padding: 10px; margin-top: 12px;
+ background: #ec4899; color: #fff; border: 0; border-radius: 6px; cursor: pointer;
+ font-weight: 700;
+}
+.ic-focus__save:disabled { opacity: .5; cursor: not-allowed; }
+.ic-focus__hint { margin-top: 12px; padding: 10px; background: rgba(245,158,11,.1); border-left: 3px solid #f59e0b; font-size: 12px; color: rgba(255,255,255,.6); line-height: 1.5; }
+.ic-focus__hint code { background: rgba(0,0,0,.2); padding: 1px 4px; border-radius: 3px; }
+
+/* ── trends panel ── */
+.ic-trends__cols { display: grid; grid-template-columns: 1fr; gap: 16px; }
+@media (min-width: 768px) { .ic-trends__cols { grid-template-columns: 1fr 1fr; } }
+.ic-trends__col h4 { margin: 0 0 8px; font-size: 14px; color: rgba(255,255,255,.5); }
+.ic-trend__group { margin-bottom: 14px; }
+.ic-trend__group-head { font-size: 12px; font-weight: 700; text-transform: uppercase; margin-bottom: 4px; letter-spacing: 0.5px; }
+.ic-trend__row {
+ display: grid; grid-template-columns: 10px 1fr 50px 36px;
+ align-items: center; gap: 8px; padding: 6px 4px;
+ border-bottom: 1px solid rgba(255,255,255,.06);
+}
+.ic-trend__cat-dot { width: 10px; height: 10px; border-radius: 50%; flex-shrink: 0; }
+.ic-trend__kw { font-weight: 500; color: var(--text-primary, #e4e4e7); font-size: 0.85rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
+.ic-trend__score { text-align: right; color: rgba(255,255,255,.4); font-variant-numeric: tabular-nums; font-size: 12px; }
+.ic-trend__make { background: #ec4899; border: 0; color: #fff; border-radius: 4px; cursor: pointer; padding: 4px; font-size: 14px; }
+.ic-trend__make:hover { background: #db2777; }
+
+.ic-panel__head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px; }
+.ic-panel__actions { display: flex; gap: 8px; align-items: center; }
+.ic-panel__actions button { padding: 6px 12px; background: rgba(255,255,255,.08); border: 1px solid rgba(255,255,255,.1); border-radius: 6px; color: rgba(255,255,255,.7); cursor: pointer; font-size: 0.8rem; }
+.ic-panel__actions button:disabled { opacity: .5; cursor: not-allowed; }
+
+/* ── impact panel ── */
+.ic-impact__row { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }
+.ic-impact__chip {
+ display: flex; align-items: baseline; gap: 6px;
+ padding: 6px 12px; background: rgba(255,255,255,.06); border-radius: 999px;
+}
+.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; }
diff --git a/src/pages/insta/InstaCards.jsx b/src/pages/insta/InstaCards.jsx
index 90d0755..d7c7d06 100644
--- a/src/pages/insta/InstaCards.jsx
+++ b/src/pages/insta/InstaCards.jsx
@@ -14,6 +14,10 @@ import {
getInstaTask,
getInstaPrompt,
putInstaPrompt,
+ getInstaTrends,
+ instaCollectTrends,
+ getInstaPreferences,
+ putInstaPreferences,
} from '../../api';
import './InstaCards.css';
@@ -92,11 +96,225 @@ function TaskStatusBox({ task }) {
);
}
+/* ══════════════════════ Trends 탭 패널 1: AccountFocusPanel ══════════════ */
+function AccountFocusPanel() {
+ const [prefs, setPrefs] = useState([]);
+ const [draft, setDraft] = useState({});
+ const [saving, setSaving] = useState(false);
+ const [newCat, setNewCat] = useState('');
+
+ const load = useCallback(async () => {
+ const data = await getInstaPreferences();
+ setPrefs(data.categories || []);
+ const m = {};
+ (data.categories || []).forEach(p => { m[p.category] = Math.round(p.weight * 100); });
+ setDraft(m);
+ }, []);
+
+ useEffect(() => { load(); }, [load]);
+
+ const save = async () => {
+ setSaving(true);
+ try {
+ const payload = {};
+ Object.entries(draft).forEach(([k, v]) => { payload[k] = (Number(v) || 0) / 100; });
+ await putInstaPreferences(payload);
+ await load();
+ } finally { setSaving(false); }
+ };
+
+ const addCat = () => {
+ const name = newCat.trim().toLowerCase();
+ if (!name || draft[name] !== undefined) return;
+ setDraft({ ...draft, [name]: 0 });
+ setNewCat('');
+ };
+
+ return (
+ 슬라이더는 각 카테고리에 자동 추출 키워드 비율을 결정합니다. 합계는 자동 정규화됩니다.🎯 이 계정의 주제 (카테고리 가중치)
+ category_seeds에 시드 키워드도 함께 정의해야 자동 추출에 반영됩니다.
+
없음
} + {Object.entries(naverGrouped).map(([cat, items]) => ( +없음
} + {google.map(renderRow)} +