feat: deep-link Inspect Pipeline button — auto-expand and scroll to video
TechniquePage's 'Inspect pipeline' button passes ?video=<id> to AdminPipeline. Previously the query param was ignored. Now AdminPipeline reads it on mount, auto-expands the matching video row, and smooth-scrolls it into view.
This commit is contained in:
parent
4b0914b12b
commit
4151e7cd25
1 changed files with 22 additions and 2 deletions
|
|
@ -3,7 +3,8 @@
|
||||||
* expandable event log with token usage and collapsible JSON viewer.
|
* expandable event log with token usage and collapsible JSON viewer.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { useCallback, useEffect, useState } from "react";
|
import { useCallback, useEffect, useRef, useState } from "react";
|
||||||
|
import { useSearchParams } from "react-router-dom";
|
||||||
import {
|
import {
|
||||||
fetchPipelineVideos,
|
fetchPipelineVideos,
|
||||||
fetchPipelineEvents,
|
fetchPipelineEvents,
|
||||||
|
|
@ -448,6 +449,7 @@ function StatusFilter({
|
||||||
// ── Main Page ────────────────────────────────────────────────────────────────
|
// ── Main Page ────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
export default function AdminPipeline() {
|
export default function AdminPipeline() {
|
||||||
|
const [searchParams] = useSearchParams();
|
||||||
const [videos, setVideos] = useState<PipelineVideoItem[]>([]);
|
const [videos, setVideos] = useState<PipelineVideoItem[]>([]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
|
@ -456,6 +458,8 @@ export default function AdminPipeline() {
|
||||||
const [actionMessage, setActionMessage] = useState<{ id: string; text: string; ok: boolean } | null>(null);
|
const [actionMessage, setActionMessage] = useState<{ id: string; text: string; ok: boolean } | null>(null);
|
||||||
const [activeFilter, setActiveFilter] = useState<string | null>(null);
|
const [activeFilter, setActiveFilter] = useState<string | null>(null);
|
||||||
const [debugMode, setDebugModeState] = useState<boolean | null>(null);
|
const [debugMode, setDebugModeState] = useState<boolean | null>(null);
|
||||||
|
const videoRefs = useRef<Map<string, HTMLDivElement>>(new Map());
|
||||||
|
const deepLinked = useRef(false);
|
||||||
|
|
||||||
const load = useCallback(async () => {
|
const load = useCallback(async () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|
@ -474,6 +478,22 @@ export default function AdminPipeline() {
|
||||||
void load();
|
void load();
|
||||||
}, [load]);
|
}, [load]);
|
||||||
|
|
||||||
|
// Deep-link: auto-expand and scroll to ?video=<id> on first load
|
||||||
|
useEffect(() => {
|
||||||
|
if (deepLinked.current || loading || videos.length === 0) return;
|
||||||
|
const targetVideoId = searchParams.get("video");
|
||||||
|
if (!targetVideoId) return;
|
||||||
|
const match = videos.find((v) => v.id === targetVideoId);
|
||||||
|
if (!match) return;
|
||||||
|
deepLinked.current = true;
|
||||||
|
setExpandedId(targetVideoId);
|
||||||
|
// Scroll after the expanded detail renders
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
const el = videoRefs.current.get(targetVideoId);
|
||||||
|
if (el) el.scrollIntoView({ behavior: "smooth", block: "start" });
|
||||||
|
});
|
||||||
|
}, [loading, videos, searchParams]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let cancelled = false;
|
let cancelled = false;
|
||||||
fetchDebugMode()
|
fetchDebugMode()
|
||||||
|
|
@ -568,7 +588,7 @@ export default function AdminPipeline() {
|
||||||
{videos
|
{videos
|
||||||
.filter((v) => activeFilter === null || v.processing_status === activeFilter)
|
.filter((v) => activeFilter === null || v.processing_status === activeFilter)
|
||||||
.map((video) => (
|
.map((video) => (
|
||||||
<div key={video.id} className="pipeline-video">
|
<div key={video.id} className="pipeline-video" ref={(el) => { if (el) videoRefs.current.set(video.id, el); }}>
|
||||||
<div
|
<div
|
||||||
className="pipeline-video__header"
|
className="pipeline-video__header"
|
||||||
onClick={() => toggleExpand(video.id)}
|
onClick={() => toggleExpand(video.id)}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue