obsidianvault.ai-dashboard
obsidianvault.ai-dashboard
import { useEffect, useState } from "react"; import { motion } from "framer-motion"; import { Card, CardContent } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { LineChart, Line, XAxis, YAxis, Tooltip, ResponsiveContainer } from "recharts"; import { Globe, Users, Lock, Coins } from "lucide-react";
export default function Dashboard() { const [stats, setStats] = useState(null); const [supporters, setSupporters] = useState([]); const [withdrawals, setWithdrawals] = useState([]);
useEffect(() => { const fetchData = async () => { const [s, sup, wd] = await Promise.all([ fetch("/api/stats").then(r => r.json()), fetch("/api/supporters").then(r => r.json()), fetch("/api/withdrawals").then(r => r.json()) ]); setStats(s); setSupporters(sup); setWithdrawals(wd); }; fetchData();
// realtime websocket updates
const ws = new WebSocket(`${window.location.origin.replace(/^http/, "ws")}/api/realtime`);
ws.onmessage = (e) => {
const msg = JSON.parse(e.data);
if (msg.type === "stats") setStats(msg.data);
if (msg.type === "supporters") setSupporters(msg.data);
if (msg.type === "withdrawals") setWithdrawals(msg.data);
};
return () => ws.close();
}, []);
if (!stats) { return
return (
{/* Stat cards */}
<div className="grid md:grid-cols-4 gap-6">
<Card>
<CardContent className="p-4 flex flex-col items-center">
<Coins className="w-8 h-8 text-indigo-600" />
<p className="mt-2 text-lg font-semibold">${stats.totalFunds.toLocaleString()}</p>
<p className="text-sm text-gray-500">Total Funds Raised</p>
</CardContent>
</Card>
<Card>
<CardContent className="p-4 flex flex-col items-center">
<Users className="w-8 h-8 text-indigo-600" />
<p className="mt-2 text-lg font-semibold">{stats.totalSupporters}</p>
<p className="text-sm text-gray-500">Supporters</p>
</CardContent>
</Card>
<Card>
<CardContent className="p-4 flex flex-col items-center">
<Globe className="w-8 h-8 text-indigo-600" />
<p className="mt-2 text-lg font-semibold">{supporters.length}</p>
<p className="text-sm text-gray-500">Countries Reached</p>
</CardContent>
</Card>
<Card>
<CardContent className="p-4 flex flex-col items-center">
<Lock className="w-8 h-8 text-indigo-600" />
<p className="mt-2 text-lg font-semibold">{withdrawals.length}</p>
<p className="text-sm text-gray-500">Pending Withdrawals</p>
</CardContent>
</Card>
</div>
{/* Chart */}
<Card>
<CardContent className="p-4">
<h2 className="text-lg font-semibold mb-4">Funding Growth</h2>
<ResponsiveContainer width="100%" height={300}>
<LineChart data={stats.history}>
<XAxis dataKey="time" />
<YAxis />
<Tooltip />
<Line type="monotone" dataKey="funds" stroke="#4f46e5" strokeWidth={2} />
</LineChart>
</ResponsiveContainer>
</CardContent>
</Card>
{/* Withdrawals table */}
<Card>
<CardContent className="p-4">
<h2 className="text-lg font-semibold mb-4">Withdrawal Queue</h2>
<table className="w-full text-left">
<thead>
<tr className="border-b">
<th className="py-2">Wallet</th>
<th className="py-2">Amount</th>
<th className="py-2">Unlocks In</th>
</tr>
</thead>
<tbody>
{withdrawals.map((w, i) => (
<tr key={i} className="border-b">
<td className="py-2">{w.wallet}</td>
<td className="py-2">${w.amount.toLocaleString()}</td>
<td className="py-2">{w.unlocksIn} days</td>
</tr>
))}
</tbody>
</table>
</CardContent>
</Card>
{/* CTA */}
<div className="text-center">
<Button size="lg" className="bg-indigo-600 hover:bg-indigo-700 text-white">
Become a Supporter
</Button>
</div>
</div>
); }
How to Install
- Download the dashboard markdown file from GitHub
- Drop it into your vault (anywhere)
- Install the Homepage plugin and point it at the file
- Enable any listed CSS snippets for the intended look
Stats
Stars
0
Forks
0
Last updated 11mo ago
Categories