// screen-dashboard.jsx — Admin dashboard const { useState: useDashState } = React; // smooth catmull-rom -> bezier path function smoothPath(pts) { if (pts.length < 2) return ''; let d = `M ${pts[0][0]} ${pts[0][1]}`; for (let i = 0; i < pts.length - 1; i++) { const p0 = pts[i - 1] || pts[i], p1 = pts[i], p2 = pts[i + 1], p3 = pts[i + 2] || p2; const c1x = p1[0] + (p2[0] - p0[0]) / 6, c1y = p1[1] + (p2[1] - p0[1]) / 6; const c2x = p2[0] - (p3[0] - p1[0]) / 6, c2y = p2[1] - (p3[1] - p1[1]) / 6; d += ` C ${c1x} ${c1y}, ${c2x} ${c2y}, ${p2[0]} ${p2[1]}`; } return d; } function LineChart() { const W = 600, H = 230, pad = 28; const days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']; const cur = [3.2, 4.1, 3.6, 5.2, 6.1, 8.4, 10.2]; const prev = [4.0, 3.4, 4.6, 4.2, 6.6, 7.0, 8.6]; const max = 12; const toPts = (arr) => arr.map((v, i) => [pad + (i * (W - pad * 2)) / (days.length - 1), H - pad - (v / max) * (H - pad * 2)]); const cp = toPts(cur), pp = toPts(prev); const area = smoothPath(cp) + ` L ${cp[cp.length - 1][0]} ${H - pad} L ${cp[0][0]} ${H - pad} Z`; return ( {[0, 3, 6, 9, 12].map(v => { const y = H - pad - (v / max) * (H - pad * 2); return {v}M; })} {cp.map((p, i) => i === 5 ? 8,452,210 : null)} {days.map((d, i) => {d})} ); } function Donut({ segments, size = 160, thickness = 26, center }) { const r = (size - thickness) / 2, c = size / 2, circ = 2 * Math.PI * r; let off = 0; return ( {segments.map((s, i) => { const len = (s.pct / 100) * circ; const el = ; off += len; return el; })} {center && {center}} ); } function DashboardScreen({ openIdea, openUser, votes = {}, onVote }) { const Icon = window.Icon; const [tab, setTab] = useDashState('Total Users'); const [pending, setPending] = useDashState(window.IDEAS.filter(i => i.status === 'pending' || i.status === 'review').slice(0, 4).map(i => ({ ...i, decided: null }))); const tabs = ['Total Users', 'Total Ideas', 'Active Users', 'Pending Ideas']; const decide = (id, v) => setPending(p => p.map(x => x.id === id ? { ...x, decided: v } : x)); const adminStats = [ { key: 'a', icon: 'bulb', label: 'Total ideas', value: '1,000', trend: 9, tint: '#0a66ff' }, { key: 'b', icon: 'checkCircle', label: 'Published', value: '612', trend: 14, tint: '#15a564' }, { key: 'c', icon: 'calendar', label: 'Pending review', value: '188', trend: -6, tint: '#e8930c' }, { key: 'd', icon: 'users', label: 'Active users', value: '500', trend: 8, tint: '#6d5cf0' }, ]; return (
Dashboard
Admin overview of idea activity and moderation queue.
{adminStats.map(s => )}
General statistics
Current week Previous week
{tabs.map(t => )}
Ideas by status
{[['Published', '#15a564', 550], ['Pending', '#e8930c', 280], ['Rejected', '#e23b56', 170]].map(([l, c, v]) => (
{l} {window.fmt(v)}
))}
Traffic by devices
{window.DEVICES.map(d => (
{d.name}{d.pct}%
))}
Users by gender
Male Female
Most interactive users
{window.TOP_USERS.map((u, i) => (
openUser && openUser(u)}>
{u.name}
{u.role}
{u.ideas}
))}
{/* pending approvals */}
Last pending ideas
{pending.map(p => ( ))}
#UsernameIdea titleCategoryStatusAction
#{p.rank}
{p.author}
openIdea(p)}>{p.title} {p.decided === 'approve' ? : p.decided === 'reject' ? : } {p.decided ? {p.decided === 'approve' ? 'Approved' : 'Rejected'} : (
)}
Recent ideas
{window.IDEAS.slice(0, 4).map(i => ( openIdea(i)}> ))}
#UsernameIdea titleCategoryStatistics
#{i.rank}
{i.author}
{i.title} onVote(i.id, d)} />
); } window.DashboardScreen = DashboardScreen;