Skip to content

Commit ff5af0b

Browse files
authored
Merge pull request #57 from NeuroJSON/dev-fan
refactor: adjust layout and styles on Dataset Detail page
2 parents 3dc8cf8 + 724a823 commit ff5af0b

File tree

3 files changed

+123
-71
lines changed

3 files changed

+123
-71
lines changed

src/components/DatasetDetailPage/LoadDatasetTabs.tsx

Lines changed: 58 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { Tabs, Tab, Box, Typography } from "@mui/material";
1+
import ContentCopyIcon from "@mui/icons-material/ContentCopy";
2+
import { Tabs, Tab, Box, Typography, IconButton, Tooltip } from "@mui/material";
23
import { Colors } from "design/theme";
34
import React from "react";
45
import { useState } from "react";
@@ -57,7 +58,9 @@ const LoadDatasetTabs: React.FC<LoadDatasetTabsProps> = ({
5758
const handleTabChange = (event: React.SyntheticEvent, newValue: number) => {
5859
setTabIndex(newValue);
5960
};
61+
console.log("datasetDocument", datasetDocument);
6062
const datasetDesc = datasetDocument?.["dataset_description.json"];
63+
console.log("datasetDesc", datasetDesc);
6164
const datasetName = datasetDesc?.Name?.includes(" - ")
6265
? datasetDesc.Name.split(" - ")[1]
6366
: datasetDesc?.Name || datasetDocument?._id || docname;
@@ -81,6 +84,26 @@ const LoadDatasetTabs: React.FC<LoadDatasetTabsProps> = ({
8184
);
8285
};
8386

87+
const CopyableCodeBlock = ({ code }: { code: string }) => {
88+
const handleCopy = () => {
89+
navigator.clipboard.writeText(code);
90+
};
91+
return (
92+
<Box sx={{ position: "relative" }}>
93+
<IconButton
94+
onClick={handleCopy}
95+
size="small"
96+
sx={{ position: "absolute", top: 5, right: 5 }}
97+
>
98+
<Tooltip title="Copy to clipboard">
99+
<ContentCopyIcon fontSize="small" />
100+
</Tooltip>
101+
</IconButton>
102+
<code style={flashcardStyles.codeBlock}>{code}</code>
103+
</Box>
104+
);
105+
};
106+
84107
return (
85108
<>
86109
<Tabs
@@ -118,20 +141,18 @@ const LoadDatasetTabs: React.FC<LoadDatasetTabsProps> = ({
118141
Load by URL with REST-API in Python
119142
</Typography>
120143
<Typography>Install:</Typography>
121-
<code style={flashcardStyles.codeBlock}>
122-
pip install jdata bjdata numpy
123-
</code>
144+
<CopyableCodeBlock code={`pip install jdata bjdata numpy`} />
124145
<Typography>Load from URL:</Typography>
125-
<code style={flashcardStyles.codeBlock}>
126-
{`import jdata as jd
146+
<CopyableCodeBlock
147+
code={`import jdata as jd
127148
data = jd.loadurl('${datasetUrl}')
128149
129150
# List all externally linked files
130151
links = jd.jsonpath(data, '$.._DataLink_')
131152
132153
# Download & cache anatomical nii.gz data for sub-01/sub-02
133154
jd.jdlink(links, {'regex': 'anat/sub-0[12]_.*\\.nii'})`}
134-
</code>
155+
/>
135156
</Box>
136157
</TabPanel>
137158

@@ -142,12 +163,10 @@ jd.jdlink(links, {'regex': 'anat/sub-0[12]_.*\\.nii'})`}
142163
Load by URL with REST-API in MATLAB
143164
</Typography>
144165
<Typography>Install:</Typography>
145-
<code style={flashcardStyles.codeBlock}>
146-
Download and addpath to JSONLab
147-
</code>
166+
<CopyableCodeBlock code={`Download and addpath to JSONLab`} />
148167
<Typography>Load from URL:</Typography>
149-
<code style={flashcardStyles.codeBlock}>
150-
{`data = loadjson('${datasetUrl}');
168+
<CopyableCodeBlock
169+
code={`data = loadjson('${datasetUrl}');
151170
152171
% or without JSONLab (webread cannot decode JData annotations)
153172
data = webread('${datasetUrl}');
@@ -157,7 +176,7 @@ links = jsonpath(data, '$.._DataLink_');
157176
158177
% Download & cache anatomical nii.gz data for sub-01/sub-02
159178
niidata = jdlink(links, 'regex', 'anat/sub-0[12]_.*\\.nii');`}
160-
</code>
179+
/>
161180
</Box>
162181
</TabPanel>
163182

@@ -168,13 +187,9 @@ niidata = jdlink(links, 'regex', 'anat/sub-0[12]_.*\\.nii');`}
168187
Use in MATLAB/Octave
169188
</Typography>
170189
<Typography>Load:</Typography>
171-
<code
172-
style={flashcardStyles.codeBlock}
173-
>{`data = loadjd('${docname}.json');`}</code>
190+
<CopyableCodeBlock code={`data = loadjd('${docname}.json');`} />
174191
<Typography>Read value:</Typography>
175-
<code
176-
style={flashcardStyles.codeBlock}
177-
>{`data.(encodevarname('${onekey}'))`}</code>
192+
<CopyableCodeBlock code={`data.(encodevarname('${onekey}'))`} />
178193
</Box>
179194
</TabPanel>
180195

@@ -185,12 +200,12 @@ niidata = jdlink(links, 'regex', 'anat/sub-0[12]_.*\\.nii');`}
185200
Use in Python
186201
</Typography>
187202
<Typography>Load:</Typography>
188-
<code style={flashcardStyles.codeBlock}>
189-
{`import jdata as jd
203+
<CopyableCodeBlock
204+
code={`import jdata as jd
190205
data = jd.load('${docname}.json')`}
191-
</code>
206+
/>
192207
<Typography>Read value:</Typography>
193-
<code style={flashcardStyles.codeBlock}>{`data["${onekey}"]`}</code>
208+
<CopyableCodeBlock code={`data["${onekey}"]`} />
194209
</Box>
195210
</TabPanel>
196211

@@ -201,21 +216,17 @@ data = jd.load('${docname}.json')`}
201216
Use in C++
202217
</Typography>
203218
<Typography>Install:</Typography>
204-
<code style={flashcardStyles.codeBlock}>
205-
Download JSON for Modern C++ json.hpp
206-
</code>
219+
<CopyableCodeBlock code={`Download JSON for Modern C++ json.hpp`} />
207220
<Typography>Load:</Typography>
208-
<code style={flashcardStyles.codeBlock}>
209-
{`#include "json.hpp"
210-
using json=nlohmann::ordered_json;
211-
212-
std::ifstream datafile("${docname}.json");
213-
json data(datafile);`}
214-
</code>
221+
<CopyableCodeBlock
222+
code={`#include "json.hpp"
223+
using json=nlohmann::ordered_json;
224+
225+
std::ifstream datafile("${docname}.json");
226+
json data(datafile);`}
227+
/>
215228
<Typography>Read value:</Typography>
216-
<code
217-
style={flashcardStyles.codeBlock}
218-
>{`std::cout << data["${onekey}"];`}</code>
229+
<CopyableCodeBlock code={`std::cout << data["${onekey}"];`} />
219230
</Box>
220231
</TabPanel>
221232

@@ -226,24 +237,20 @@ data = jd.load('${docname}.json')`}
226237
Use in JS/Node.js
227238
</Typography>
228239
<Typography>Install:</Typography>
229-
<code style={flashcardStyles.codeBlock}>
230-
npm install jda numjs pako atob
231-
</code>
240+
<CopyableCodeBlock code={`npm install jda numjs pako atob`} />
232241
<Typography>Load:</Typography>
233-
<code style={flashcardStyles.codeBlock}>
234-
{`const fs = require("fs");
235-
const jd = require("jda");
236-
global.atob = require("atob");
242+
<CopyableCodeBlock
243+
code={`const fs = require("fs");
244+
const jd = require("jda");
245+
global.atob = require("atob");
237246
238-
const fn = "${docname}.json";
239-
var jstr = fs.readFileSync(fn).toString().replace(/\\n/g, "");
240-
var data = new jd(JSON.parse(jstr));
241-
data = data.decode();`}
242-
</code>
247+
const fn = "${docname}.json";
248+
var jstr = fs.readFileSync(fn).toString().replace(/\\n/g, "");
249+
var data = new jd(JSON.parse(jstr));
250+
data = data.decode();`}
251+
/>
243252
<Typography>Read value:</Typography>
244-
<code
245-
style={flashcardStyles.codeBlock}
246-
>{`console.log(data.data["${onekey}"]);`}</code>
253+
<CopyableCodeBlock code={`console.log(data.data["${onekey}"]);`} />
247254
</Box>
248255
</TabPanel>
249256
</>

src/components/DatasetPageCard.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,10 @@ const DatasetPageCard: React.FC<DatasetPageCardProps> = ({
9898
<Chip
9999
label={`${doc.value.subj.length} subjects`}
100100
size="small"
101-
sx={{ backgroundColor: Colors.purple, color: Colors.white }}
101+
sx={{
102+
backgroundColor: Colors.darkOrange,
103+
color: Colors.white,
104+
}}
102105
/>
103106
)}
104107
{doc.value.modality &&
@@ -108,7 +111,7 @@ const DatasetPageCard: React.FC<DatasetPageCardProps> = ({
108111
label={mod}
109112
size="small"
110113
sx={{
111-
backgroundColor: Colors.purpleGrey,
114+
backgroundColor: Colors.purple,
112115
color: Colors.white,
113116
}}
114117
/>

src/pages/DatasetDetailPage.tsx

Lines changed: 60 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -242,9 +242,19 @@ const DatasetDetailPage: React.FC = () => {
242242
externalLinks.forEach((link) => {
243243
const url = link.url;
244244
const match = url.match(/file=([^&]+)/);
245+
// const filename = match
246+
// ? decodeURIComponent(match[1])
247+
// : `file-${link.index}`;
245248
const filename = match
246-
? decodeURIComponent(match[1])
249+
? (() => {
250+
try {
251+
return decodeURIComponent(match[1]);
252+
} catch {
253+
return match[1]; // fallback if decode fails
254+
}
255+
})()
247256
: `file-${link.index}`;
257+
248258
const outputPath = `$HOME/.neurojson/io/${dbName}/${docId}/${filename}`;
249259

250260
script += `curl -L --create-dirs "${url}" -o "${outputPath}"\n`;
@@ -519,9 +529,17 @@ const DatasetDetailPage: React.FC = () => {
519529
</style>
520530
<Box sx={{ padding: 4 }}>
521531
<Button
522-
variant="contained"
532+
variant="text"
523533
onClick={() => navigate(-1)}
524-
sx={{ marginBottom: 2, backgroundColor: Colors.primary.main }}
534+
sx={{
535+
marginBottom: 2,
536+
color: Colors.white,
537+
"&:hover": {
538+
transform: "scale(1.05)",
539+
backgroundColor: "transparent",
540+
textDecoration: "underline",
541+
},
542+
}}
525543
>
526544
Back
527545
</Button>
@@ -540,7 +558,7 @@ const DatasetDetailPage: React.FC = () => {
540558
{/* ✅ Dataset Title (From dataset_description.json) */}
541559
<Typography
542560
variant="h4"
543-
color={Colors.primary.main}
561+
color={Colors.darkPurple}
544562
sx={{ fontWeight: "bold", mb: 1 }}
545563
>
546564
{datasetDocument?.["dataset_description.json"]?.Name ??
@@ -579,7 +597,15 @@ const DatasetDetailPage: React.FC = () => {
579597
"&:hover": { backgroundColor: "transparent" },
580598
}}
581599
>
582-
<HomeIcon sx={{ color: Colors.primary.main }} />
600+
<HomeIcon
601+
sx={{
602+
color: Colors.darkPurple,
603+
"&:hover": {
604+
transform: "scale(1.1)",
605+
backgroundColor: "transparent",
606+
},
607+
}}
608+
/>
583609
</Button>
584610

585611
<Typography variant="h5" sx={{ marginX: 1, fontWeight: "bold" }}>
@@ -593,7 +619,11 @@ const DatasetDetailPage: React.FC = () => {
593619
textTransform: "none",
594620
fontSize: "1.2rem",
595621
fontWeight: "bold",
596-
color: Colors.primary.dark,
622+
color: Colors.darkPurple,
623+
"&:hover": {
624+
transform: "scale(1.05)",
625+
backgroundColor: "transparent",
626+
},
597627
}}
598628
>
599629
{dbName?.toLowerCase()}
@@ -608,7 +638,7 @@ const DatasetDetailPage: React.FC = () => {
608638
variant="h5"
609639
sx={{
610640
fontWeight: "bold",
611-
color: Colors.textPrimary,
641+
color: Colors.darkPurple,
612642
fontSize: "1.2rem",
613643
}}
614644
>
@@ -633,9 +663,9 @@ const DatasetDetailPage: React.FC = () => {
633663
startIcon={<CloudDownloadIcon />}
634664
onClick={handleDownloadDataset}
635665
sx={{
636-
backgroundColor: "#ffb300",
637-
color: "black",
638-
"&:hover": { backgroundColor: "#ff9100" },
666+
backgroundColor: Colors.purple,
667+
color: Colors.lightGray,
668+
"&:hover": { backgroundColor: Colors.secondaryPurple },
639669
}}
640670
>
641671
{/* Download Dataset (1 Mb) */}
@@ -647,9 +677,9 @@ const DatasetDetailPage: React.FC = () => {
647677
startIcon={<DescriptionIcon />}
648678
onClick={handleDownloadScript}
649679
sx={{
650-
backgroundColor: "#ffb300",
651-
color: "black",
652-
"&:hover": { backgroundColor: "#ff9100" },
680+
backgroundColor: Colors.purple,
681+
color: Colors.lightGray,
682+
"&:hover": { backgroundColor: Colors.secondaryPurple },
653683
}}
654684
>
655685
Script to Download All Files ({downloadScript.length} Bytes)
@@ -682,6 +712,8 @@ const DatasetDetailPage: React.FC = () => {
682712
gap: 2,
683713
alignItems: "flex-start",
684714
marginTop: 2,
715+
height: "960px", // fixed height container
716+
// border: "2px solid red",
685717
}}
686718
>
687719
{/* ✅ JSON Viewer (left panel) */}
@@ -692,6 +724,8 @@ const DatasetDetailPage: React.FC = () => {
692724
padding: 2,
693725
borderRadius: "8px",
694726
overflowX: "auto",
727+
height: "100%",
728+
// border: "2px solid yellow",
695729
}}
696730
>
697731
<ReactJson
@@ -713,14 +747,19 @@ const DatasetDetailPage: React.FC = () => {
713747
display: "flex",
714748
flexDirection: "column",
715749
gap: 2,
750+
height: "100%",
751+
// border: "2px solid green",
716752
}}
717753
>
718754
<Box
719755
sx={{
720-
backgroundColor: "#cdddf6",
756+
backgroundColor: Colors.lightBlue,
721757
padding: 2,
722758
borderRadius: "8px",
723-
marginTop: 4,
759+
// marginTop: 4,
760+
// border: "2px solid orange",
761+
flex: 1,
762+
// overflowY: "auto",
724763
}}
725764
>
726765
{/* ✅ Collapsible header */}
@@ -745,7 +784,7 @@ const DatasetDetailPage: React.FC = () => {
745784
sx={{
746785
maxHeight: "400px",
747786
overflowY: "auto",
748-
marginTop: 2,
787+
// marginTop: 2,
749788
paddingRight: 1,
750789
"&::-webkit-scrollbar": {
751790
width: "6px",
@@ -824,7 +863,10 @@ const DatasetDetailPage: React.FC = () => {
824863
backgroundColor: "#eaeaea",
825864
padding: 2,
826865
borderRadius: "8px",
827-
marginTop: 4,
866+
// marginTop: 4,
867+
flex: 1,
868+
// overflowY: "auto",
869+
// border: "2px solid blue",
828870
}}
829871
>
830872
{/* ✅ Header with toggle */}
@@ -849,7 +891,7 @@ const DatasetDetailPage: React.FC = () => {
849891
sx={{
850892
maxHeight: "400px",
851893
overflowY: "auto",
852-
marginTop: 2,
894+
// marginTop: 2,
853895
paddingRight: 1,
854896
"&::-webkit-scrollbar": {
855897
width: "6px",

0 commit comments

Comments
 (0)