diff --git a/src/app/api/sentry-example-api/route.ts b/src/app/(routes)/api/sentry-example-api/route.ts
similarity index 100%
rename from src/app/api/sentry-example-api/route.ts
rename to src/app/(routes)/api/sentry-example-api/route.ts
diff --git a/src/app/auth/callback/route.ts b/src/app/(routes)/auth/callback/route.ts
similarity index 100%
rename from src/app/auth/callback/route.ts
rename to src/app/(routes)/auth/callback/route.ts
diff --git a/src/app/fetch/route.ts b/src/app/(routes)/fetch/route.ts
similarity index 100%
rename from src/app/fetch/route.ts
rename to src/app/(routes)/fetch/route.ts
diff --git a/src/app/public-stats/page.tsx b/src/app/public-stats/page.tsx
new file mode 100644
index 00000000..ca736ae7
--- /dev/null
+++ b/src/app/public-stats/page.tsx
@@ -0,0 +1,11 @@
+import React from "react";
+import PublicStatsDashboard from "@/components/stats-chart/StatsChart";
+const page = () => {
+ return (
+
+ );
+};
+
+export default page;
diff --git a/src/components/stats-chart/StatsChart.tsx b/src/components/stats-chart/StatsChart.tsx
new file mode 100644
index 00000000..93b01724
--- /dev/null
+++ b/src/components/stats-chart/StatsChart.tsx
@@ -0,0 +1,261 @@
+"use client";
+
+import { useState, useEffect } from "react";
+import {
+ Bar,
+ BarChart,
+ Cell,
+ Pie,
+ PieChart,
+ ResponsiveContainer,
+ XAxis,
+ YAxis,
+} from "recharts";
+import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
+import {
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+} from "@/components/ui/chart";
+import { Skeleton } from "@/components/ui/skeleton";
+
+interface PublicStats {
+ timestamp: string;
+ total_jobs: number;
+ main_chat_jobs: number;
+ individual_crew_jobs: number;
+ top_profile_stacks_addresses: string[];
+ top_crew_names: string[];
+}
+
+export default function PublicStatsDashboard() {
+ const [data, setData] = useState(null);
+
+ useEffect(() => {
+ const fetchData = async () => {
+ try {
+ const response = await fetch(
+ `${process.env.NEXT_PUBLIC_API_URL}/public_stats/`
+ );
+ if (!response.ok) throw new Error("Failed to fetch data");
+ const result: PublicStats = await response.json();
+ setData(result);
+ } catch (err) {
+ console.error(err);
+ }
+ };
+ void fetchData();
+ }, []);
+
+ if (!data) {
+ return (
+
+ );
+ }
+
+ const executionData = [
+ // RENAMING JOBS TO CHAT
+ { name: "Main Chat", value: data.main_chat_jobs },
+ { name: "Crew Chat", value: data.individual_crew_jobs },
+ ];
+
+ const addressData = data.top_profile_stacks_addresses.map(
+ (address, index) => ({
+ name: `A${index + 1}`,
+ value: 1,
+ fullAddress: address,
+ })
+ );
+
+ const crewData = data.top_crew_names.map((name, index) => ({
+ name: name.length > 12 ? name.slice(0, 12) + "..." : name,
+ value: Math.max(5 - index, 1),
+ }));
+
+ return (
+
+
+
Public Stats Dashboard
+
+ {new Date(data.timestamp).toLocaleString()}
+
+
+
+
+
+
+
+ Total Crew Executions
+
+
+
+
+ {data.total_jobs.toLocaleString()}
+
+
+
+
+
+
+ Main Chat Executions
+
+
+
+
+ {data.main_chat_jobs.toLocaleString()}
+
+
+
+
+
+
+ Individual Crew Jobs
+
+
+
+
+ {data.individual_crew_jobs.toLocaleString()}
+
+
+
+
+
+
+
+
+ Execution Distribution
+
+
+
+
+
+
+
+ } />
+
+ {executionData.map((entry, index) => (
+ |
+ ))}
+
+
+
+
+
+
+
+
+
+
+ Top Profile Stack Addresses
+
+
+
+
+
+
+ name}
+ labelLine={false}
+ >
+ {addressData.map((entry, index) => (
+ |
+ ))}
+
+ {
+ if (payload && payload.length) {
+ const data = payload[0].payload;
+ return (
+
+ );
+ }
+ return null;
+ }}
+ />
+
+
+
+
+
+
+
+
+
+ Top Crews
+
+
+
+
+
+
+
+ } />
+
+ {crewData.map((entry, index) => (
+ |
+ ))}
+
+
+
+
+
+
+
+ );
+}