diff --git a/pages/index.js b/pages/index.js index 3b65802..f745608 100644 --- a/pages/index.js +++ b/pages/index.js @@ -67,7 +67,7 @@ export default function Dashboard() { versions: true, peers: true, }); - + // Pagination and Search states const [versionPage, setVersionPage] = useState(1); const [peerPage, setPeerPage] = useState(1); @@ -83,8 +83,8 @@ export default function Dashboard() { useEffect(() => { if (searchQuery.trim()) { setIsSearching(true); - const results = activePeerIds.filter(peerId => - peerId.toLowerCase().includes(searchQuery.toLowerCase()) + const results = activeNodeIds.filter(nodeId => + nodeId.toLowerCase().includes(searchQuery.toLowerCase()) ); setSearchResults(results); setPeerPage(1); @@ -105,12 +105,24 @@ export default function Dashboard() { }); try { + // Calculate date range based on timeframe + const now = new Date(); + const startDate = new Date(); + if (timeframe === "7d") { + startDate.setDate(now.getDate() - 7); + } else if (timeframe === "30d") { + startDate.setDate(now.getDate() - 30); + } else if (timeframe === "1y") { + startDate.setDate(now.getDate() - 365); + } + // Fetch metrics const { data: metricsData, error: metricsError } = await supabase .from("metrics") .select("*") - .order("date", { ascending: true }) - .limit(timeframe === "7d" ? 7 : timeframe === "30d" ? 30 : 365); + .gte('date', startDate.toISOString().split('T')[0]) + .lte('date', now.toISOString().split('T')[0]) + .order("date", { ascending: true }); if (metricsError) throw metricsError; setMetrics(metricsData || []); @@ -188,7 +200,7 @@ export default function Dashboard() { const date = new Date(timestamp); const time = format(date, "HH:mm"); let dateText; - + if (isToday(date)) { dateText = "Today"; } else if (isYesterday(date)) { @@ -196,17 +208,26 @@ export default function Dashboard() { } else { dateText = format(date, "dd.MM.yyyy"); } - + return { time, dateText }; }; // Calculate statistics - const currentActiveNodes = activeNodes.length; + const totalUniqueNodes = [...new Set(activeNodes.map(node => node.node_id))].length; const averagePeerCount = activeNodes.length ? (activeNodes.reduce((acc, node) => acc + node.peer_count, 0) / activeNodes.length).toFixed(1) : 0; const activePeerIds = [...new Set(activeNodes.map((node) => node.peer_id))]; - const totalNodes = metrics.reduce((acc, day) => acc + day.new_records_count, 0); + + // Calculate today's active nodes + const todayStart = new Date(); + todayStart.setHours(0, 0, 0, 0); + const todayActiveNodes = [...new Set( + activeNodes + .filter(node => new Date(node.timestamp) >= todayStart) + .map(node => node.node_id) + )].length; + const versionDistribution = activeNodes.reduce((acc, node) => { acc[node.version] = (acc[node.version] || 0) + 1; return acc; @@ -223,12 +244,46 @@ export default function Dashboard() { const totalVersionPages = getPageCount(versionEntries.length); const totalPeerPages = getPageCount(displayPeerIds.length); + // Prepare chart data + const chartData = metrics.map((day) => ({ + date: new Date(day.date), + "Active Nodes": day.active_nodes_count + })); + + // Get unique node IDs and their latest records + const nodeRecords = activeNodes.reduce((acc, node) => { + if (!acc[node.node_id] || new Date(acc[node.node_id].timestamp) < new Date(node.timestamp)) { + acc[node.node_id] = node; + } + return acc; + }, {}); + + const activeNodeIds = Object.keys(nodeRecords); + const displayNodeIds = searchQuery ? searchResults : activeNodeIds; + const paginatedNodeIds = getPaginatedData(displayNodeIds, peerPage); + const totalNodePages = getPageCount(displayNodeIds.length); + + // Node details dialog state + const [selectedNode, setSelectedNode] = useState(null); + + // Format timestamp for node details + const formatNodeTimestamp = (timestamp) => { + if (!timestamp) return "N/A"; + const date = new Date(timestamp); + if (isToday(date)) { + return `Today at ${format(date, "HH:mm")}`; + } else if (isYesterday(date)) { + return `Yesterday at ${format(date, "HH:mm")}`; + } + return format(date, "MMM d, yyyy 'at' HH:mm"); + }; + return ( <> Codex Metrics - + {/* Open Graph / Facebook */} @@ -237,14 +292,14 @@ export default function Dashboard() { - + {/* Twitter */} - + {/* Additional SEO */} @@ -312,9 +367,9 @@ export default function Dashboard() {

The data displayed in this dashboard is collected from Codex nodes that use the{' '} - @@ -323,19 +378,19 @@ export default function Dashboard() { {' '}for running a Codex alturistic node in the testnet.

- Users agree to a privacy disclaimer before using the Codex CLI and the data collected will be used to - understand the testnet statistics and help troubleshooting users who face + Users agree to a privacy disclaimer before using the Codex CLI and the data collected will be used to + understand the testnet statistics and help troubleshooting users who face difficulty in getting onboarded to Codex.

-
+
-

Don't wish to provide data?

+

Don't wish to provide data?

You can still run a Codex node without providing any data. To do this, please follow the steps mentioned in the{' '} - @@ -356,7 +411,7 @@ export default function Dashboard() { The best way to get in touch with us is to join the{' '} @@ -384,8 +439,8 @@ export default function Dashboard() {

{[ { - title: "Active Nodes", - value: currentActiveNodes, + title: "Total Unique Nodes", + value: totalUniqueNodes, Icon: Users, delay: 0, isLoading: componentLoading.nodes, @@ -398,11 +453,11 @@ export default function Dashboard() { isLoading: componentLoading.nodes, }, { - title: "Total Nodes", - value: totalNodes, + title: "Active Today", + value: todayActiveNodes, Icon: Database, delay: 0.2, - isLoading: componentLoading.metrics, + isLoading: componentLoading.nodes, }, { title: "Last Updated", @@ -481,7 +536,7 @@ export default function Dashboard() { ) : (
- + @@ -589,7 +644,7 @@ export default function Dashboard() { )} - {/* Active Peer IDs List */} + {/* Active Node IDs List */} -
-

- - Active Peer IDs +
+

+ + Active Node IDs

+ setSearchQuery(e.target.value)} - className="w-full bg-neutral-800 border border-neutral-700 rounded-lg px-4 py-2 - text-sm placeholder-neutral-500 focus:border-[#7afbaf] focus:ring-1 - focus:ring-[#7afbaf] transition-colors outline-none" + placeholder="Search node IDs..." + className="pl-9 pr-4 py-2 bg-neutral-800 border border-neutral-700 rounded-lg text-sm + placeholder:text-neutral-500 focus:border-[#7afbaf] focus:ring-1 focus:ring-[#7afbaf] + transition-colors outline-none w-[200px]" /> -
- {componentLoading.peers ? ( - - ) : activePeerIds.length === 0 ? ( -
-

No active peers available

-
- ) : isSearching ? ( -
- -
- ) : searchQuery && searchResults.length === 0 ? ( -
-

No matching peer IDs found

+ {componentLoading.nodes ? ( + + ) : paginatedNodeIds.length === 0 ? ( +
+

+ {searchQuery ? "No matching node IDs found" : "No active node IDs"} +

) : ( <> -
- {paginatedPeerIds.map((peerId, index) => ( - - {peerId} - - ))} + {paginatedNodeIds.map((nodeId) => { + const node = nodeRecords[nodeId]; + return ( + + + + + + +
+ Codex +
+

Codex

+ Testnet +
+
+
+
+

NODE ID

+

{nodeId}

+
+
+

VERSION

+

{node.version}

+
+
+

LAST ONLINE

+

+ {formatNodeTimestamp(node.timestamp)} +

+
+
+

CONNECTED DHT PEERS

+

{node.peer_count}

+
+
+
+
+
+ ); + })}
diff --git a/public/logo.png b/public/logo.png new file mode 100644 index 0000000..ab6158d Binary files /dev/null and b/public/logo.png differ