Skip to content

Commit ff78764

Browse files
committed
feat: integrate metadata search form with Redux for state management
1 parent d2d3d07 commit ff78764

File tree

6 files changed

+106
-82
lines changed

6 files changed

+106
-82
lines changed

src/design/theme.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ export const Colors = {
3131
darkPurple: "#282C56",
3232
orange: "#FF9F2F",
3333
darkOrange: "#E88C25",
34+
blue: "#1976d2",
3435
primary,
3536
secondary,
3637
};

src/pages/SearchPage.tsx

Lines changed: 21 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -2,81 +2,25 @@ import { schema } from "./searchformSchema";
22
import { Typography, Container, Box } from "@mui/material";
33
import Form from "@rjsf/mui";
44
import validator from "@rjsf/validator-ajv8";
5+
import { Colors } from "design/theme";
6+
import { useAppDispatch } from "hooks/useAppDispatch";
7+
import { useAppSelector } from "hooks/useAppSelector";
58
import React from "react";
69
import { useState } from "react";
710
import { Link } from "react-router-dom";
11+
import { fetchMetadataSearchResults } from "redux/neurojson/neurojson.action";
12+
import { RootState } from "redux/store";
813
import RoutesEnum from "types/routes.enum";
914

10-
// helper function to build query string
11-
const buildQueryString = (formData: any): string => {
12-
const map: Record<string, string> = {
13-
keyword: "keyword",
14-
age_min: "agemin",
15-
age_max: "agemax",
16-
task_min: "taskmin",
17-
task_max: "taskmax",
18-
run_min: "runmin",
19-
run_max: "runmax",
20-
sess_min: "sessmin",
21-
sess_max: "sessmax",
22-
modality: "modality",
23-
run_name: "run",
24-
type_name: "type",
25-
session_name: "session",
26-
task_name: "task",
27-
limit: "limit",
28-
skip: "skip",
29-
count: "count",
30-
unique: "unique",
31-
gender: "gender",
32-
database: "dbname",
33-
dataset: "dsname",
34-
subject: "subname",
35-
};
36-
37-
const params = new URLSearchParams();
38-
Object.keys(formData).forEach((key) => {
39-
let val = formData[key];
40-
if (val === "" || val === "any" || val === undefined || val === null)
41-
return;
42-
43-
const queryKey = map[key];
44-
if (!queryKey) return;
45-
46-
if (key.startsWith("age")) {
47-
params.append(queryKey, String(Math.floor(val * 100)).padStart(5, "0"));
48-
} else if (key === "gender") {
49-
params.append(queryKey, val[0]);
50-
} else if (key === "modality") {
51-
params.append(queryKey, val.replace(/.*\(/, "").replace(/\).*/, ""));
52-
} else {
53-
params.append(queryKey, val.toString());
54-
}
55-
});
56-
57-
return `?${params.toString()}`;
58-
};
59-
6015
const SearchPage: React.FC = () => {
61-
const [result, setResult] = useState<any>(null);
16+
const dispatch = useAppDispatch();
6217
const [hasSearched, setHasSearched] = useState(false);
18+
const searchResults = useAppSelector(
19+
(state: RootState) => state.neurojson.searchResults
20+
);
6321

64-
// const handleSubmit = ({ formData }: any) => {
65-
// console.log("submitted search query:", formData);
66-
// };
67-
const handleSubmit = async ({ formData }: any) => {
68-
console.log("submitted search query:", formData);
69-
const query = buildQueryString(formData);
70-
const url = `https://cors.redoc.ly/https://neurojson.org/io/search.cgi${query}`;
71-
// console.log("url", url);
72-
try {
73-
const res = await fetch(url);
74-
const data = await res.json();
75-
setResult(data);
76-
console.log(data);
77-
} catch (err) {
78-
console.error("Failed to fetch data:", err);
79-
}
22+
const handleSubmit = ({ formData }: any) => {
23+
dispatch(fetchMetadataSearchResults(formData));
8024
setHasSearched(true);
8125
};
8226

@@ -97,32 +41,27 @@ const SearchPage: React.FC = () => {
9741
liveValidate
9842
/>
9943

100-
{/* {result && (
101-
<Box mt={4}>
102-
<Typography variant="h6">Datasets Found</Typography>
103-
<pre style={{ background: "#f5f5f5", padding: "1rem" }}>
104-
{JSON.stringify(result, null, 2)}
105-
</pre>
106-
</Box>
107-
)} */}
108-
{hasSearched && (
44+
{hasSearched && searchResults && (
10945
<Box mt={4}>
110-
{Array.isArray(result) ? (
111-
result.length > 0 ? (
46+
{Array.isArray(searchResults) ? (
47+
searchResults.length > 0 ? (
11248
<>
11349
<Typography variant="h6">
114-
{`Found ${result.length} Datasets`}
50+
{`Found ${searchResults.length} Datasets`}
11551
</Typography>
11652
<ul>
117-
{result.map((item, idx) => {
53+
{searchResults.map((item, idx) => {
11854
const label = `${item.dbname}/${item.dsname}`;
11955
const link = `${RoutesEnum.DATABASES}/${item.dbname}/${item.dsname}`;
12056

12157
return (
12258
<Box key={idx} mb={1}>
12359
<Link
12460
to={link}
125-
style={{ textDecoration: "none", color: "#1976d2" }}
61+
style={{
62+
textDecoration: "none",
63+
color: Colors.blue,
64+
}}
12665
>
12766
{label}
12867
</Link>
@@ -138,7 +77,7 @@ const SearchPage: React.FC = () => {
13877
)
13978
) : (
14079
<Typography color="error">
141-
{result?.msg === "empty output"
80+
{searchResults?.msg === "empty output"
14281
? "No results found based on your criteria. Please adjust the filters and try again."
14382
: "Something went wrong. Please try again later."}
14483
</Typography>

src/redux/neurojson/neurojson.action.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,3 +95,15 @@ export const fetchDbStats = createAsyncThunk(
9595
}
9696
}
9797
);
98+
99+
export const fetchMetadataSearchResults = createAsyncThunk(
100+
"neurojson/fetchMetadataSearchResults",
101+
async (formData: any, { rejectWithValue }) => {
102+
try {
103+
const data = await NeurojsonService.getMetadataSearchResults(formData);
104+
return data;
105+
} catch (error: any) {
106+
return rejectWithValue("Failed to fetch metadata search results");
107+
}
108+
}
109+
);

src/redux/neurojson/neurojson.slice.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@ import {
55
fetchDbInfo,
66
fetchDocumentDetails,
77
fetchDbStats,
8+
fetchMetadataSearchResults,
89
} from "./neurojson.action";
910
import { DBDatafields, INeuroJsonState } from "./types/neurojson.interface";
1011
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
12+
import { stat } from "fs";
1113

1214
const initialState: INeuroJsonState = {
1315
loading: false,
@@ -20,6 +22,7 @@ const initialState: INeuroJsonState = {
2022
registry: null,
2123
dbInfo: null, // add dbInfo in neurojson.interface.ts
2224
dbStats: null,
25+
searchResults: null,
2326
};
2427

2528
const neurojsonSlice = createSlice({
@@ -144,6 +147,21 @@ const neurojsonSlice = createSlice({
144147
.addCase(fetchDbStats.rejected, (state, action) => {
145148
state.loading = false;
146149
state.error = action.payload as string;
150+
})
151+
.addCase(fetchMetadataSearchResults.pending, (state) => {
152+
state.loading = true;
153+
state.error = null;
154+
})
155+
.addCase(
156+
fetchMetadataSearchResults.fulfilled,
157+
(state, action: PayloadAction<any>) => {
158+
state.loading = false;
159+
state.searchResults = action.payload;
160+
}
161+
)
162+
.addCase(fetchMetadataSearchResults.rejected, (state, action) => {
163+
state.loading = false;
164+
state.error = action.payload as string;
147165
});
148166
},
149167
});

src/redux/neurojson/types/neurojson.interface.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export interface INeuroJsonState {
1111
hasMore: boolean;
1212
dbInfo: DBParticulars | null; // add dbInfo type
1313
dbStats: DbStatsItem[] | null; // for dbStats on landing page
14+
searchResults: any[] | { status: string; msg: string } | null;
1415
}
1516

1617
export interface DBParticulars {

src/services/neurojson.service.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,57 @@ export const NeurojsonService = {
4646
);
4747
return response.data;
4848
},
49+
getMetadataSearchResults: async (formData: any): Promise<any> => {
50+
const map: Record<string, string> = {
51+
keyword: "keyword",
52+
age_min: "agemin",
53+
age_max: "agemax",
54+
task_min: "taskmin",
55+
task_max: "taskmax",
56+
run_min: "runmin",
57+
run_max: "runmax",
58+
sess_min: "sessmin",
59+
sess_max: "sessmax",
60+
modality: "modality",
61+
run_name: "run",
62+
type_name: "type",
63+
session_name: "session",
64+
task_name: "task",
65+
limit: "limit",
66+
skip: "skip",
67+
count: "count",
68+
unique: "unique",
69+
gender: "gender",
70+
database: "dbname",
71+
dataset: "dsname",
72+
subject: "subname",
73+
};
74+
75+
const params = new URLSearchParams();
76+
Object.keys(formData).forEach((key) => {
77+
let val = formData[key];
78+
if (val === "" || val === "any" || val === undefined || val === null)
79+
return;
80+
81+
const queryKey = map[key];
82+
if (!queryKey) return;
83+
84+
if (key.startsWith("age")) {
85+
params.append(queryKey, String(Math.floor(val * 100)).padStart(5, "0"));
86+
} else if (key === "gender") {
87+
params.append(queryKey, val[0]);
88+
} else if (key === "modality") {
89+
params.append(queryKey, val.replace(/.*\(/, "").replace(/\).*/, ""));
90+
} else {
91+
params.append(queryKey, val.toString());
92+
}
93+
});
94+
95+
const queryString = `?${params.toString()}`;
96+
const response = await axios.get(
97+
`https://cors.redoc.ly/https://neurojson.org/io/search.cgi${queryString}`
98+
);
99+
100+
return response.data;
101+
},
49102
};

0 commit comments

Comments
 (0)