Enhance validation for DICOM viewer input data
- Added validation checks for nested stacks and stack objects to ensure imageIds are valid arrays of strings. - Implemented console warnings for invalid entries, including nested stacks and stack objects, to aid in debugging. - Improved error handling for unknown or invalid entries in the parsed data.
This commit is contained in:
+37
-32
@@ -2880,53 +2880,58 @@ useEffect(() => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{availableStacks.map((stackObj, idx) => {
|
{availableStacks.map((stackObj, idx) => {
|
||||||
const thisUID = stackObj.studyInstanceUID;
|
const imageIds = (stackObj && Array.isArray((stackObj as any).imageIds)) ? (stackObj as any).imageIds : [];
|
||||||
const sameStudy = referenceUID && thisUID && referenceUID === thisUID;
|
const label = (stackObj && ((stackObj as any).name || (stackObj as any).caseId))
|
||||||
const dotColor = sameStudy ? "#4caf50" : "#888";
|
? ((stackObj as any).name || `Case: ${(stackObj as any).caseId}`)
|
||||||
const isEmpty = stackObj.imageIds.length === 0;
|
: `Stack ${idx + 1}`;
|
||||||
|
const count = imageIds.length;
|
||||||
|
const invalid = !Array.isArray(imageIds) || count === 0;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<button
|
||||||
key={idx}
|
key={idx}
|
||||||
role="option"
|
role="option"
|
||||||
aria-selected={selectedStackIdx[i] === idx}
|
aria-selected={false}
|
||||||
|
title={invalid ? `Invalid stack descriptor` : `${label} — ${count} image${count === 1 ? '' : 's'}${(stackObj as any)?.caseId ? ` — case ${(stackObj as any).caseId}` : ''}`}
|
||||||
style={{
|
style={{
|
||||||
padding: "6px 12px",
|
|
||||||
background: selectedStackIdx[i] === idx ? "#444" : "#222",
|
|
||||||
color: isEmpty ? "#aaa" : "#fff",
|
|
||||||
cursor: isEmpty ? "not-allowed" : "pointer",
|
|
||||||
display: "flex",
|
display: "flex",
|
||||||
alignItems: "center",
|
flexDirection: "column",
|
||||||
fontWeight: selectedStackIdx[i] === idx ? "bold" : "normal",
|
alignItems: "flex-start",
|
||||||
borderBottom: "1px solid #333",
|
width: "100%",
|
||||||
userSelect: "none",
|
padding: "8px 12px",
|
||||||
opacity: isEmpty ? 0.6 : 1,
|
background: "transparent",
|
||||||
|
color: "#fff",
|
||||||
|
border: "none",
|
||||||
|
textAlign: "left",
|
||||||
|
cursor: invalid ? "not-allowed" : "pointer",
|
||||||
}}
|
}}
|
||||||
tabIndex={-1}
|
onClick={(e) => {
|
||||||
onMouseDown={e => {
|
e.stopPropagation();
|
||||||
e.preventDefault();
|
if (invalid) {
|
||||||
if (!isEmpty) {
|
console.warn(`Attempted to select invalid stack at index ${idx}`, stackObj);
|
||||||
handleLoadStack(i, idx);
|
return;
|
||||||
setOpenDropdownIdx(null);
|
|
||||||
}
|
}
|
||||||
|
// Select the stack for this viewport
|
||||||
|
// Use the shared handler so selection behavior is consistent
|
||||||
|
handleLoadStack(i, idx).catch((err) => {
|
||||||
|
console.error("Failed to load stack:", err);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Close the dropdown
|
||||||
|
setOpenDropdownIdx(null);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span style={{
|
<div style={{ fontWeight: "600", fontSize: 13 }}>{label}</div>
|
||||||
color: dotColor,
|
<div style={{ fontSize: 12, opacity: 0.7 }}>{count} image{count === 1 ? '' : 's'}</div>
|
||||||
fontWeight: "bold",
|
</button>
|
||||||
marginRight: 8,
|
|
||||||
fontSize: "16px",
|
|
||||||
verticalAlign: "middle",
|
|
||||||
}}>●</span>
|
|
||||||
Stack {idx + 1} ({stackObj.imageIds.length} images
|
|
||||||
{isEmpty && <span style={{ color: "#f44336", marginLeft: 6 }}>(empty)</span>})
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)
|
||||||
|
}
|
||||||
</div >
|
</div >
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,15 +52,22 @@ export function mountDicomViewers() {
|
|||||||
|
|
||||||
if (Array.isArray(parsed)) {
|
if (Array.isArray(parsed)) {
|
||||||
const out: any[] = [];
|
const out: any[] = [];
|
||||||
parsed.forEach(item => {
|
parsed.forEach((item, itemIdx) => {
|
||||||
// Case object with nested stacks: { caseId, stacks: [ { name, imageIds } ] }
|
// Case object with nested stacks: { caseId, stacks: [ { name, imageIds } ] }
|
||||||
if (item && typeof item === 'object' && Array.isArray(item.stacks)) {
|
if (item && typeof item === "object" && Array.isArray(item.stacks)) {
|
||||||
const caseId = item.caseId || item.caseUID || item.case || undefined;
|
const caseId = item.caseId || item.caseUID || item.case || undefined;
|
||||||
item.stacks.forEach((s: any) => {
|
item.stacks.forEach((s: any, sIdx: number) => {
|
||||||
let imageIds: string[] = [];
|
let imageIds: string[] = [];
|
||||||
if (Array.isArray(s)) imageIds = s;
|
if (Array.isArray(s)) imageIds = s;
|
||||||
else if (Array.isArray(s.imageIds)) imageIds = s.imageIds;
|
else if (Array.isArray(s.imageIds)) imageIds = s.imageIds;
|
||||||
else if (Array.isArray(s.i)) imageIds = s.i;
|
else if (Array.isArray(s.i)) imageIds = s.i;
|
||||||
|
|
||||||
|
// Validation
|
||||||
|
if (!Array.isArray(imageIds) || imageIds.length === 0 || !imageIds.every(x => typeof x === "string")) {
|
||||||
|
console.warn(`data-named-stacks: skipping invalid nested stack at parsed[${itemIdx}].stacks[${sIdx}]`, s);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
out.push({
|
out.push({
|
||||||
imageIds: toWadouri(imageIds),
|
imageIds: toWadouri(imageIds),
|
||||||
name: s.name || s.label || undefined,
|
name: s.name || s.label || undefined,
|
||||||
@@ -73,6 +80,12 @@ export function mountDicomViewers() {
|
|||||||
// Stack object: { imageIds, i, name, caseId }
|
// Stack object: { imageIds, i, name, caseId }
|
||||||
if (item && typeof item === 'object' && (Array.isArray(item.imageIds) || Array.isArray(item.i))) {
|
if (item && typeof item === 'object' && (Array.isArray(item.imageIds) || Array.isArray(item.i))) {
|
||||||
const imageIds = Array.isArray(item.imageIds) ? item.imageIds : item.i;
|
const imageIds = Array.isArray(item.imageIds) ? item.imageIds : item.i;
|
||||||
|
|
||||||
|
if (!Array.isArray(imageIds) || imageIds.length === 0 || !imageIds.every(x => typeof x === "string")) {
|
||||||
|
console.warn(`data-named-stacks: skipping invalid stack object at parsed[${itemIdx}]`, item);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
out.push({
|
out.push({
|
||||||
imageIds: toWadouri(imageIds),
|
imageIds: toWadouri(imageIds),
|
||||||
name: item.name || item.label || undefined,
|
name: item.name || item.label || undefined,
|
||||||
@@ -86,6 +99,9 @@ export function mountDicomViewers() {
|
|||||||
out.push(toDescriptor(item));
|
out.push(toDescriptor(item));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Unknown / invalid entry
|
||||||
|
console.warn(`data-named-stacks: unknown or invalid entry at parsed[${itemIdx}] — skipping`, item);
|
||||||
});
|
});
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user