Skip to content

Commit 8ba366a

Browse files
Merge pull request #45 from NeuroJSON/dev_hari
Fix #37 Small refinements to the dataset level page
2 parents f78be49 + e357d5c commit 8ba366a

File tree

1 file changed

+115
-32
lines changed

1 file changed

+115
-32
lines changed

src/pages/DatasetDetailPage.tsx

Lines changed: 115 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,31 @@ interface InternalDataLink {
4040
arraySize?: number[];
4141
}
4242

43+
const transformJsonForDisplay = (obj: any): any => {
44+
if (typeof obj !== "object" || obj === null) return obj;
45+
46+
const transformed: any = Array.isArray(obj) ? [] : {};
47+
48+
for (const key in obj) {
49+
if (!Object.prototype.hasOwnProperty.call(obj, key)) continue;
50+
51+
const value = obj[key];
52+
53+
// Match README, CHANGES, or file extensions
54+
const isLongTextKey = /^(README|CHANGES)$|\.md$|\.txt$|\.m$/i.test(key);
55+
56+
if (typeof value === "string" && isLongTextKey) {
57+
transformed[key] = `<code class="puretext">${value}</code>`;
58+
} else if (typeof value === "object") {
59+
transformed[key] = transformJsonForDisplay(value);
60+
} else {
61+
transformed[key] = value;
62+
}
63+
}
64+
65+
return transformed;
66+
};
67+
4368
const DatasetDetailPage: React.FC = () => {
4469
const { dbName, docId } = useParams<{ dbName: string; docId: string }>();
4570
const navigate = useNavigate();
@@ -63,41 +88,43 @@ const DatasetDetailPage: React.FC = () => {
6388
const [expandedPaths, setExpandedPaths] = useState<string[]>([]);
6489
const [originalTextMap, setOriginalTextMap] = useState<Map<HTMLElement, string>>(new Map());
6590
const [jsonViewerKey, setJsonViewerKey] = useState(0);
66-
91+
const [jsonSize, setJsonSize] = useState<number>(0);
92+
const [transformedDataset, setTransformedDataset] = useState<any>(null);
6793

6894

6995
// Recursive function to find `_DataLink_`
7096
const extractDataLinks = (obj: any, path: string): ExternalDataLink[] => {
7197
const links: ExternalDataLink[] = [];
72-
73-
if (typeof obj === "object" && obj !== null) {
74-
for (const key in obj) {
75-
if (obj.hasOwnProperty(key)) {
76-
if (key === "_DataLink_" && typeof obj[key] === "string") {
77-
let correctedUrl = obj[key].replace(/:\$.*$/, "");
78-
79-
const sizeMatch = obj[key].match(/size=(\d+)/);
98+
99+
const traverse = (node: any, currentPath: string) => {
100+
if (typeof node === "object" && node !== null) {
101+
for (const key in node) {
102+
if (key === "_DataLink_" && typeof node[key] === "string") {
103+
let correctedUrl = node[key].replace(/:\$.*$/, "");
104+
105+
const sizeMatch = node[key].match(/size=(\d+)/);
80106
const size = sizeMatch
81107
? `${(parseInt(sizeMatch[1], 10) / 1024 / 1024).toFixed(2)} MB`
82108
: "Unknown Size";
83-
84-
const subMatch = path.match(/sub-\d+/);
109+
110+
const subMatch = currentPath.match(/sub-\d+/);
85111
const subPath = subMatch ? subMatch[0] : "Unknown Sub";
86-
112+
87113
links.push({
88-
name: `${path.split("/").pop() || "ExternalData"} (${size}) [/${subPath}]`,
114+
name: `${currentPath.split("/").pop() || "ExternalData"} (${size}) [/${subPath}]`,
89115
size,
90-
path: subPath,
116+
path: currentPath, // keep full JSON path for file placement
91117
url: correctedUrl,
92118
index: links.length,
93119
});
94-
} else if (typeof obj[key] === "object") {
95-
links.push(...extractDataLinks(obj[key], `${path}/${key}`));
120+
} else if (typeof node[key] === "object") {
121+
traverse(node[key], `${currentPath}/${key}`);
96122
}
97123
}
98124
}
99-
}
100-
125+
};
126+
127+
traverse(obj, path);
101128
return links;
102129
};
103130

@@ -185,9 +212,24 @@ const DatasetDetailPage: React.FC = () => {
185212

186213
setExternalLinks(links);
187214
setInternalLinks(internalData);
215+
const transformed = transformJsonForDisplay(datasetDocument);
216+
setTransformedDataset(transformed);
217+
218+
const blob = new Blob([JSON.stringify(datasetDocument, null, 2)], { type: "application/json" });
219+
setJsonSize(blob.size);
220+
221+
// // ✅ Construct download script dynamically
222+
let script = `curl -L --create-dirs "https://neurojson.io:7777/${dbName}/${docId}" -o "${docId}.json"\n`;
188223

189-
// ✅ Construct download script dynamically
190-
const script = `curl -L --create-dirs "https://neurojson.io:7777/${dbName}/${docId}" -o "${docId}.json"`;
224+
225+
externalLinks.forEach((link) => {
226+
const url = link.url;
227+
const match = url.match(/file=([^&]+)/);
228+
const filename = match ? decodeURIComponent(match[1]) : `file-${link.index}`;
229+
const outputPath = `$HOME/.neurojson/io/${dbName}/${docId}/${filename}`;
230+
231+
script += `curl -L --create-dirs "${url}" -o "${outputPath}"\n`;
232+
});
191233
setDownloadScript(script);
192234
}
193235
}, [datasetDocument]);
@@ -208,7 +250,20 @@ const DatasetDetailPage: React.FC = () => {
208250
element.classList.remove("highlighted");
209251
});
210252
};
211-
}, [searchTerm, datasetDocument]);
253+
}, [searchTerm, datasetDocument]);
254+
255+
useEffect(() => {
256+
if (!transformedDataset) return;
257+
258+
const spans = document.querySelectorAll(".string-value");
259+
260+
spans.forEach((el) => {
261+
if (el.textContent?.includes("<code class=\"puretext\">")) {
262+
// Inject as HTML so it renders code block correctly
263+
el.innerHTML = el.textContent ?? "";
264+
}
265+
});
266+
}, [transformedDataset]);
212267

213268
const handleDownloadDataset = () => {
214269
if (!datasetDocument) return;
@@ -402,6 +457,32 @@ const DatasetDetailPage: React.FC = () => {
402457
}
403458

404459
return (
460+
<>
461+
{/* 🔧 Inline CSS for string formatting */}
462+
<style>
463+
{`
464+
code.puretext {
465+
white-space: pre-wrap;
466+
display: -webkit-box;
467+
-webkit-box-orient: vertical;
468+
-webkit-line-clamp: 4;
469+
overflow: hidden;
470+
text-overflow: ellipsis;
471+
font-family: monospace;
472+
color: #d14;
473+
font-size: 14px;
474+
background-color: transparent;
475+
cursor: pointer;
476+
transition: all 0.2s ease;
477+
}
478+
479+
code.puretext:hover, code.puretext:focus {
480+
-webkit-line-clamp: unset;
481+
overflow: visible;
482+
background-color: #f0f0f0;
483+
}`}
484+
485+
</style>
405486
<Box sx={{ padding: 4 }}>
406487
<Button
407488
variant="contained"
@@ -413,12 +494,12 @@ const DatasetDetailPage: React.FC = () => {
413494

414495
<Box
415496
sx={{
416-
position: "sticky", // ✅ Keeps title & search bar fixed
417-
top: 0, // ✅ Sticks to the top
418-
backgroundColor: "white", // ✅ Ensures smooth UI
419-
zIndex: 10, // ✅ Keeps it above scrollable content
420-
paddingBottom: 2, // ✅ Adds space for clarity
421-
borderBottom: `1px solid ${Colors.lightGray}`, // ✅ Adds subtle separator
497+
position: "sticky",
498+
top: 0,
499+
backgroundColor: "white",
500+
zIndex: 10,
501+
paddingBottom: 2,
502+
borderBottom: `1px solid ${Colors.lightGray}`,
422503
}}>
423504

424505
{/* ✅ Dataset Title (From dataset_description.json) */}
@@ -462,7 +543,7 @@ const DatasetDetailPage: React.FC = () => {
462543

463544
{/* Database Name (Clickable) */}
464545
<Button
465-
onClick={() => navigate(`/RoutesEnum.DATABASES/${dbName}`)}
546+
onClick={() => navigate(`/databases/${dbName}`)}
466547
sx={{
467548
textTransform: "none",
468549
fontSize: "1.2rem",
@@ -505,7 +586,8 @@ const DatasetDetailPage: React.FC = () => {
505586
"&:hover": { backgroundColor: "#ff9100" },
506587
}}
507588
>
508-
Download Dataset (1 Mb)
589+
{/* Download Dataset (1 Mb) */}
590+
Download Dataset ({(jsonSize / 1024).toFixed(0)} MB)
509591
</Button>
510592

511593
<Button
@@ -518,7 +600,7 @@ const DatasetDetailPage: React.FC = () => {
518600
"&:hover": { backgroundColor: "#ff9100" },
519601
}}
520602
>
521-
Script to Download All Files (138 Bytes) (links: 0)
603+
Script to Download All Files ({downloadScript.length} Bytes) (links: {externalLinks.length})
522604
</Button>
523605

524606
<Box display="flex" alignItems="center" gap={1} sx={{ ml: "auto" }}>
@@ -542,7 +624,7 @@ const DatasetDetailPage: React.FC = () => {
542624
{/* ✅ JSON Viewer (left panel) */}
543625
<Box sx={{ flex: 3, backgroundColor: "#f5f5f5", padding: 2, borderRadius: "8px", overflowX: "auto" }}>
544626
<ReactJson
545-
src={datasetDocument}
627+
src={transformedDataset || datasetDocument}
546628
name={false}
547629
enableClipboard={true}
548630
displayDataTypes={false}
@@ -761,7 +843,8 @@ const DatasetDetailPage: React.FC = () => {
761843
isInternal={previewIsInternal}
762844
onClose={handleClosePreview}
763845
/>
764-
</Box>
846+
</Box>
847+
</>
765848
)};
766849

767850
export default DatasetDetailPage;

0 commit comments

Comments
 (0)