From 8e0e26c50b8f76f686c4be613f9b3aaa19e6c34d Mon Sep 17 00:00:00 2001 From: becarta Date: Sun, 8 Jun 2025 01:55:33 +0200 Subject: [PATCH] Enhance UptimeStatusIsland component with badge toggle functionality and click handling - Added state management for open badges and refs for badge popups. - Implemented toggle functionality for badge information on click. - Updated badge rendering to support mobile interactions, improving user experience. --- src/components/UptimeStatusIsland.jsx | 93 +++++++++++++++++++++++---- 1 file changed, 82 insertions(+), 11 deletions(-) diff --git a/src/components/UptimeStatusIsland.jsx b/src/components/UptimeStatusIsland.jsx index acf626e..9011426 100644 --- a/src/components/UptimeStatusIsland.jsx +++ b/src/components/UptimeStatusIsland.jsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect, useCallback } from 'react'; +import React, { useState, useEffect, useCallback, useRef } from 'react'; import { FiAward, FiPercent, FiActivity } from 'react-icons/fi'; function getStatusColor(validCert) { @@ -47,6 +47,10 @@ export default function UptimeStatusIsland() { const [data, setData] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); + // Track open badge for each monitor by monitor.id and badge type + const [openBadge, setOpenBadge] = useState({}); + // Refs for each badge popup + const badgeRefs = useRef({}); const fetchData = useCallback(async () => { setLoading(true); @@ -67,6 +71,41 @@ export default function UptimeStatusIsland() { fetchData(); }, [fetchData]); + // Helper to toggle badge info + const toggleBadge = (monitorId, badge) => { + setOpenBadge((prev) => ({ + ...prev, + [monitorId]: { + ...prev[monitorId], + [badge]: !prev[monitorId]?.[badge], + }, + })); + }; + + // Close popup on outside click + useEffect(() => { + function handleClick(e) { + let clickedInside = false; + Object.values(badgeRefs.current).forEach((monitorBadges) => { + Object.values(monitorBadges || {}).forEach((ref) => { + if (ref && ref.current && ref.current.contains(e.target)) { + clickedInside = true; + } + }); + }); + if (!clickedInside) setOpenBadge({}); + } + document.addEventListener('mousedown', handleClick); + return () => document.removeEventListener('mousedown', handleClick); + }, []); + + // Helper to get/create refs for each badge + const getBadgeRef = (monitorId, badge) => { + if (!badgeRefs.current[monitorId]) badgeRefs.current[monitorId] = {}; + if (!badgeRefs.current[monitorId][badge]) badgeRefs.current[monitorId][badge] = React.createRef(); + return badgeRefs.current[monitorId][badge]; + }; + return (
@@ -89,7 +128,7 @@ export default function UptimeStatusIsland() { {monitor.name}
{/* Cert Exp (center-left, col-span-1) */} -
+
{monitor.certExpiryDaysRemaining !== undefined && ( <> {/* Desktop: full badge */} @@ -97,40 +136,72 @@ export default function UptimeStatusIsland() { title={monitor.certExpiryDaysRemaining < 0 ? 'Certificate expired!' : `Certificate expires in ${monitor.certExpiryDaysRemaining} days`}> {getCertText(monitor.certExpiryDaysRemaining)} - {/* Mobile: icon badge */} - + {/* Mobile: icon badge with click */} + toggleBadge(monitor.id, 'cert')} + style={{ cursor: 'pointer', position: 'relative' }} + > + {openBadge[monitor.id]?.cert && ( + + {monitor.certExpiryDaysRemaining < 0 + ? 'Expired!' + : `Expires in ${monitor.certExpiryDaysRemaining} days`} + + )} )}
{/* Avg. (center-right, col-span-1) */} -
+
{monitor.avgPing !== undefined && ( <> {/* Desktop: full badge */} Avg.: {monitor.avgPing < 100 ? monitor.avgPing.toFixed(1) : Math.round(monitor.avgPing)} ms - {/* Mobile: icon badge */} - + {/* Mobile: icon badge with click */} + toggleBadge(monitor.id, 'avg')} + style={{ cursor: 'pointer', position: 'relative' }} + > + {openBadge[monitor.id]?.avg && ( + + {monitor.avgPing < 100 ? monitor.avgPing.toFixed(1) : Math.round(monitor.avgPing)} ms + + )} )}
{/* 24h (right, col-span-1) */} -
+
{monitor.uptime24h !== undefined && ( <> {/* Desktop: full badge */} 24h: {monitor.uptime24h.toFixed(1)}% - {/* Mobile: icon badge */} - + {/* Mobile: icon badge with click */} + toggleBadge(monitor.id, 'uptime')} + style={{ cursor: 'pointer', position: 'relative' }} + > + {openBadge[monitor.id]?.uptime && ( + + {monitor.uptime24h.toFixed(1)}% + + )} )}