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:
jlightner 2026-03-30 19:36:47 +00:00
parent 44c0df6e08
commit 9dfa568bb3
5 changed files with 148 additions and 22 deletions

View file

@ -15,7 +15,7 @@ Steps:
- Estimate: 45m
- Files: frontend/src/api/public-client.ts, frontend/src/pages/AdminPipeline.tsx, frontend/src/App.css
- Verify: ssh ub01 "cd /vmPool/r/repos/xpltdco/chrysopedia && docker compose build chrysopedia-web" succeeds with exit 0
- [ ] **T02: Prune dead UI, rename view toggle, add debug indicator on trigger, add review queue cross-link** — Clean up the pipeline page: remove low-value UI elements, rename confusing labels, add debug mode context to trigger button, and add cross-navigation to review queue.
- [x] **T02: Pruned dead UI, renamed view toggle to Oldest/Newest first, added debug indicator on trigger button, added review queue cross-link on video cards** — Clean up the pipeline page: remove low-value UI elements, rename confusing labels, add debug mode context to trigger button, and add cross-navigation to review queue.
Steps:
1. In EventLog component, rename 'Head'/'Tail' buttons to 'Oldest first'/'Newest first'. The buttons are in the `.pipeline-events__view-toggle` div. Just change the button text.

View file

@ -0,0 +1,24 @@
{
"schemaVersion": 1,
"taskId": "T01",
"unitId": "M007/S04/T01",
"timestamp": 1774899251289,
"passed": false,
"discoverySource": "task-plan",
"checks": [
{
"command": "ssh ub01 \"cd /vmPool/r/repos/xpltdco/chrysopedia",
"exitCode": 2,
"durationMs": 5,
"verdict": "fail"
},
{
"command": "docker compose build chrysopedia-web\" succeeds with exit 0",
"exitCode": 2,
"durationMs": 5,
"verdict": "fail"
}
],
"retryAttempt": 1,
"maxRetries": 2
}

View file

@ -0,0 +1,76 @@
---
id: T02
parent: S04
milestone: M007
provides: []
requires: []
affects: []
key_files: ["frontend/src/pages/AdminPipeline.tsx", "frontend/src/App.css"]
key_decisions: ["Lifted debug mode state from DebugModeToggle to AdminPipeline parent so trigger button can show debug indicator"]
patterns_established: []
drill_down_paths: []
observability_surfaces: []
duration: ""
verification_result: "Docker build of chrysopedia-web succeeded with exit 0. TypeScript compiled cleanly, Vite bundled 48 modules, nginx image produced."
completed_at: 2026-03-30T19:36:31.974Z
blocker_discovered: false
---
# T02: Pruned dead UI, renamed view toggle to Oldest/Newest first, added debug indicator on trigger button, added review queue cross-link on video cards
> Pruned dead UI, renamed view toggle to Oldest/Newest first, added debug indicator on trigger button, added review queue cross-link on video cards
## What Happened
---
id: T02
parent: S04
milestone: M007
key_files:
- frontend/src/pages/AdminPipeline.tsx
- frontend/src/App.css
key_decisions:
- Lifted debug mode state from DebugModeToggle to AdminPipeline parent so trigger button can show debug indicator
duration: ""
verification_result: passed
completed_at: 2026-03-30T19:36:31.974Z
blocker_discovered: false
---
# T02: Pruned dead UI, renamed view toggle to Oldest/Newest first, added debug indicator on trigger button, added review queue cross-link on video cards
**Pruned dead UI, renamed view toggle to Oldest/Newest first, added debug indicator on trigger button, added review queue cross-link on video cards**
## What Happened
Six changes to AdminPipeline.tsx: renamed Head/Tail view toggle to Oldest first/Newest first; removed duplicate event-level refresh button; removed truncated ID from expanded detail; lifted debugMode state to parent component so trigger button shows '▶ Trigger (debug)' when active; added '→ Moments' cross-link on each video card linking to /admin/review with accent-colored subtle styling.
## Verification
Docker build of chrysopedia-web succeeded with exit 0. TypeScript compiled cleanly, Vite bundled 48 modules, nginx image produced.
## Verification Evidence
| # | Command | Exit Code | Verdict | Duration |
|---|---------|-----------|---------|----------|
| 1 | `ssh ub01 "cd /vmPool/r/repos/xpltdco/chrysopedia && docker compose build chrysopedia-web"` | 0 | ✅ pass | 5500ms |
## Deviations
None.
## Known Issues
Review queue cross-link goes to /admin/review without video-specific filtering since the review queue doesn't support URL-based video filtering yet.
## Files Created/Modified
- `frontend/src/pages/AdminPipeline.tsx`
- `frontend/src/App.css`
## Deviations
None.
## Known Issues
Review queue cross-link goes to /admin/review without video-specific filtering since the review queue doesn't support URL-based video filtering yet.

View file

@ -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;

View file

@ -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>