From 59c9918fa8cc41895c7e78596cd50327175d1a2e Mon Sep 17 00:00:00 2001 From: Mark Krasner <70119343+mzkrasner@users.noreply.github.com> Date: Thu, 9 Jan 2025 10:14:41 -0500 Subject: [PATCH] Initial integration of light dark toggle using themeprovider (#20) * update Readme * Initial integration of light dark toggle using themeprovider * Moving theme-provider into providers directory in ui --------- Co-authored-by: kenny-io --- app/globals.css | 3 + app/latency/page.tsx | 12 +- app/layout.tsx | 5 + app/page.tsx | 16 ++- app/realtime/page.tsx | 66 +++++---- components/Footer.tsx | 160 +++++++++++---------- components/Header.tsx | 20 +-- components/LatencyMonitor.tsx | 64 ++++++--- components/UptimeMonitor.tsx | 61 ++++---- components/ui/mode-toggle.tsx | 25 ++++ components/ui/providers/theme-provider.tsx | 18 +++ 11 files changed, 270 insertions(+), 180 deletions(-) create mode 100644 components/ui/mode-toggle.tsx create mode 100644 components/ui/providers/theme-provider.tsx diff --git a/app/globals.css b/app/globals.css index 20b1c1d..54ef7d7 100644 --- a/app/globals.css +++ b/app/globals.css @@ -43,6 +43,7 @@ --chart-4: 43 74% 66%; --chart-5: 27 87% 67%; --radius: 0.5rem; + --destructive: 0 84.2% 60.2%; } .dark { --background: 0 0% 3.9%; @@ -69,6 +70,7 @@ --chart-3: 30 80% 55%; --chart-4: 280 65% 60%; --chart-5: 340 75% 55%; + --destructive: 0 70% 50%; } } @@ -80,3 +82,4 @@ @apply bg-background text-foreground; } } + diff --git a/app/latency/page.tsx b/app/latency/page.tsx index b25bdca..da4c1d1 100644 --- a/app/latency/page.tsx +++ b/app/latency/page.tsx @@ -1,15 +1,11 @@ - -import { LatencyMonitor } from '@/components/LatencyMonitor' +import { LatencyMonitor } from '@/components/LatencyMonitor'; export default function Home() { return ( -
+
- - -
- ) -} \ No newline at end of file + ); +} diff --git a/app/layout.tsx b/app/layout.tsx index e56a59b..ad02a23 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,10 +1,13 @@ import './globals.css'; import type { Metadata } from 'next'; import { Inter } from 'next/font/google'; +import {ThemeProvider} from "@/components/ui/providers/theme-provider"; const inter = Inter({ subsets: ['latin'] }); import { Footer } from '@/components/Footer'; import { Header } from '@/components/Header'; + + export const metadata: Metadata = { title: 'Shardeum Network Status Monitor', description: 'Monitor the uptime and latency of the Shardeum network', @@ -18,9 +21,11 @@ export default function RootLayout({ return ( +
{children}
+ ); diff --git a/app/page.tsx b/app/page.tsx index 0f7b8e7..1b2df39 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,13 +1,15 @@ -import { UptimeMonitor } from "@/components/UptimeMonitor" +import { UptimeMonitor } from "@/components/UptimeMonitor"; export default function Home() { return ( -
+

Service Status Monitor

-

Last 30 Days

-
+

+ Last 30 Days +

+
Online @@ -21,7 +23,7 @@ export default function Home() { Offline
-
+
No Data
@@ -29,5 +31,5 @@ export default function Home() {
- ) -} \ No newline at end of file + ); +} diff --git a/app/realtime/page.tsx b/app/realtime/page.tsx index 1073dc2..f01fd8f 100644 --- a/app/realtime/page.tsx +++ b/app/realtime/page.tsx @@ -1,28 +1,27 @@ -"use client" +"use client"; -import { Card } from '@/components/ui/card' -import { CheckCircle, XCircle, AlertCircle } from 'lucide-react' -import { useRealtimeStatus } from '@/hooks/useRealtimeStatus' -import { CountdownTimer } from '@/components/CountdownTimer' -import { Loader2 } from 'lucide-react' -import { format } from 'date-fns' +import { Card } from "@/components/ui/card"; +import { CheckCircle, XCircle, AlertCircle, Loader2 } from "lucide-react"; +import { useRealtimeStatus } from "@/hooks/useRealtimeStatus"; +import { format } from "date-fns"; const REFRESH_INTERVAL = 10000; // 10 seconds export default function RealtimePage() { - const { services, loading, error, lastUpdated, refresh } = useRealtimeStatus(REFRESH_INTERVAL); + const { services, loading, error, lastUpdated } = + useRealtimeStatus(REFRESH_INTERVAL); if (loading) { return ( -
- +
+
); } if (error) { return ( -
+
Failed to load service status: {error.message}
); @@ -38,54 +37,53 @@ export default function RealtimePage() { }, {} as Record); return ( -
+

Current Network Status

-
- Last updated: {format(lastUpdated, 'HH:mm:ss')} +
+ Last updated: {format(lastUpdated, "HH:mm:ss")}
-
- +
- {Object.entries(groupedServices).map(([group, services]) => ( - services.map(service => ( - + services.map((service) => ( +
{service.status === -1 ? ( - + ) : service.status === 1 ? ( ) : ( - + )}
- +

{service.name}

-

- {service.status === -1 - ? 'Unknown' - : service.status === 1 - ? 'Operational' - : 'Down'} +

+ {service.status === -1 + ? "Unknown" + : service.status === 1 + ? "Operational" + : "Down"}

- + {service.latency !== undefined && ( -

+

Response time: {service.latency.toFixed(0)}ms

)}
)) - ))} + )}
); -} \ No newline at end of file +} diff --git a/components/Footer.tsx b/components/Footer.tsx index dc61ed7..67a5df8 100644 --- a/components/Footer.tsx +++ b/components/Footer.tsx @@ -1,78 +1,90 @@ - const navigation = { - main: [ - { name: 'About', href: 'https://shardeum.org/' }, - { name: 'Blog', href: 'https://shardeum.org/blog/' }, - { name: 'Careers', href: 'https://careers.shardeum.org/companies' }, - { name: 'Developers', href: 'https://shardeum.org/developers/' }, - { name: 'Whitepaper', href: 'https://shardeum.org/Shardeum_Whitepaper.pdf' }, - { name: 'FAQs', href: 'https://shardeum.org/faq/general/' }, - ], - social: [ - - { - name: 'X', - href: 'https://twitter.com/shardeum', - icon: (props: any) => ( - - - - ), - }, - { - name: 'GitHub', - href: 'https://github.com/shardeum/', - icon: (props: any) => ( - - - - ), - }, - { - name: 'YouTube', - href: 'https://www.youtube.com/channel/UCO20LJZBF-lYbc6PWVvwkMA', - icon: (props: any) => ( - - - - ), - }, - ], -} + main: [ + { name: "About", href: "https://shardeum.org/" }, + { name: "Blog", href: "https://shardeum.org/blog/" }, + { name: "Careers", href: "https://careers.shardeum.org/companies" }, + { name: "Developers", href: "https://shardeum.org/developers/" }, + { + name: "Whitepaper", + href: "https://shardeum.org/Shardeum_Whitepaper.pdf", + }, + { name: "FAQs", href: "https://shardeum.org/faq/general/" }, + ], + social: [ + { + name: "X", + href: "https://twitter.com/shardeum", + icon: (props: any) => ( + + + + ), + }, + { + name: "GitHub", + href: "https://github.com/shardeum/", + icon: (props: any) => ( + + + + ), + }, + { + name: "YouTube", + href: "https://www.youtube.com/channel/UCO20LJZBF-lYbc6PWVvwkMA", + icon: (props: any) => ( + + + + ), + }, + ], +}; export const Footer = () => { - return ( -
-
- -
- {navigation.social.map((item) => ( - - {item.name} - - ))} -
-

- © {new Date(Date.now()).getFullYear()} Shardeum, Inc. All rights reserved. -

+ return ( + - ) -} + ))} + +
+ {navigation.social.map((item) => ( + + {item.name} + + ))} +
+

+ © {new Date(Date.now()).getFullYear()} Shardeum, Inc. All rights + reserved. +

+
+
+ ); +}; diff --git a/components/Header.tsx b/components/Header.tsx index 14ccb86..19a1886 100644 --- a/components/Header.tsx +++ b/components/Header.tsx @@ -1,7 +1,9 @@ -"use client" -import { Button } from "@/components/ui/button" -import Image from "next/image" -import Link from "next/link" +"use client"; +import { Button } from "@/components/ui/button"; +import { ModeToggle } from "@/components/ui/mode-toggle"; + +import Image from "next/image"; +import Link from "next/link"; export function Header() { return ( @@ -10,13 +12,13 @@ export function Header() {
Shardeum Logo -
@@ -26,9 +28,9 @@ export function Header() { - +
- ) -} \ No newline at end of file + ); +} diff --git a/components/LatencyMonitor.tsx b/components/LatencyMonitor.tsx index 2f03586..771687d 100644 --- a/components/LatencyMonitor.tsx +++ b/components/LatencyMonitor.tsx @@ -1,9 +1,9 @@ -"use client" +"use client"; -import { Card } from '@/components/ui/card' -import { useLatencyData } from '@/hooks/useLatencyData' -import { Loader2 } from 'lucide-react' -import { format } from 'date-fns' +import { Card } from "@/components/ui/card"; +import { useLatencyData } from "@/hooks/useLatencyData"; +import { Loader2 } from "lucide-react"; +import { format } from "date-fns"; import { LineChart, Line, @@ -12,8 +12,8 @@ import { CartesianGrid, Tooltip, Legend, - ResponsiveContainer -} from 'recharts' + ResponsiveContainer, +} from "recharts"; export function LatencyMonitor() { const { services, loading, error } = useLatencyData(); @@ -21,7 +21,7 @@ export function LatencyMonitor() { if (loading) { return (
- +
); } @@ -29,7 +29,7 @@ export function LatencyMonitor() { if (error) { console.error(error); return ( -
+
Failed to load latency data: {error.message}
); @@ -45,7 +45,7 @@ export function LatencyMonitor() { }, {} as Record); const formatDate = (timestamp: number) => { - return format(new Date(timestamp * 1000), 'HH:mm'); + return format(new Date(timestamp * 1000), "HH:mm"); }; const formatLatency = (value: number) => { @@ -53,28 +53,33 @@ export function LatencyMonitor() { }; const colors = [ - 'hsl(var(--chart-1))', - 'hsl(var(--chart-2))', - 'hsl(var(--chart-3))', - 'hsl(var(--chart-4))', - 'hsl(var(--chart-5))' + "hsl(var(--chart-1))", + "hsl(var(--chart-2))", + "hsl(var(--chart-3))", + "hsl(var(--chart-4))", + "hsl(var(--chart-5))", ]; return (
-

Response Time (Last Hour)

+

+ Response Time (Last Hour) +

{Object.entries(groupedServices).map(([group, groupServices]) => (
-

{group}

+

{group}

{groupServices.map((service, serviceIndex) => ( - +

{service.name}

-

+

Average latency: {service.averageLatency.toFixed(0)}ms

@@ -85,19 +90,32 @@ export function LatencyMonitor() { data={service.dataPoints} margin={{ top: 5, right: 30, left: 20, bottom: 5 }} > - + formatDate(label as number)} - formatter={(value: number) => [`${value.toFixed(0)}ms`, 'Latency']} + formatter={(value: number) => [ + `${value.toFixed(0)}ms`, + "Latency", + ]} + contentStyle={{ + backgroundColor: "hsl(var(--card))", + color: "hsl(var(--card-foreground))", + borderColor: "hsl(var(--border))", + }} /> ); -} \ No newline at end of file +} diff --git a/components/UptimeMonitor.tsx b/components/UptimeMonitor.tsx index 593aa16..6acc0e9 100644 --- a/components/UptimeMonitor.tsx +++ b/components/UptimeMonitor.tsx @@ -1,17 +1,17 @@ -"use client" +"use client"; -import { Card } from '@/components/ui/card'; +import { Card } from "@/components/ui/card"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from "@/components/ui/tooltip"; -import { format } from 'date-fns'; -import { usePrometheusData } from '@/hooks/usePrometheusData'; -import { Loader2 } from 'lucide-react'; -import { STATUS_COLORS, STATUS_LABELS, MINUTES_IN_DAY } from '@/lib/constants'; -import { formatUptime, formatDowntime } from '@/lib/uptime'; +import { format } from "date-fns"; +import { usePrometheusData } from "@/hooks/usePrometheusData"; +import { Loader2 } from "lucide-react"; +import { STATUS_COLORS, STATUS_LABELS, MINUTES_IN_DAY } from "@/lib/constants"; +import { formatUptime, formatDowntime } from "@/lib/uptime"; export function UptimeMonitor() { const { services, loading, error } = usePrometheusData(); @@ -19,14 +19,14 @@ export function UptimeMonitor() { if (loading) { return (
- +
); } if (error) { return ( -
+
Failed to load service status data: {error.message}
); @@ -46,31 +46,39 @@ export function UptimeMonitor() {
{Object.entries(groupedServices).map(([group, groupServices]) => (
-

{group}

+

{group}

{groupServices.map((service) => ( - +

{service.name}

- +
{service.uptime.map((point, i) => (
-

- {format(point.timestamp, 'MMM d, yyyy')} +

+ {format(point.timestamp, "MMM d, yyyy")}
- Status: {STATUS_LABELS[point.status as keyof typeof STATUS_LABELS]} - {/*
*/} - {/* Uptime: {formatUptime(MINUTES_IN_DAY - point.downtimeMinutes)} */} + Status:{" "} + { + STATUS_LABELS[ + point.status as keyof typeof STATUS_LABELS + ] + }
Downtime: {formatDowntime(point.downtimeMinutes)}

@@ -78,18 +86,21 @@ export function UptimeMonitor() { ))}
- -
+ +
- {service.uptime.length > 0 && - format(service.uptime[0].timestamp, 'MMM d, yyyy')} + {service.uptime.length > 0 && + format(service.uptime[0].timestamp, "MMM d, yyyy")} {service.uptimePercentage.toFixed(2)}% uptime - {service.uptime.length > 0 && - format(service.uptime[service.uptime.length - 1].timestamp, 'MMM d, yyyy')} + {service.uptime.length > 0 && + format( + service.uptime[service.uptime.length - 1].timestamp, + "MMM d, yyyy" + )}
@@ -100,4 +111,4 @@ export function UptimeMonitor() {
); -} \ No newline at end of file +} diff --git a/components/ui/mode-toggle.tsx b/components/ui/mode-toggle.tsx new file mode 100644 index 0000000..463069b --- /dev/null +++ b/components/ui/mode-toggle.tsx @@ -0,0 +1,25 @@ +"use client"; + +import * as React from "react"; +import { Moon, Sun } from "lucide-react"; +import { useTheme } from "next-themes"; + +import { Button } from "@/components/ui/button"; + +export function ModeToggle() { + const { theme, setTheme } = useTheme(); + + return ( + + ); +} diff --git a/components/ui/providers/theme-provider.tsx b/components/ui/providers/theme-provider.tsx new file mode 100644 index 0000000..5952e85 --- /dev/null +++ b/components/ui/providers/theme-provider.tsx @@ -0,0 +1,18 @@ +"use client"; + +import * as React from "react"; +import { ThemeProvider as NextThemesProvider } from "next-themes"; +import { type ThemeProviderProps } from "next-themes/dist/types"; + +export function ThemeProvider({ children, ...props }: ThemeProviderProps) { + return ( + + {children} + + ); +}