diff --git a/app/src/components/Dashboard.tsx b/app/src/components/Dashboard.tsx new file mode 100644 index 0000000..a409a9f --- /dev/null +++ b/app/src/components/Dashboard.tsx @@ -0,0 +1,85 @@ +import { useEffect, useState } from 'react'; +import styles from '../styles/Dashboard.module.css'; + +interface MetricsData { + totalPlayers: number; + totalRuns: number; + activePlayers24h: number; + totalBossesDefeated: number; + totalPrestigeLevelsGained: number; +} + +const SUBGRAPH_URL = "https://api.studio.thegraph.com/query/75782/slay-the-moloch-base-mainnet/version/latest"; + +const Dashboard = () => { + const [metrics, setMetrics] = useState(); + + useEffect(() => { + const fetchMetrics = async () => { + try { + const response = await fetch(SUBGRAPH_URL, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + query: `{ + # Get global stats + globalStat(id: "1") { + totalPlayers + totalBossesDefeated + totalPrestigeLevels + totalRuns + } + # Get active players in last 24h + players(where: { lastRaidedAt_gt: "${Math.floor(Date.now() / 1000) - 86400}" }) { + id + } + }` + }) + }); + + const data = await response.json(); + + setMetrics({ + totalPlayers: parseInt(data.data.globalStat.totalPlayers), + totalRuns: parseInt(data.data.globalStat.totalRuns), + activePlayers24h: data.data.players.length, + totalBossesDefeated: parseInt(data.data.globalStat.totalBossesDefeated), + totalPrestigeLevels: parseInt(data.data.globalStat.totalPrestigeLevels) + }); + } catch (error) { + console.error("Error fetching metrics:", error); + } + }; + + fetchMetrics(); + const interval = setInterval(fetchMetrics, 30000); // Refresh every 30 seconds + return () => clearInterval(interval); + }, []); + + return ( +
+
+

Total Players

+

{metrics?.totalPlayers || 0}

+
+
+

Total Game Runs

+

{metrics?.totalRuns || 0}

+
+
+

Active Players (24h)

+

{metrics?.activePlayers24h || 0}

+
+
+

Total Bosses Defeated

+

{metrics?.totalBossesDefeated || 0}

+
+
+

Total Prestige Levels

+

{metrics?.totalPrestigeLevels || 0}

+
+
+ ); +}; + +export default Dashboard; \ No newline at end of file diff --git a/app/src/components/Scene.tsx b/app/src/components/Scene.tsx index e891639..ea13474 100644 --- a/app/src/components/Scene.tsx +++ b/app/src/components/Scene.tsx @@ -8,6 +8,7 @@ import Leaderboard from "./Leaderboard"; import { usePlayer } from "../providers/PlayerProvider"; import Boss from "./Boss"; import BossInfo from "./BossInfo"; +import Link from "next/link"; const bossToMountainsClass = { 0: styles.mountains0, @@ -53,6 +54,7 @@ const Scene = () => { > 🏆 Top players + 📈 Game metrics {isLeaderboardOpen && (
diff --git a/app/src/pages/metrics.tsx b/app/src/pages/metrics.tsx new file mode 100644 index 0000000..1018452 --- /dev/null +++ b/app/src/pages/metrics.tsx @@ -0,0 +1,15 @@ +import Dashboard from '../components/Dashboard'; +import styles from '../styles/Metrics.module.css'; +import Link from 'next/link'; + +const MetricsPage = () => { + return ( +
+ ← Back to game +

Game Metrics

+ +
+ ); +}; + +export default MetricsPage; \ No newline at end of file diff --git a/app/src/styles/Background.module.css b/app/src/styles/Background.module.css index 2d426d6..6c97a88 100644 --- a/app/src/styles/Background.module.css +++ b/app/src/styles/Background.module.css @@ -402,7 +402,8 @@ } } -.leaderboardButton { +.leaderboardButton, +.metricsButton { position: absolute; top: 30px; left: 80px; @@ -425,6 +426,12 @@ } } } +.metricsButton { + left: auto; + top: auto; + right: 32px; + bottom: 32px; +} .leaderboardButton:hover { transform: scale(1.1); diff --git a/app/src/styles/Dashboard.module.css b/app/src/styles/Dashboard.module.css new file mode 100644 index 0000000..cef3f41 --- /dev/null +++ b/app/src/styles/Dashboard.module.css @@ -0,0 +1,29 @@ +.dashboard { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); + gap: 2rem; + padding: 2rem; + max-width: 1200px; + margin: 0 auto; + } + + .metric { + background: rgba(0, 0, 0, 0.8); + border-radius: 8px; + padding: 1.5rem; + text-align: center; + border: 1px solid rgba(255, 255, 255, 0.1); + } + + .metric h3 { + color: #888; + margin: 0 0 1rem 0; + font-size: 1.1rem; + } + + .metric p { + color: #fff; + font-size: 2rem; + margin: 0; + font-weight: bold; + } \ No newline at end of file diff --git a/app/src/styles/Metrics.module.css b/app/src/styles/Metrics.module.css new file mode 100644 index 0000000..7491b44 --- /dev/null +++ b/app/src/styles/Metrics.module.css @@ -0,0 +1,23 @@ +.metricsPage { + min-height: 100vh; + padding: 2rem; + background: #1a1a1a; + color: white; + } + + .metricsPage h1 { + text-align: center; + margin-bottom: 3rem; + } + + .backLink { + display: inline-block; + color: #888; + text-decoration: none; + margin-bottom: 2rem; + transition: color 0.2s; + } + + .backLink:hover { + color: white; + } \ No newline at end of file