diff --git a/src/pages/music/components/RevenueTab.jsx b/src/pages/music/components/RevenueTab.jsx index 623301e..2fa48b5 100644 --- a/src/pages/music/components/RevenueTab.jsx +++ b/src/pages/music/components/RevenueTab.jsx @@ -1,3 +1,276 @@ +// src/pages/music/components/RevenueTab.jsx +import { useState, useEffect } from 'react'; +import { + getRevenueDashboard, getRevenueRecords, + addRevenueRecord, updateRevenueRecord, deleteRevenueRecord, +} from '../../../api'; + +const COUNTRIES = ['BR', 'US', 'ID', 'MX', 'KR']; +const currentMonth = () => new Date().toISOString().slice(0, 7); + export default function RevenueTab() { - return null; + const [dashboard, setDashboard] = useState(null); + const [records, setRecords] = useState([]); + const [form, setForm] = useState({ + yt_video_id: '', record_month: currentMonth(), + revenue_usd: '', views: '', country: 'BR', + }); + const [saving, setSaving] = useState(false); + const [editingId, setEditingId] = useState(null); + const [editForm, setEditForm] = useState({}); + + const loadAll = async () => { + const [dash, recs] = await Promise.all([ + getRevenueDashboard().catch(() => null), + getRevenueRecords().catch(() => []), + ]); + setDashboard(dash); + setRecords(Array.isArray(recs) ? recs : recs.records ?? []); + }; + + useEffect(() => { loadAll(); }, []); + + const handleAdd = async () => { + if (!form.yt_video_id || !form.revenue_usd || !form.views) return; + setSaving(true); + try { + await addRevenueRecord({ + yt_video_id: form.yt_video_id, + record_month: form.record_month, + revenue_usd: parseFloat(form.revenue_usd), + views: parseInt(form.views, 10), + country: form.country, + }); + setForm({ yt_video_id: '', record_month: currentMonth(), revenue_usd: '', views: '', country: 'BR' }); + await loadAll(); + } catch (e) { + console.error('addRevenueRecord:', e); + } finally { + setSaving(false); + } + }; + + const handleEditSave = async () => { + try { + await updateRevenueRecord(editingId, { + revenue_usd: parseFloat(editForm.revenue_usd), + views: parseInt(editForm.views, 10), + }); + setEditingId(null); + await loadAll(); + } catch (e) { + console.error('updateRevenueRecord:', e); + } + }; + + const handleDelete = async (id) => { + if (!window.confirm('이 기록을 삭제할까요?')) return; + try { + await deleteRevenueRecord(id); + await loadAll(); + } catch (e) { + console.error('deleteRevenueRecord:', e); + } + }; + + // 영상별 RPM 상위 5개 (bar chart 용) + const chartData = records + .filter(r => r.views > 0) + .map(r => ({ + label: r.yt_video_id, + rpm: (r.revenue_usd / r.views) * 1000, + })) + .sort((a, b) => b.rpm - a.rpm) + .slice(0, 5); + const maxRpm = chartData.length > 0 ? Math.max(...chartData.map(d => d.rpm)) : 1; + + return ( +
수익 기록이 없습니다. 위 폼으로 추가해보세요.
+ ) : ( +