feat: Pruned dead UI, renamed view toggle to Oldest/Newest first, added…
- "frontend/src/pages/AdminPipeline.tsx" - "frontend/src/App.css" GSD-Task: S04/T02
This commit is contained in:
parent
4186c6e208
commit
031f6d3d5b
2 changed files with 47 additions and 21 deletions
|
|
@ -2776,6 +2776,20 @@ a.app-footer__repo:hover {
|
|||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.pipeline-video__review-link {
|
||||
color: var(--color-accent);
|
||||
font-size: 0.75rem;
|
||||
text-decoration: none;
|
||||
white-space: nowrap;
|
||||
opacity: 0.7;
|
||||
transition: opacity 0.15s;
|
||||
}
|
||||
|
||||
.pipeline-video__review-link:hover {
|
||||
opacity: 1;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.pipeline-video__actions {
|
||||
display: flex;
|
||||
gap: 0.375rem;
|
||||
|
|
|
|||
|
|
@ -245,16 +245,15 @@ function EventLog({ videoId }: { videoId: string }) {
|
|||
className={`pipeline-events__view-btn${viewMode === "head" ? " pipeline-events__view-btn--active" : ""}`}
|
||||
onClick={() => { setViewMode("head"); setOffset(0); }}
|
||||
>
|
||||
Head
|
||||
Oldest first
|
||||
</button>
|
||||
<button
|
||||
className={`pipeline-events__view-btn${viewMode === "tail" ? " pipeline-events__view-btn--active" : ""}`}
|
||||
onClick={() => { setViewMode("tail"); setOffset(0); }}
|
||||
>
|
||||
Tail
|
||||
Newest first
|
||||
</button>
|
||||
</div>
|
||||
<button className="btn btn--small btn--secondary" onClick={() => void load()}>↻ Refresh</button>
|
||||
</div>
|
||||
|
||||
<div className="pipeline-events__list">
|
||||
|
|
@ -368,28 +367,21 @@ function WorkerStatus() {
|
|||
|
||||
// ── Debug Mode Toggle ────────────────────────────────────────────────────────
|
||||
|
||||
function DebugModeToggle() {
|
||||
const [debugMode, setDebugModeState] = useState<boolean | null>(null);
|
||||
function DebugModeToggle({
|
||||
debugMode,
|
||||
onDebugModeChange,
|
||||
}: {
|
||||
debugMode: boolean | null;
|
||||
onDebugModeChange: (mode: boolean) => void;
|
||||
}) {
|
||||
const [debugLoading, setDebugLoading] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
fetchDebugMode()
|
||||
.then((res) => {
|
||||
if (!cancelled) setDebugModeState(res.debug_mode);
|
||||
})
|
||||
.catch(() => {
|
||||
// silently fail — toggle stays hidden
|
||||
});
|
||||
return () => { cancelled = true; };
|
||||
}, []);
|
||||
|
||||
async function handleToggle() {
|
||||
if (debugMode === null || debugLoading) return;
|
||||
setDebugLoading(true);
|
||||
try {
|
||||
const res = await setDebugMode(!debugMode);
|
||||
setDebugModeState(res.debug_mode);
|
||||
onDebugModeChange(res.debug_mode);
|
||||
} catch {
|
||||
// swallow — leave previous state
|
||||
} finally {
|
||||
|
|
@ -463,6 +455,7 @@ export default function AdminPipeline() {
|
|||
const [actionLoading, setActionLoading] = useState<string | null>(null);
|
||||
const [actionMessage, setActionMessage] = useState<{ id: string; text: string; ok: boolean } | null>(null);
|
||||
const [activeFilter, setActiveFilter] = useState<string | null>(null);
|
||||
const [debugMode, setDebugModeState] = useState<boolean | null>(null);
|
||||
|
||||
const load = useCallback(async () => {
|
||||
setLoading(true);
|
||||
|
|
@ -481,6 +474,18 @@ export default function AdminPipeline() {
|
|||
void load();
|
||||
}, [load]);
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
fetchDebugMode()
|
||||
.then((res) => {
|
||||
if (!cancelled) setDebugModeState(res.debug_mode);
|
||||
})
|
||||
.catch(() => {
|
||||
// silently fail — toggle stays hidden
|
||||
});
|
||||
return () => { cancelled = true; };
|
||||
}, []);
|
||||
|
||||
const handleTrigger = async (videoId: string) => {
|
||||
setActionLoading(videoId);
|
||||
setActionMessage(null);
|
||||
|
|
@ -538,7 +543,7 @@ export default function AdminPipeline() {
|
|||
</p>
|
||||
</div>
|
||||
<div className="admin-pipeline__header-right">
|
||||
<DebugModeToggle />
|
||||
<DebugModeToggle debugMode={debugMode} onDebugModeChange={setDebugModeState} />
|
||||
<WorkerStatus />
|
||||
<button className="btn btn--secondary" onClick={() => void load()} disabled={loading}>
|
||||
↻ Refresh
|
||||
|
|
@ -588,6 +593,14 @@ export default function AdminPipeline() {
|
|||
<span className="pipeline-video__time">
|
||||
{formatDate(video.last_event_at)}
|
||||
</span>
|
||||
<a
|
||||
className="pipeline-video__review-link"
|
||||
href="/admin/review"
|
||||
title={`Review moments from ${video.creator_name}`}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
→ Moments
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div className="pipeline-video__actions" onClick={(e) => e.stopPropagation()}>
|
||||
|
|
@ -597,7 +610,7 @@ export default function AdminPipeline() {
|
|||
disabled={actionLoading === video.id}
|
||||
title="Retrigger pipeline"
|
||||
>
|
||||
{actionLoading === video.id ? "…" : "▶ Trigger"}
|
||||
{actionLoading === video.id ? "…" : debugMode ? "▶ Trigger (debug)" : "▶ Trigger"}
|
||||
</button>
|
||||
<button
|
||||
className="btn btn--small btn--danger"
|
||||
|
|
@ -619,7 +632,6 @@ export default function AdminPipeline() {
|
|||
{expandedId === video.id && (
|
||||
<div className="pipeline-video__detail">
|
||||
<div className="pipeline-video__detail-meta">
|
||||
<span>ID: {video.id.slice(0, 8)}…</span>
|
||||
<span>Created: {formatDate(video.created_at)}</span>
|
||||
<span>Updated: {formatDate(video.updated_at)}</span>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue