Skip to content

Commit cfbfe5b

Browse files
committed
feat: add user-friendly description to limit and skip fields, and show guidance text before search
1 parent ad52a3c commit cfbfe5b

File tree

5 files changed

+148
-106
lines changed

5 files changed

+148
-106
lines changed

src/components/SearchPage/DatasetCard.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,11 +79,14 @@ const DatasetCard: React.FC<DatasetCardProps> = ({
7979
<Typography variant="body2" mt={1}>
8080
<strong>Subjects:</strong> {subj && `${subj.length} subjects`}
8181
</Typography>
82-
{/* {subj && <Chip label={`${subj.length} subjects`} color="primary" />} */}
8382
</Stack>
8483
<Stack direction="row" spacing={1} flexWrap="wrap" gap={1}>
8584
{readme && (
86-
<Typography variant="body2" paragraph>
85+
<Typography
86+
variant="body2"
87+
paragraph
88+
sx={{ textOverflow: "ellipsis" }}
89+
>
8790
<strong>Summary:</strong> {readme}
8891
</Typography>
8992
)}

src/components/SearchPage/SubjectCard.tsx

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ const SubjectCard: React.FC<SubjectCardProps> = ({
4141

4242
// cover age string to readable format
4343
let ageDisplay = "N/A";
44-
4544
if (age) {
4645
const ageNum = parseInt(age, 10) / 100;
4746
if (Number.isInteger(ageNum)) {
@@ -114,13 +113,6 @@ const SubjectCard: React.FC<SubjectCardProps> = ({
114113
<Typography variant="body2" mt={1}>
115114
<strong>Sessions:</strong> {sessions?.length}
116115
</Typography>
117-
{/* {sessions?.map((session, idx) => (
118-
<Chip
119-
key={`sess-${idx}`}
120-
label={`Session ${session}`}
121-
variant="outlined"
122-
/>
123-
))} */}
124116
</Stack>
125117
<Stack direction="row" spacing={1} flexWrap="wrap" gap={1}>
126118
{types?.length && (

src/design/theme.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export const Colors = {
3232
orange: "#FF9F2F",
3333
darkOrange: "#E88C25",
3434
blue: "#1976d2",
35+
lightBlue: "#e8f0fe",
3536
primary,
3637
secondary,
3738
};

src/pages/SearchPage.tsx

Lines changed: 140 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,11 @@ import { useAppDispatch } from "hooks/useAppDispatch";
99
import { useAppSelector } from "hooks/useAppSelector";
1010
import React from "react";
1111
import { useState, useEffect, useMemo } from "react";
12-
import { Link } from "react-router-dom";
1312
import {
1413
fetchMetadataSearchResults,
1514
fetchRegistry,
1615
} from "redux/neurojson/neurojson.action";
1716
import { RootState } from "redux/store";
18-
import RoutesEnum from "types/routes.enum";
1917

2018
const SearchPage: React.FC = () => {
2119
const dispatch = useAppDispatch();
@@ -27,19 +25,79 @@ const SearchPage: React.FC = () => {
2725
(state: RootState) => state.neurojson.registry
2826
);
2927

30-
// console.log("result:", searchResults);
31-
if (Array.isArray(searchResults)) {
32-
searchResults.forEach((item, idx) => {
33-
// console.log(`Raw item #${idx}:`, item);
34-
try {
35-
const parsed = JSON.parse(item.json);
36-
console.log(`Result #${idx}:`, { ...item, parsedJson: parsed });
37-
} catch (e) {
38-
console.error(`Failed to parse JSON for item #${idx}`, e);
39-
}
40-
});
41-
} else {
42-
console.warn("searchResults is not an array:", searchResults);
28+
const [formData, setFormData] = useState<Record<string, any>>({});
29+
30+
const uiSchema = useMemo(() => {
31+
const activeStyle = {
32+
"ui:options": {
33+
style: {
34+
backgroundColor: Colors.lightBlue,
35+
},
36+
},
37+
};
38+
39+
return {
40+
keyword: formData["keyword"] ? activeStyle : {},
41+
database:
42+
formData["database"] && formData["database"] !== "any"
43+
? activeStyle
44+
: {},
45+
dataset: formData["dataset"] ? activeStyle : {},
46+
47+
age_min: formData["age_min"] ? activeStyle : {},
48+
age_max: formData["age_max"] ? activeStyle : {},
49+
50+
gender:
51+
formData["gender"] && formData["gender"] !== "any" ? activeStyle : {},
52+
53+
sess_min: formData["sess_min"] ? activeStyle : {},
54+
sess_max: formData["sess_max"] ? activeStyle : {},
55+
56+
task_min: formData["task_min"] ? activeStyle : {},
57+
task_max: formData["task_max"] ? activeStyle : {},
58+
59+
run_min: formData["run_min"] ? activeStyle : {},
60+
run_max: formData["run_max"] ? activeStyle : {},
61+
62+
task_name: formData["task_name"] ? activeStyle : {},
63+
session_name: formData["session_name"] ? activeStyle : {},
64+
run_name: formData["run_name"] ? activeStyle : {},
65+
type_name: formData["type_name"] ? activeStyle : {},
66+
67+
modality:
68+
formData["modality"] && formData["modality"] !== "any"
69+
? activeStyle
70+
: {},
71+
72+
limit: formData["limit"] ? activeStyle : {},
73+
skip: formData["skip"] ? activeStyle : {},
74+
};
75+
}, [formData]);
76+
77+
// print the result in dev tool
78+
// if (Array.isArray(searchResults)) {
79+
// searchResults.forEach((item, idx) => {
80+
// try {
81+
// const parsed = JSON.parse(item.json);
82+
// console.log(`Result #${idx}:`, { ...item, parsedJson: parsed });
83+
// } catch (e) {
84+
// console.error(`Failed to parse JSON for item #${idx}`, e);
85+
// }
86+
// });
87+
// } else {
88+
// console.warn("searchResults is not an array:", searchResults);
89+
// }
90+
91+
// determine the results are subject-level or dataset-level
92+
let isDataset: boolean | null = null;
93+
94+
if (Array.isArray(searchResults) && searchResults.length > 0) {
95+
try {
96+
const parsed = JSON.parse(searchResults[0].json);
97+
isDataset = parsed?.value?.subj && Array.isArray(parsed.value.subj);
98+
} catch {
99+
isDataset = null;
100+
}
43101
}
44102

45103
useEffect(() => {
@@ -63,8 +121,7 @@ const SearchPage: React.FC = () => {
63121
<Container
64122
style={{
65123
marginTop: "2rem",
66-
// backgroundColor: "rgba(97, 109, 243, 0.4)",
67-
// backdropFilter: "blur(10px)",
124+
marginBottom: "2rem",
68125
backgroundColor: Colors.white,
69126
padding: "2rem",
70127
borderRadius: 4,
@@ -85,15 +142,36 @@ const SearchPage: React.FC = () => {
85142
p: 3,
86143
borderRadius: 2,
87144
boxShadow: 1,
145+
minWidth: "35%",
88146
}}
89147
>
90148
<Form
91149
schema={schema}
92150
onSubmit={handleSubmit}
93151
validator={validator}
94152
// liveValidate
153+
// formData={formData}
154+
// onChange={({ formData }) => setFormData(formData)}
155+
// uiSchema={uiSchema}
95156
/>
96157
</Box>
158+
<Box>
159+
{!hasSearched && (
160+
<Typography
161+
variant="subtitle1"
162+
sx={{
163+
// whiteSpace: "nowrap",
164+
flexWrap: "wrap",
165+
fontWeight: 500,
166+
fontSize: "large",
167+
color: Colors.darkPurple,
168+
}}
169+
>
170+
Use the filters to search for datasets or subjects based on
171+
metadata.
172+
</Typography>
173+
)}
174+
</Box>
97175

98176
<Box
99177
sx={{
@@ -105,95 +183,61 @@ const SearchPage: React.FC = () => {
105183
boxShadow: 1,
106184
}}
107185
>
108-
{hasSearched && searchResults && (
186+
{hasSearched && (
109187
<Box mt={4}>
110188
{Array.isArray(searchResults) ? (
111-
searchResults.length > 0 ? (
112-
searchResults.map((item, idx) => {
113-
try {
114-
const parsedJson = JSON.parse(item.json);
115-
const isDataset =
116-
parsedJson?.value?.subj &&
117-
Array.isArray(parsedJson.value.subj);
118-
119-
return isDataset ? (
120-
<DatasetCard
121-
key={idx}
122-
dbname={item.dbname}
123-
dsname={item.dsname}
124-
parsedJson={parsedJson}
125-
/>
126-
) : (
127-
<SubjectCard
128-
key={idx}
129-
{...item}
130-
parsedJson={parsedJson}
131-
/>
132-
);
133-
} catch (e) {
134-
return (
135-
<Typography key={idx} color="error">
136-
Failed to parse item #{idx}
137-
</Typography>
138-
);
139-
}
140-
})
141-
) : (
142-
<Typography variant="h6">
143-
No matching dataset was found
189+
<>
190+
<Typography
191+
variant="h6"
192+
sx={{ borderBottom: "1px solid lightgray", mb: 2 }}
193+
>
194+
{searchResults.length > 0
195+
? `Found ${searchResults.length} ${
196+
isDataset ? "Datasets" : "Subjects"
197+
}`
198+
: `No matching ${
199+
isDataset ? "datasets" : "subjects"
200+
} found`}
144201
</Typography>
145-
)
146-
) : (
147-
<Typography color="error">
148-
{searchResults?.msg === "empty output"
149-
? "No results found based on your criteria. Please adjust the filters and try again."
150-
: "Something went wrong. Please try again later."}
151-
</Typography>
152-
)}
153202

154-
{/* {Array.isArray(searchResults) ? (
155-
searchResults.length > 0 ? (
156-
<>
157-
<Typography
158-
variant="h6"
159-
sx={{ borderBottom: "1px solid lightgray" }}
160-
>
161-
{`Found ${searchResults.length} Datasets`}
162-
</Typography>
163-
<ul>
164-
{searchResults.map((item, idx) => {
165-
const label = `${item.dbname}/${item.dsname}`;
166-
const link = `${RoutesEnum.DATABASES}/${item.dbname}/${item.dsname}`;
167-
168-
return (
169-
<Box key={idx} mb={1}>
170-
<Link
171-
to={link}
172-
style={{
173-
textDecoration: "none",
174-
color: Colors.blue,
175-
}}
176-
target="_blank"
177-
>
178-
{label}
179-
</Link>
180-
</Box>
203+
{searchResults.length > 0 &&
204+
searchResults.map((item, idx) => {
205+
try {
206+
const parsedJson = JSON.parse(item.json);
207+
const isDataset =
208+
parsedJson?.value?.subj &&
209+
Array.isArray(parsedJson.value.subj);
210+
211+
return isDataset ? (
212+
<DatasetCard
213+
key={idx}
214+
dbname={item.dbname}
215+
dsname={item.dsname}
216+
parsedJson={parsedJson}
217+
/>
218+
) : (
219+
<SubjectCard
220+
key={idx}
221+
{...item}
222+
parsedJson={parsedJson}
223+
/>
181224
);
182-
})}
183-
</ul>
184-
</>
185-
) : (
186-
<Typography variant="h6">
187-
No matching dataset was found
188-
</Typography>
189-
)
225+
} catch (e) {
226+
console.error(
227+
`Failed to parse JSON for item #${idx}`,
228+
e
229+
);
230+
return null;
231+
}
232+
})}
233+
</>
190234
) : (
191-
<Typography color="error">
235+
<Typography sx={{ color: Colors.error }}>
192236
{searchResults?.msg === "empty output"
193237
? "No results found based on your criteria. Please adjust the filters and try again."
194238
: "Something went wrong. Please try again later."}
195239
</Typography>
196-
)} */}
240+
)}
197241
</Box>
198242
)}
199243
</Box>

src/pages/searchformSchema.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,11 +120,13 @@ export const baseSchema: JSONSchema7 = {
120120
type: "integer",
121121
title: "limit",
122122
minimum: 0,
123+
description: "Set the maximum number of results to return.",
123124
},
124125
skip: {
125126
type: "integer",
126127
title: "skip",
127128
minimum: 0,
129+
description: "Set a number N to skip the first N results.",
128130
},
129131
// count: {
130132
// title: "Only return total counts",

0 commit comments

Comments
 (0)