Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 0 additions & 65 deletions PanTS-Demo/src/routes/UploadPage.css
Original file line number Diff line number Diff line change
Expand Up @@ -59,15 +59,6 @@
padding: 28px 32px 32px;
position: relative;
}
.upload-card-label {
font-family: 'Space Grotesk', sans-serif;
font-size: 11px;
font-weight: 600;
letter-spacing: 0.12em;
text-transform: uppercase;
color: #8f8f8f;
margin-bottom: 20px;
}

/* ── Drop zone ── */
.dropzone {
Expand Down Expand Up @@ -624,62 +615,6 @@
color: #111;
}

/* ── Advanced options ── */
.advanced-section {
margin-top: 20px;
padding-top: 20px;
border-top: 1px solid rgba(0,0,0,.06);
}
.advanced-toggle {
display: flex;
align-items: center;
gap: 6px;
background: none;
border: none;
color: #6a6a6a;
font-family: 'JetBrains Mono', monospace;
font-size: 10px;
letter-spacing: 0.08em;
text-transform: uppercase;
cursor: pointer;
padding: 4px 0;
transition: color 0.2s;
}
.advanced-toggle:hover {
color: #8f8f8f;
}
.advanced-toggle svg {
transition: transform 0.2s;
}
.advanced-toggle.open svg {
transform: rotate(90deg);
}
.advanced-fields {
display: flex;
flex-direction: column;
gap: 10px;
margin-top: 12px;
}
.advanced-input {
width: 100%;
padding: 10px 14px;
background: rgba(0,0,0,.03);
border: 1px solid rgba(0,0,0,.06);
border-radius: 8px;
color: #111111;
font-family: 'JetBrains Mono', monospace;
font-size: 12px;
outline: none;
transition: all 0.2s;
}
.advanced-input::placeholder {
color: #6a6a6a;
}
.advanced-input:focus {
border-color: rgba(0,0,0,.25);
box-shadow: 0 0 0 2px rgba(0,0,0,.06);
}

/* ── Action bar (check status, download) ── */
.action-bar {
display: flex;
Expand Down
77 changes: 8 additions & 69 deletions PanTS-Demo/src/routes/UploadPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,7 @@
// Which selected item's inline preview is open (null = none). One at a time.
const [previewItemId, setPreviewItemId] = useState<string | null>(null);
const [message, setMessage] = useState<string>("");
const [serverPath, setServerPath] = useState<string>("");
const [sessionId, setSessionId] = useState<string>("");
const [bdmapId, setBdmapId] = useState<string>("");
const [uploadProgress, setUploadProgress] = useState<number>(0);
const [isUploading, setIsUploading] = useState<boolean>(false);
const [inferenceCompleted, setInferenceCompleted] = useState<boolean>(false);
Expand All @@ -88,7 +86,6 @@
const [postDropOpen, setPostDropOpen] = useState(false);
const postDropRef = useRef<HTMLDivElement>(null);
const [postValue, setPostValue] = useState("");
const [showAdvanced, setShowAdvanced] = useState(false);
const [isDragOver, setIsDragOver] = useState(false);
const [recentUploads, setRecentUploads] = useState<RecentUpload[]>(() => loadRecentUploads());
// Sub-state of each Active card: "uploading" | "queued" | "running".
Expand Down Expand Up @@ -131,7 +128,7 @@
return;
}
setSelectedItems(prev => [...prev, ...filteredFiles.map(f => ({ id: crypto.randomUUID(), kind: 'nifti' as const, file: f }))]);
}, []);

Check warning on line 131 in PanTS-Demo/src/routes/UploadPage.tsx

View workflow job for this annotation

GitHub Actions / Frontend (Node 22)

React Hook useCallback has a missing dependency: 'allowedExtensions'. Either include it or remove the dependency array

Check warning on line 131 in PanTS-Demo/src/routes/UploadPage.tsx

View workflow job for this annotation

GitHub Actions / Frontend (Node 20)

React Hook useCallback has a missing dependency: 'allowedExtensions'. Either include it or remove the dependency array

const handleDragOver = useCallback((e: React.DragEvent) => {
e.preventDefault();
Expand Down Expand Up @@ -296,7 +293,7 @@
}
})();
return () => { cancelled = true; stopAllPolling(); };
}, []);

Check warning on line 296 in PanTS-Demo/src/routes/UploadPage.tsx

View workflow job for this annotation

GitHub Actions / Frontend (Node 22)

React Hook useEffect has missing dependencies: 'runUpload' and 'startInferencePolling'. Either include them or remove the dependency array

Check warning on line 296 in PanTS-Demo/src/routes/UploadPage.tsx

View workflow job for this annotation

GitHub Actions / Frontend (Node 20)

React Hook useEffect has missing dependencies: 'runUpload' and 'startInferencePolling'. Either include them or remove the dependency array

// Only warn before an unload if the current upload could NOT be stored in
// IndexedDB (quota/private-mode) - otherwise an interrupted upload resumes
Expand Down Expand Up @@ -535,16 +532,14 @@
return;
}

const path = serverPath.trim();
if (!item && !path) {
alert("Provide a server file path or upload/select a file first.");
if (!item) {
alert("Select a file to upload first.");
return;
}

const model = selectedModel;
const sid = crypto.randomUUID();
const itemName = item ? (item.kind === 'dicom' ? item.label : item.file.name) : undefined;
const label = bdmapId.trim() || (path ? path.split("/").pop() : itemName) || sid;
const label = (item.kind === 'dicom' ? item.label : item.file.name) || sid;

setInferenceCompleted(false);
setRecentUploads(
Expand All @@ -558,47 +553,23 @@
})
);

// Server-path run: nothing to upload, kick off inference directly. A server
// path always wins over a selected file, matching the prior behavior.
if (path) {
try {
setSessionId(sid);
setMessage(`Starting ${model} inference...`);
const fd = new FormData();
fd.append("session_id", sid);
fd.append("model_name", model);
fd.append("INPUT_SERVER_PATH", path);
const res = await fetch(`${API_BASE}/api/run-epai-inference`, { method: "POST", body: fd });
const data = await parseApiResponse(res);
if (!res.ok) throw new Error(data.error || "Failed to start inference");
setMessage(`${model} inference started. Session: ${sid}`);
setPhase(sid, "queued");
startInferencePolling(sid, model);
} catch (err) {
console.error(err);
setRecentUploads(updateRecentUploadStatus(sid, "Failed"));
setMessage("Failed: " + (err as Error).message);
}
return;
}

// Consume the first item so the next can be queued. A DICOM folder uploads its
// slices and converts server-side; a NIfTI file rides the resumable path (stashed
// in IndexedDB so an interrupted upload can resume).
setSelectedItems(prev => prev.slice(1));

if (item!.kind === 'dicom') {
runDicomUpload(sid, item!.files, model);
if (item.kind === 'dicom') {
runDicomUpload(sid, item.files, model);
return;
}

const file = item!.file;
const file = item.file;
const pending: PendingUpload = {
sessionId: sid,
file,
filename: file.name,
model,
bdmapId: bdmapId.trim(),
bdmapId: "",
totalChunks: Math.ceil(file.size / CHUNK_SIZE),
nextChunk: 0,
};
Expand Down Expand Up @@ -729,8 +700,6 @@

<div className="upload-main">
<div className="upload-card">
<div className="upload-card-label">Upload</div>

{/* ── Drop zone ── */}
<div
className={`dropzone${isDragOver ? ' drag-over' : ''}`}
Expand Down Expand Up @@ -882,7 +851,7 @@
</div>
<div className="model-dropdown" ref={modelDropRef}>
<button
className={`model-dropdown-btn${selectedModel ? ' has-value' : ''}${modelDropOpen ? ' open' : ''}`}
className={`model-dropdown-btn${selectedModel && selectedModel !== 'None' ? ' has-value' : ''}${modelDropOpen ? ' open' : ''}`}
onClick={() => setModelDropOpen(o => !o)}
type="button"
>
Expand Down Expand Up @@ -976,36 +945,6 @@
</div>

{/* ── Advanced options ── */}
<div className="advanced-section">
<button
className={`advanced-toggle${showAdvanced ? ' open' : ''}`}
onClick={() => setShowAdvanced(!showAdvanced)}
>
<svg width="8" height="8" viewBox="0 0 8 8" fill="none">
<path d="M2 1l4 3-4 3" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
</svg>
Advanced Options
</button>
{showAdvanced && (
<div className="advanced-fields">
<input
type="text"
className="advanced-input"
placeholder="Server CT path: /path/to/xxx.nii.gz"
value={serverPath}
onChange={(e) => setServerPath(e.target.value)}
/>
<input
type="text"
className="advanced-input"
placeholder="Optional BDMAP ID (e.g. BDMAP_00000338)"
value={bdmapId}
onChange={(e) => setBdmapId(e.target.value)}
/>
</div>
)}
</div>

{/* ── Action bar ── */}
{sessionId && (
<div className="action-bar">
Expand Down
Loading