diff --git a/web/src/app/dashboard/page.tsx b/web/src/app/dashboard/page.tsx index deff416..0935854 100644 --- a/web/src/app/dashboard/page.tsx +++ b/web/src/app/dashboard/page.tsx @@ -1,6 +1,6 @@ "use client"; -import { useState, useEffect } from "react"; +import { useState, useEffect, useCallback } from "react"; import Link from "next/link"; import { Download, @@ -11,6 +11,7 @@ import { AlertTriangle, RefreshCw, AlertCircle, + FileDown, } from "lucide-react"; import { Navbar } from "@/components/Navbar"; import { Footer } from "@/components/Footer"; @@ -43,11 +44,75 @@ interface TestResult { }; } +type DateRange = "7" | "30" | "90" | "all"; + +const DATE_RANGE_OPTIONS: { value: DateRange; label: string }[] = [ + { value: "7", label: "Last 7 days" }, + { value: "30", label: "Last 30 days" }, + { value: "90", label: "Last 90 days" }, + { value: "all", label: "All time" }, +]; + +function exportToCSV(results: TestResult[]) { + const headers = [ + "Date", + "Server", + "Region", + "Avg Ping (ms)", + "Min Ping (ms)", + "Max Ping (ms)", + "Jitter (ms)", + "Packet Loss (%)", + "ISP", + "Country", + "City", + ]; + + const rows = results.map((r) => [ + new Date(r.created_at).toISOString(), + r.game_servers?.location ?? "Unknown", + r.game_servers?.region ?? "", + r.ping_avg, + r.ping_min, + r.ping_max, + r.jitter?.toFixed(2) ?? "0", + r.packet_loss, + r.isp ?? "Unknown", + r.country ?? "", + r.city ?? "", + ]); + + const csvContent = [headers, ...rows] + .map((row) => + row + .map((cell) => { + const str = String(cell); + // Wrap in quotes if contains comma, quote, or newline + if (str.includes(",") || str.includes('"') || str.includes("\n")) { + return `"${str.replace(/"/g, '""')}"`; + } + return str; + }) + .join(",") + ) + .join("\n"); + + const blob = new Blob([csvContent], { type: "text/csv;charset=utf-8;" }); + const url = URL.createObjectURL(blob); + const link = document.createElement("a"); + link.href = url; + link.download = `pingdiff-results-${new Date().toISOString().slice(0, 10)}.csv`; + link.click(); + URL.revokeObjectURL(url); +} + export default function DashboardPage() { const [results, setResults] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [selectedRegion, setSelectedRegion] = useState("all"); + const [dateRange, setDateRange] = useState("30"); + useEffect(() => { fetchResults(); }, []); @@ -70,11 +135,23 @@ export default function DashboardPage() { } }; - // Calculate stats + // Apply date range filter + const applyDateFilter = useCallback( + (data: TestResult[]): TestResult[] => { + if (dateRange === "all") return data; + const cutoff = new Date(); + cutoff.setDate(cutoff.getDate() - parseInt(dateRange)); + return data.filter((r) => new Date(r.created_at) >= cutoff); + }, + [dateRange] + ); + + // Apply region filter on top of date filter + const dateFiltered = applyDateFilter(results); const filteredResults = selectedRegion === "all" - ? results - : results.filter((r) => r.game_servers?.region === selectedRegion); + ? dateFiltered + : dateFiltered.filter((r) => r.game_servers?.region === selectedRegion); const avgPing = filteredResults.length > 0 @@ -100,7 +177,7 @@ export default function DashboardPage() { ).toFixed(1) : "0"; - // Get unique regions + // Get unique regions (from all results, not filtered) const regions = [ ...new Set(results.map((r) => r.game_servers?.region).filter(Boolean)), ]; @@ -152,22 +229,34 @@ export default function DashboardPage() { {/* Main Content */}
-
+

Dashboard

Your connection test results

- {/* Region Filter */} -
- + {/* Filters */} +
+ {/* Date range filter */} + + + {/* Region filter */} + + {/* Export CSV button */} + {filteredResults.length > 0 && ( + + )} + + {/* Refresh */} +
@@ -213,6 +326,20 @@ export default function DashboardPage() { Download PingDiff
+ ) : filteredResults.length === 0 ? ( +
+ +

No Results in This Range

+

+ No tests found for the selected filters. Try a wider date range or different region. +

+ +
) : ( <> {/* Stats Cards */} @@ -327,7 +454,12 @@ export default function DashboardPage() { {/* Recent Results Table */}
-

Recent Tests

+
+

Recent Tests

+ + Showing {Math.min(filteredResults.length, 10)} of {filteredResults.length} + +