From c7a31c794a4d45873d9d5d22511b01058fbb4e9d Mon Sep 17 00:00:00 2001 From: Massiles Ghernaout <749-gm213204@users.noreply.www-apps.univ-lehavre.fr> Date: Mon, 9 Feb 2026 01:27:12 +0100 Subject: [PATCH 1/2] make the UI nicer and improve UX --- frontend/src/App.jsx | 257 +++++++++++++++++++++++++++++++++---------- todo.txt | 6 - 2 files changed, 201 insertions(+), 62 deletions(-) diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 8449566..30192d8 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -12,6 +12,10 @@ const MODES = { const MODE_KEYS = ["veryGentle", "gentle", "normal", "fast", "aggressive", "veryAggressive"]; const intervalLabel = (ms) => (ms >= 1000 ? `${ms / 1000}s` : `${ms}ms`); + +const CLUSTER_POLL_INTERVAL_MS = 5000; +const CLUSTER_COUNTDOWN_SECS = Math.ceil(CLUSTER_POLL_INTERVAL_MS / 1000); // for good animation/UX + const MODE_LABELS = { veryGentle: `Très gentil (${intervalLabel(MODES.veryGentle)})`, gentle: `Gentil (${intervalLabel(MODES.gentle)})`, @@ -42,9 +46,14 @@ function App() { const [autoRunning, setAutoRunning] = useState(false); const [jobs, setJobs] = useState([]); const [clusterState, setClusterState] = useState(null); + + // for good UX/annimations + const [clusterCountdown, setClusterCountdown] = useState(CLUSTER_COUNTDOWN_SECS); + const [sendingHash, setSendingHash] = useState(false); const sendManual = async () => { if (!hashInput) return; + setSendingHash(true); try { const data = await apiRequest("/hash/manual", { method: "POST", @@ -55,6 +64,8 @@ function App() { } catch (e) { console.error(e); alert("Erreur lors de l'envoi du hash"); + } finally { + setSendingHash(false); } }; @@ -98,23 +109,31 @@ function App() { }, []); useEffect(() => { - // Monitoring cluster async function pollCluster() { try { const state = await apiRequest("/cluster/state"); setClusterState(state); + setClusterCountdown(CLUSTER_COUNTDOWN_SECS); } catch (e) { console.error("cluster state error", e); } } pollCluster(); - let pollClusterId = setInterval(pollCluster, 5000); + const pollClusterId = setInterval(pollCluster, CLUSTER_POLL_INTERVAL_MS); + return () => clearInterval(pollClusterId); + }, []); - return () => { - clearInterval(pollClusterId) - } - }, []) + // Countdown display: tick every second (resets to CLUSTER_COUNTDOWN_SECS after 0) + useEffect(() => { + if (clusterState == null) return; + + // Update our count down every second. + const t = setInterval(() => { + setClusterCountdown((c) => (c <= 0 ? CLUSTER_COUNTDOWN_SECS : c - 1)); + }, 1000); + return () => clearInterval(t); + }, [clusterState]); // Envoi auto de hash aléatoires selon le mode @@ -171,26 +190,80 @@ function App() { return (
Chargement de l'état du cluster…
+Aucun job pour le moment.
+Aucun job pour le moment.
) : ( -| ID | -Hash | -Statut | -Plaintext | -Temps (ms) | -||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| {job.id.slice(0, 8)}… | -{job.hash} | -- {job.result - ? job.result.found - ? "Trouvé" - : "Échec" - : "En cours / en file"} - | -- {job.result?.plaintext ?? "-"} - | -- {job.result?.elapsedMs ?? "-"} - | +
| ID | +Hash | +Statut | +Plaintext | +Temps (ms) |
|---|