-
-
-
Archive Isolated
-
System buffer awaiting briefing generation protocols
-
+
+ navigate('/toolkit')
+ }}
+ />
)}
diff --git a/frontend/src/pages/Scans.tsx b/frontend/src/pages/Scans.tsx
index 48a07f0b..cd7443c1 100644
--- a/frontend/src/pages/Scans.tsx
+++ b/frontend/src/pages/Scans.tsx
@@ -1,14 +1,12 @@
-import React, { useState, useEffect } from "react";
-import { useNavigate } from "react-router-dom";
-import { motion, AnimatePresence } from "framer-motion";
-import { API_BASE, deleteTask, clearAllTasks, bulkDeleteTasks } from "../api";
-import { routePath } from "../routes";
-import {
- parseDateSafe,
- formatLocaleDate,
- formatLocaleTime,
-} from "../utils/date";
-import Pagination from "../components/Pagination";
+import React, { useState, useEffect } from 'react'
+import { useNavigate } from 'react-router-dom'
+import { motion, AnimatePresence } from 'framer-motion'
+import { API_BASE, deleteTask, clearAllTasks, bulkDeleteTasks } from '../api'
+import { routePath } from '../routes'
+import { parseDateSafe, formatLocaleDate, formatLocaleTime } from '../utils/date'
+import LoadingSkeleton, { ListItemSkeleton } from '../components/LoadingSkeleton'
+import EmptyState from '../components/EmptyState'
+import { useLoadingState } from '../hooks/useLoadingState'
interface Task {
task_id: string;
@@ -53,39 +51,36 @@ const itemVariants = {
} as const;
export default function Scans() {
- const navigate = useNavigate();
- const [tasks, setTasks] = useState
([]);
- const [loading, setLoading] = useState(true);
- const [filter, setFilter] = useState("all");
- const [expandedId, setExpandedId] = useState(null);
- const [selectedIds, setSelectedIds] = useState([]);
- const [page, setPage] = useState(1);
- const [total, setTotal] = useState(0);
- const PAGE_LIMIT = 10;
-
- useEffect(() => {
- loadTasks();
- const interval = setInterval(loadTasks, 5000);
- return () => clearInterval(interval);
- }, [filter, page]);
-
- async function loadTasks() {
- try {
- const params = new URLSearchParams();
- if (filter !== "all") params.set("status", filter);
- params.set("page", String(page));
- params.set("per_page", String(PAGE_LIMIT));
-
- const res = await fetch(`${API_BASE}/tasks?${params.toString()}`);
- const data = await res.json();
- setTasks(data.tasks || []);
- if (data.pagination?.total_items !== undefined) {
- setTotal(data.pagination.total_items);
- }
- } catch (err) {
- console.error("Failed to load tasks:", err);
- } finally {
- setLoading(false);
+ const navigate = useNavigate()
+ const [tasks, setTasks] = useState([])
+ const { isLoading, isInitialLoad, error, startLoading, stopLoading, setError } = useLoadingState({ delay: 300, minDuration: 500 })
+ const [filter, setFilter] = useState('all')
+ const [expandedId, setExpandedId] = useState(null)
+ const [selectedIds, setSelectedIds] = useState([])
+
+ useEffect(() => {
+ loadTasks()
+ const interval = setInterval(loadTasks, 5000)
+ return () => clearInterval(interval)
+ }, [filter])
+
+ async function loadTasks() {
+ try {
+ startLoading()
+ const url = filter === 'all'
+ ? `${API_BASE}/tasks`
+ : `${API_BASE}/tasks?status=${filter}`
+
+ const res = await fetch(url)
+ const data = await res.json()
+ setTasks(data.tasks || [])
+ setError(null)
+ stopLoading()
+ } catch (err) {
+ console.error('Failed to load tasks:', err)
+ const errorMessage = err instanceof Error ? err.message : 'Failed to load tasks'
+ stopLoading(errorMessage)
+ }
}
}
function handleFilterChange(value: string) {
@@ -175,14 +170,23 @@ export default function Scans() {
}
}
- function toggleSelection(taskId: string, e: React.MouseEvent) {
- e.stopPropagation();
- setSelectedIds((prev) =>
- prev.includes(taskId)
- ? prev.filter((id) => id !== taskId)
- : [...prev, taskId],
- );
- }
+ return (
+
+
+ {/* Neo-Brutalist Header */}
+
+
+
+ Operational_Registry_v10.1
+
+
+ Operational Registry
+
+
+ Total_Registry_Keys: {tasks.length} // SYSTEM_STATUS: {isLoading ? 'SYNCING...' : 'SYNCED'}
+
+
+
function toggleSelectAll() {
if (selectedIds.length === tasks.length) {
@@ -344,76 +348,261 @@ export default function Scans() {
)
}
>
-
-
-
-
toggleSelection(task.task_id, e)}
- className={`w-10 h-10 border-4 border-black flex items-center justify-center transition-all ${
- selectedIds.includes(task.task_id)
- ? "bg-rag-blue text-black shadow-[4px_4px_0px_0px_rgba(0,0,0,1)] -translate-x-1 -translate-y-1"
- : "bg-charcoal-dark text-silver/10 hover:border-rag-blue/40"
- }`}
- >
-
- {selectedIds.includes(task.task_id)
- ? "check"
- : "add"}
-
-
-
- {task.status}
-
- {task.status === "queued" &&
- task.queue_position && (
-
- Queue #{task.queue_position}/
- {task.pending_count}
-
- )}
-
- OP_ID_{task.task_id.split("-")[0].toUpperCase()}
-
-
-
-
-
- {task.tool}
-
-
-
- target
-
- {task.target}
-
-
-
+
+ {selectedIds.length === tasks.length && tasks.length > 0 ? 'check_box' : 'check_box_outline_blank'}
+
+ Select_All
+
+
+ {statusFilters.map(f => (
+
+ ))}
+
+
+ {tasks.length > 0 && (
+
+ )}
+
+ Isolation_Protocol_Active // v4_stable
+
+
+
-
-
-
- Historical_Execution
-
-
- {formatLocaleDate(createDate)} //{" "}
- {formatLocaleTime(createDate)}
-
-
- {task.duration_seconds && (
-
-
- {formatDuration(
- task.duration_seconds,
- )?.toUpperCase()}
-
+ {/* Timeline Operations Feed */}
+
+ {/* Vertical Timeline Cable */}
+
+
+ {/* Loading State */}
+ {isLoading && isInitialLoad ? (
+
+
+
+ ) : error ? (
+
+
+
Registry_Load_Failed
+
{error}
+
+
+
+ ) : tasks.length > 0 ? (
+
+
+ {tasks.map((task) => {
+ const createDate = parseDateSafe(task.created_at);
+ const startDate = task.started_at ? parseDateSafe(task.started_at) : null;
+ const endDate = task.completed_at ? parseDateSafe(task.completed_at) : null;
+
+ return (
+
+ {/* Timeline Node */}
+
+
+ setExpandedId(expandedId === task.task_id ? null : task.task_id)}
+ >
+
+
+
+
toggleSelection(task.task_id, e)}
+ className={`w-10 h-10 border-4 border-black flex items-center justify-center transition-all ${
+ selectedIds.includes(task.task_id)
+ ? 'bg-rag-blue text-black shadow-[4px_4px_0px_0px_rgba(0,0,0,1)] -translate-x-1 -translate-y-1'
+ : 'bg-charcoal-dark text-silver/10 hover:border-rag-blue/40'
+ }`}
+ >
+
+ {selectedIds.includes(task.task_id) ? 'check' : 'add'}
+
+
+
+ {task.status}
+
+
+ OP_ID_{task.task_id.split('-')[0].toUpperCase()}
+
+
+
+
+
+ {task.tool}
+
+
+ target
+ {task.target}
+
+
+
+
+
+
+
Historical_Execution
+
+ {formatLocaleDate(createDate)} // {formatLocaleTime(createDate)}
+
+
+ {task.duration_seconds && (
+
+
{formatDuration(task.duration_seconds)?.toUpperCase()}
+
+ )}
+
+
+
+ {/* Expandable Details Block */}
+
+ {expandedId === task.task_id && (
+
+
+
+
+ Signal_Metadata
+
+
+
PLUGIN: {task.plugin_id}
+
SESSION: ENCRYPTED_VTX
+
+
+
+
+
+ Time_Matrix
+
+
+
+ In_Lock
+ {startDate ? formatLocaleTime(startDate) : 'PENDING'}
+
+
+ Release
+ {endDate ? formatLocaleTime(endDate) : 'N/A'}
+
+
+
+
+
+ {(task.status === 'completed' || task.status === 'failed' || task.status === 'cancelled') && (
+
+ )}
+ {(task.status === 'completed' || task.status === 'failed') && (
+
+ )}
+
+
+
+
+ )}
+
+
+
+ );
+ })}
+
+
+ ) : (
+ navigate(routePath.toolkit)
+ }}
+ />
+ )}
+
+
+ {/* Floating Bulk Action Bar */}
+
+ {selectedIds.length > 0 && (
+
+
+
+
{selectedIds.length}
+
+
Records_Selected_For_Pruning
+
Bulk_Action_Protocol_v4_Active
+
)}