mirror of
https://github.com/mgeeky/decode-spam-headers.git
synced 2026-02-22 13:33:30 +01:00
MAESTRO: wire analyse shortcut action feedback
This commit is contained in:
@@ -41,7 +41,7 @@ Note: `npx vitest run src/__tests__/AnalyseButton.test.tsx` passes; Vitest emits
|
|||||||
- [x] User can paste text into the header input area
|
- [x] User can paste text into the header input area
|
||||||
- [x] User can drop an EML/TXT file and see it auto-populate the input
|
- [x] User can drop an EML/TXT file and see it auto-populate the input
|
||||||
- [x] Analyse button is disabled when input is empty
|
- [x] Analyse button is disabled when input is empty
|
||||||
- [ ] Ctrl+Enter keyboard shortcut triggers the analyse action
|
- [x] Ctrl+Enter keyboard shortcut triggers the analyse action
|
||||||
- [ ] Dark hacker theme is visible with correct colour palette
|
- [ ] Dark hacker theme is visible with correct colour palette
|
||||||
- [ ] Validation shows user-friendly errors for empty and oversized input
|
- [ ] Validation shows user-friendly errors for empty and oversized input
|
||||||
- [ ] `npx eslint src/` and `npx prettier --check src/` pass with zero errors
|
- [ ] `npx eslint src/` and `npx prettier --check src/` pass with zero errors
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
|
|
||||||
import AnalyseButton from "../components/AnalyseButton";
|
import AnalyseButton from "../components/AnalyseButton";
|
||||||
import FileDropZone from "../components/FileDropZone";
|
import FileDropZone from "../components/FileDropZone";
|
||||||
@@ -8,7 +8,31 @@ import HeaderInput from "../components/HeaderInput";
|
|||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
const [headerInput, setHeaderInput] = useState("");
|
const [headerInput, setHeaderInput] = useState("");
|
||||||
|
const [isAnalyzing, setIsAnalyzing] = useState(false);
|
||||||
const hasHeaderInput = headerInput.trim().length > 0;
|
const hasHeaderInput = headerInput.trim().length > 0;
|
||||||
|
const analyseTimeoutRef = useRef<number | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
if (analyseTimeoutRef.current !== null) {
|
||||||
|
window.clearTimeout(analyseTimeoutRef.current);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleAnalyse = () => {
|
||||||
|
if (!hasHeaderInput) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsAnalyzing(true);
|
||||||
|
if (analyseTimeoutRef.current !== null) {
|
||||||
|
window.clearTimeout(analyseTimeoutRef.current);
|
||||||
|
}
|
||||||
|
analyseTimeoutRef.current = window.setTimeout(() => {
|
||||||
|
setIsAnalyzing(false);
|
||||||
|
}, 800);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main className="min-h-screen bg-background text-text">
|
<main className="min-h-screen bg-background text-text">
|
||||||
@@ -44,7 +68,11 @@ export default function Home() {
|
|||||||
heuristics, and delivery path insights.
|
heuristics, and delivery path insights.
|
||||||
</p>
|
</p>
|
||||||
<div className="mt-4 flex flex-col gap-3 sm:flex-row sm:items-center">
|
<div className="mt-4 flex flex-col gap-3 sm:flex-row sm:items-center">
|
||||||
<AnalyseButton hasInput={hasHeaderInput} onAnalyse={() => undefined} />
|
<AnalyseButton
|
||||||
|
hasInput={hasHeaderInput}
|
||||||
|
onAnalyse={handleAnalyse}
|
||||||
|
isLoading={isAnalyzing}
|
||||||
|
/>
|
||||||
<div className="flex items-center gap-2 text-xs text-text/60">
|
<div className="flex items-center gap-2 text-xs text-text/60">
|
||||||
<kbd className="rounded-md border border-info/30 bg-background/40 px-2 py-1 font-mono">
|
<kbd className="rounded-md border border-info/30 bg-background/40 px-2 py-1 font-mono">
|
||||||
Ctrl
|
Ctrl
|
||||||
|
|||||||
Reference in New Issue
Block a user