@openneuro/app 4.30.1 → 4.31.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +5 -5
- package/src/assets/activity-icon.png +0 -0
- package/src/assets/icon-archived.png +0 -0
- package/src/assets/icon-saved.png +0 -0
- package/src/assets/icon-unread.png +0 -0
- package/src/client.jsx +1 -1
- package/src/scripts/datalad/dataset/dataset-query-fragments.js +4 -14
- package/src/scripts/dataset/__tests__/__snapshots__/snapshot-container.spec.tsx.snap +4 -304
- package/src/scripts/dataset/components/ValidationBlock.tsx +13 -15
- package/src/scripts/dataset/components/__tests__/ValidationBlock.spec.tsx +2 -0
- package/src/scripts/dataset/draft-container.tsx +2 -1
- package/src/scripts/dataset/files/__tests__/__snapshots__/file-tree.spec.jsx.snap +1 -9
- package/src/scripts/dataset/fragments/__tests__/{dataset-alert-draft.spec.tsx → dataset-alert.spec.tsx} +33 -1
- package/src/scripts/dataset/fragments/{dataset-alert-draft.tsx → dataset-alert.tsx} +30 -18
- package/src/scripts/dataset/routes/delete-page.tsx +72 -39
- package/src/scripts/dataset/routes/snapshot.tsx +23 -17
- package/src/scripts/dataset/routes/tab-routes-draft.tsx +5 -2
- package/src/scripts/dataset/snapshot-container.tsx +11 -0
- package/src/scripts/search/__tests__/search-params-ctx.spec.tsx +3 -0
- package/src/scripts/search/initial-search-params.tsx +2 -0
- package/src/scripts/search/inputs/__tests__/nihselect.spec.tsx +36 -0
- package/src/scripts/search/inputs/index.ts +2 -0
- package/src/scripts/search/inputs/nih-select.tsx +63 -0
- package/src/scripts/search/search-container.tsx +20 -12
- package/src/scripts/search/search-params-ctx.tsx +2 -0
- package/src/scripts/search/search-routes.tsx +14 -6
- package/src/scripts/search/use-search-results.tsx +15 -0
- package/src/scripts/types/user-types.ts +72 -0
- package/src/scripts/uploader/upload-issues.tsx +2 -2
- package/src/scripts/users/__tests__/datasest-card.spec.tsx +201 -0
- package/src/scripts/users/__tests__/user-card.spec.tsx +30 -3
- package/src/scripts/users/__tests__/user-query.spec.tsx +6 -0
- package/src/scripts/users/__tests__/user-routes.spec.tsx +42 -18
- package/src/scripts/users/components/user-dataset-filters.tsx +157 -0
- package/src/scripts/users/dataset-card.tsx +121 -0
- package/src/scripts/users/fragments/query.js +42 -0
- package/src/scripts/users/scss/datasetcard.module.scss +153 -0
- package/src/scripts/users/scss/usernotifications.module.scss +159 -0
- package/src/scripts/users/user-account-view.tsx +1 -12
- package/src/scripts/users/user-card.tsx +1 -14
- package/src/scripts/users/user-container.tsx +1 -17
- package/src/scripts/users/user-datasets-view.tsx +58 -43
- package/src/scripts/users/user-notification-accordion.tsx +160 -0
- package/src/scripts/users/user-notification-list.tsx +27 -0
- package/src/scripts/users/user-notifications-tab-content.tsx +85 -0
- package/src/scripts/users/user-notifications-view.tsx +102 -4
- package/src/scripts/users/user-query.tsx +6 -14
- package/src/scripts/users/user-routes.tsx +18 -19
- package/src/scripts/utils/__tests__/user-datasets.spec.tsx +86 -0
- package/src/scripts/utils/gtag.js +3 -2
- package/src/scripts/utils/user-datasets.tsx +60 -0
- package/src/scripts/validation/__tests__/__snapshots__/validation-issues.spec.tsx.snap +1 -122
- package/src/scripts/validation/validation-results-query.ts +44 -0
- package/src/scripts/validation/validation-results.tsx +31 -7
- package/src/scripts/validation/validation.tsx +58 -49
- package/src/scripts/workers/schema.worker.ts +2 -7
- package/tsconfig.json +1 -2
- package/vite.config.js +1 -0
|
@@ -1,22 +1,16 @@
|
|
|
1
1
|
import React, { useState } from "react"
|
|
2
|
-
import { useCookies } from "react-cookie"
|
|
3
2
|
import DeleteDatasetForm from "../mutations/delete-dataset-form.jsx"
|
|
4
3
|
import DeleteDataset from "../mutations/delete.jsx"
|
|
5
4
|
import LoggedIn from "../../authentication/logged-in.jsx"
|
|
6
|
-
import { getProfile } from "../../authentication/profile"
|
|
7
|
-
import AdminUser from "../../authentication/admin-user.jsx"
|
|
8
|
-
import { RegularUser } from "../../authentication/regular-user"
|
|
9
5
|
import { DatasetPageBorder } from "./styles/dataset-page-border"
|
|
10
6
|
import { HeaderRow3 } from "./styles/header-row"
|
|
11
7
|
|
|
12
|
-
interface
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
id: string
|
|
16
|
-
}
|
|
8
|
+
interface DeletePageAction {
|
|
9
|
+
datasetId: string
|
|
10
|
+
hasEdit: boolean
|
|
17
11
|
}
|
|
18
12
|
|
|
19
|
-
|
|
13
|
+
export function DeletePageAction({ datasetId, hasEdit }: DeletePageAction) {
|
|
20
14
|
const [values, setValues] = useState({
|
|
21
15
|
reason: "",
|
|
22
16
|
redirect: "",
|
|
@@ -28,40 +22,79 @@ const DeletePage = ({ dataset }: DeletePageProps): React.ReactElement => {
|
|
|
28
22
|
}
|
|
29
23
|
setValues(newValues)
|
|
30
24
|
}
|
|
31
|
-
const [cookies] = useCookies()
|
|
32
|
-
const user = getProfile(cookies)
|
|
33
|
-
const hasEdit = user && user.admin
|
|
34
|
-
const datasetId = dataset.id
|
|
35
25
|
return (
|
|
36
|
-
|
|
37
|
-
<
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
26
|
+
<>
|
|
27
|
+
<DeleteDatasetForm
|
|
28
|
+
values={values}
|
|
29
|
+
onChange={handleInputChange}
|
|
30
|
+
hideDisabled={false}
|
|
31
|
+
hasEdit={hasEdit}
|
|
32
|
+
/>
|
|
33
|
+
<p>
|
|
34
|
+
<small className="warning-text">
|
|
35
|
+
* Warning: this action will permanently remove this dataset along with
|
|
36
|
+
associated snapshots.
|
|
37
|
+
</small>
|
|
38
|
+
</p>
|
|
39
|
+
<div className="dataset-form-controls">
|
|
40
|
+
<LoggedIn>
|
|
41
|
+
<DeleteDataset datasetId={datasetId} metadata={values} />
|
|
42
|
+
</LoggedIn>
|
|
43
|
+
</div>
|
|
44
|
+
</>
|
|
45
|
+
)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
interface DeletePageMessageProps {
|
|
49
|
+
hasEdit: boolean
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function DeletePageMessage({ hasEdit }: DeletePageMessageProps) {
|
|
53
|
+
if (hasEdit) {
|
|
54
|
+
return (
|
|
55
|
+
<>
|
|
45
56
|
<p>
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
with associated snapshots.
|
|
49
|
-
</small>
|
|
57
|
+
This dataset has versions with DOIs that would be orphaned by
|
|
58
|
+
deletion.
|
|
50
59
|
</p>
|
|
51
|
-
<div className="dataset-form-controls">
|
|
52
|
-
<LoggedIn>
|
|
53
|
-
<DeleteDataset datasetId={datasetId} metadata={values} />
|
|
54
|
-
</LoggedIn>
|
|
55
|
-
</div>
|
|
56
|
-
</AdminUser>
|
|
57
|
-
<RegularUser>
|
|
58
60
|
<p>
|
|
59
|
-
|
|
60
|
-
versions of it. Provide a reason for the removal
|
|
61
|
-
dataset is a duplicate or has been supplanted by
|
|
62
|
-
information for a redirect to be created.
|
|
61
|
+
If deletion is required please contact support to permanently remove a
|
|
62
|
+
dataset and all versions of it. Provide a reason for the removal
|
|
63
|
+
request and if the dataset is a duplicate or has been supplanted by
|
|
64
|
+
another provide that information for a redirect to be created.
|
|
63
65
|
</p>
|
|
64
|
-
|
|
66
|
+
</>
|
|
67
|
+
)
|
|
68
|
+
} else {
|
|
69
|
+
return (
|
|
70
|
+
<p>
|
|
71
|
+
Login or request dataset admin permissions from a dataset admin to
|
|
72
|
+
delete this dataset.
|
|
73
|
+
</p>
|
|
74
|
+
)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
interface DeletePageProps {
|
|
79
|
+
dataset: {
|
|
80
|
+
permissions: Record<string, object>
|
|
81
|
+
id: string
|
|
82
|
+
snapshots: object[]
|
|
83
|
+
}
|
|
84
|
+
hasEdit: boolean
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const DeletePage = (
|
|
88
|
+
{ dataset, hasEdit }: DeletePageProps,
|
|
89
|
+
): React.ReactElement => {
|
|
90
|
+
const datasetId = dataset.id
|
|
91
|
+
const canBeDeleted = dataset.snapshots.length === 0 && hasEdit
|
|
92
|
+
return (
|
|
93
|
+
<DatasetPageBorder>
|
|
94
|
+
<HeaderRow3>Delete Dataset</HeaderRow3>
|
|
95
|
+
{canBeDeleted
|
|
96
|
+
? <DeletePageAction datasetId={datasetId} hasEdit={hasEdit} />
|
|
97
|
+
: <DeletePageMessage hasEdit={hasEdit} />}
|
|
65
98
|
</DatasetPageBorder>
|
|
66
99
|
)
|
|
67
100
|
}
|
|
@@ -15,12 +15,10 @@ const FormRow = styled.div`
|
|
|
15
15
|
margin-bottom: 1.3em;
|
|
16
16
|
`
|
|
17
17
|
|
|
18
|
-
export const NoErrors = ({
|
|
19
|
-
const noErrors =
|
|
20
|
-
issues.filter((issue) => issue.severity === "error").length === 0
|
|
18
|
+
export const NoErrors = ({ validation, authors, children }) => {
|
|
19
|
+
const noErrors = validation?.errors === 0
|
|
21
20
|
// zero authors will cause DOI minting to fail
|
|
22
|
-
const hasAuthor =
|
|
23
|
-
issues.filter((issue) => issue.code === 113).length === 0
|
|
21
|
+
const hasAuthor = authors.length > 0
|
|
24
22
|
if (noErrors && hasAuthor) {
|
|
25
23
|
return children
|
|
26
24
|
} else {
|
|
@@ -42,7 +40,7 @@ export const NoErrors = ({ issues, children }) => {
|
|
|
42
40
|
const SnapshotRoute = ({
|
|
43
41
|
datasetId,
|
|
44
42
|
snapshots,
|
|
45
|
-
|
|
43
|
+
validation,
|
|
46
44
|
description,
|
|
47
45
|
}): React.ReactElement => {
|
|
48
46
|
const [changes, setChanges] = useState([])
|
|
@@ -76,8 +74,8 @@ const SnapshotRoute = ({
|
|
|
76
74
|
)}
|
|
77
75
|
<p>
|
|
78
76
|
Create a new version of this dataset for download and public access.
|
|
79
|
-
This will begin an export of
|
|
80
|
-
been made public.
|
|
77
|
+
DOIs are assigned to versions created. This will begin an export of
|
|
78
|
+
this dataset to GitHub and S3 if it has been made public.
|
|
81
79
|
</p>
|
|
82
80
|
<FormRow className="snapshot-input-group">
|
|
83
81
|
{newVersion}
|
|
@@ -124,21 +122,29 @@ const SnapshotRoute = ({
|
|
|
124
122
|
setElements={setChanges}
|
|
125
123
|
/>
|
|
126
124
|
</div>
|
|
127
|
-
<NoErrors
|
|
125
|
+
<NoErrors validation={validation} authors={description.Authors}>
|
|
128
126
|
{changes.length ? null : (
|
|
129
127
|
<small className="text-danger">
|
|
130
128
|
You must add at least one change message to create a new version
|
|
131
129
|
</small>
|
|
132
130
|
)}
|
|
131
|
+
{snapshots.length === 0
|
|
132
|
+
? (
|
|
133
|
+
<p>
|
|
134
|
+
Once a version has been created and DOI assigned, the dataset
|
|
135
|
+
can no longer be deleted.
|
|
136
|
+
</p>
|
|
137
|
+
)
|
|
138
|
+
: null}
|
|
139
|
+
<div className="dataset-form-controls">
|
|
140
|
+
<SnapshotDataset
|
|
141
|
+
datasetId={datasetId}
|
|
142
|
+
tag={newVersion}
|
|
143
|
+
changes={changes}
|
|
144
|
+
disabled={changes.length < 1}
|
|
145
|
+
/>
|
|
146
|
+
</div>
|
|
133
147
|
</NoErrors>
|
|
134
|
-
<div className="dataset-form-controls">
|
|
135
|
-
<SnapshotDataset
|
|
136
|
-
datasetId={datasetId}
|
|
137
|
-
tag={newVersion}
|
|
138
|
-
changes={changes}
|
|
139
|
-
disabled={changes.length < 1}
|
|
140
|
-
/>
|
|
141
|
-
</div>
|
|
142
148
|
</div>
|
|
143
149
|
</DatasetPageBorder>
|
|
144
150
|
)
|
|
@@ -31,7 +31,10 @@ export const TabRoutesDraft = ({ dataset, hasEdit }) => {
|
|
|
31
31
|
path="derivatives"
|
|
32
32
|
element={<Derivatives derivatives={dataset.derivatives} />}
|
|
33
33
|
/>
|
|
34
|
-
<Route
|
|
34
|
+
<Route
|
|
35
|
+
path="delete"
|
|
36
|
+
element={<DeletePage dataset={dataset} hasEdit={hasEdit} />}
|
|
37
|
+
/>
|
|
35
38
|
<Route path="admin" element={<AdminDataset dataset={dataset} />} />
|
|
36
39
|
<Route
|
|
37
40
|
path="publish"
|
|
@@ -43,7 +46,7 @@ export const TabRoutesDraft = ({ dataset, hasEdit }) => {
|
|
|
43
46
|
<Snapshot
|
|
44
47
|
datasetId={dataset.id}
|
|
45
48
|
snapshots={dataset.snapshots}
|
|
46
|
-
|
|
49
|
+
validation={dataset.draft.validation}
|
|
47
50
|
description={dataset.draft.description}
|
|
48
51
|
/>
|
|
49
52
|
}
|
|
@@ -9,6 +9,7 @@ import { pageTitle } from "../resources/strings.js"
|
|
|
9
9
|
import { config } from "../config"
|
|
10
10
|
import DatasetCitation from "./fragments/dataset-citation.jsx"
|
|
11
11
|
import { DatasetAlertVersion } from "./fragments/dataset-alert-version"
|
|
12
|
+
import { DatasetAlertPrivate } from "./fragments/dataset-alert"
|
|
12
13
|
import { AnalyzeDropdown } from "./components/AnalyzeDropdown"
|
|
13
14
|
import { CloneDropdown } from "./components/CloneDropdown"
|
|
14
15
|
import { DatasetGitAccess } from "./components/DatasetGitAccess"
|
|
@@ -74,6 +75,9 @@ export const SnapshotContainer: React.FC<SnapshotContainerProps> = ({
|
|
|
74
75
|
isAdmin
|
|
75
76
|
const isDatasetAdmin =
|
|
76
77
|
hasDatasetAdminPermissions(dataset.permissions, profile?.sub) || isAdmin
|
|
78
|
+
const hasDraftChanges = dataset.snapshots.length === 0 ||
|
|
79
|
+
dataset.draft.head !==
|
|
80
|
+
dataset.snapshots[dataset.snapshots.length - 1].hexsha
|
|
77
81
|
const modality: string = summary?.modalities[0] || ""
|
|
78
82
|
const hasDerivatives = dataset?.derivatives.length > 0
|
|
79
83
|
|
|
@@ -111,6 +115,12 @@ export const SnapshotContainer: React.FC<SnapshotContainerProps> = ({
|
|
|
111
115
|
</DatasetHeader>
|
|
112
116
|
</>
|
|
113
117
|
)}
|
|
118
|
+
{!dataset.public && isDatasetAdmin && (
|
|
119
|
+
<DatasetAlertPrivate
|
|
120
|
+
datasetId={dataset.id}
|
|
121
|
+
hasDraftChanges={hasDraftChanges}
|
|
122
|
+
/>
|
|
123
|
+
)}
|
|
114
124
|
{snapshot?.deprecated && (
|
|
115
125
|
<DatasetAlertVersion
|
|
116
126
|
datasetId={dataset.id}
|
|
@@ -125,6 +135,7 @@ export const SnapshotContainer: React.FC<SnapshotContainerProps> = ({
|
|
|
125
135
|
<div className="dataset-validation">
|
|
126
136
|
<ValidationBlock
|
|
127
137
|
datasetId={datasetId}
|
|
138
|
+
version={snapshot.tag}
|
|
128
139
|
issues={snapshot.issues}
|
|
129
140
|
validation={snapshot.validation}
|
|
130
141
|
/>
|
|
@@ -41,6 +41,9 @@ describe("SearchParamsProvider", () => {
|
|
|
41
41
|
</SearchParamsCtx.Consumer>,
|
|
42
42
|
{ wrapper },
|
|
43
43
|
)
|
|
44
|
+
// Open the author dropdown
|
|
45
|
+
const authorDropdown = screen.getByRole("switch")
|
|
46
|
+
fireEvent.click(authorDropdown)
|
|
44
47
|
const textInput = screen.getByRole("textbox")
|
|
45
48
|
const button = screen.getByRole("button")
|
|
46
49
|
fireEvent.change(textInput, { target: { value: "New Author" } })
|
|
@@ -157,6 +157,7 @@ export interface SearchParams {
|
|
|
157
157
|
sortBy_selected
|
|
158
158
|
bidsDatasetType_available: string[]
|
|
159
159
|
bidsDatasetType_selected: string | null
|
|
160
|
+
brain_initiative: string | null
|
|
160
161
|
}
|
|
161
162
|
|
|
162
163
|
const initialSearchParams: SearchParams = {
|
|
@@ -204,6 +205,7 @@ const initialSearchParams: SearchParams = {
|
|
|
204
205
|
sortBy_selected: sortBy[0],
|
|
205
206
|
bidsDatasetType_available: ["raw", "derivative"],
|
|
206
207
|
bidsDatasetType_selected: null,
|
|
208
|
+
brain_initiative: null,
|
|
207
209
|
}
|
|
208
210
|
|
|
209
211
|
export default initialSearchParams
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
import { fireEvent, render, screen, waitFor } from "@testing-library/react"
|
|
3
|
+
import { MemoryRouter, Route, Routes } from "react-router-dom"
|
|
4
|
+
import { SearchParamsCtx } from "../../search-params-ctx"
|
|
5
|
+
import NIHSelect from "../nih-select"
|
|
6
|
+
|
|
7
|
+
const customRender = (
|
|
8
|
+
ui,
|
|
9
|
+
{ searchParams = {}, initialRoute = "/search" } = {},
|
|
10
|
+
) => {
|
|
11
|
+
return render(
|
|
12
|
+
<SearchParamsCtx.Provider
|
|
13
|
+
value={{ searchParams, setSearchParams: () => {} }}
|
|
14
|
+
>
|
|
15
|
+
<MemoryRouter initialEntries={[initialRoute]}>
|
|
16
|
+
<Routes>
|
|
17
|
+
<Route path="/search" element={ui} />
|
|
18
|
+
<Route path="/search/nih" element={<h1>NIH Brain Initiative</h1>} />
|
|
19
|
+
</Routes>
|
|
20
|
+
</MemoryRouter>
|
|
21
|
+
</SearchParamsCtx.Provider>,
|
|
22
|
+
)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
describe("NIHSelect Component", () => {
|
|
26
|
+
it("updates search params when NIH is selected and navigates to the NIH page", async () => {
|
|
27
|
+
customRender(<NIHSelect label="NIH Funding" />)
|
|
28
|
+
|
|
29
|
+
const nihOption = screen.getByText("NIH Funding")
|
|
30
|
+
fireEvent.click(nihOption)
|
|
31
|
+
|
|
32
|
+
await waitFor(() => {
|
|
33
|
+
expect(screen.getByText("NIH Brain Initiative")).toBeInTheDocument()
|
|
34
|
+
})
|
|
35
|
+
})
|
|
36
|
+
})
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import KeywordInput from "./keyword-input"
|
|
2
2
|
import AllDatasetsToggle from "./admin-allDatasets-toggle"
|
|
3
3
|
import ModalitySelect from "./modality-select"
|
|
4
|
+
import NIHSelect from "./nih-select"
|
|
4
5
|
import ShowDatasetRadios from "./show-datasets-radios"
|
|
5
6
|
import AgeRangeInput from "./age-range-input"
|
|
6
7
|
import SubjectCountRangeInput from "./subject-count-range-input"
|
|
@@ -30,6 +31,7 @@ export {
|
|
|
30
31
|
DiagnosisSelect,
|
|
31
32
|
KeywordInput,
|
|
32
33
|
ModalitySelect,
|
|
34
|
+
NIHSelect,
|
|
33
35
|
ScannerManufacturers,
|
|
34
36
|
ScannerManufacturersModelNames,
|
|
35
37
|
SectionSelect,
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import React, { useContext, useEffect } from "react"
|
|
2
|
+
import type { FC } from "react"
|
|
3
|
+
import { useLocation, useNavigate } from "react-router-dom"
|
|
4
|
+
import { SearchParamsCtx } from "../search-params-ctx"
|
|
5
|
+
import { SingleSelect } from "@openneuro/components/facets"
|
|
6
|
+
|
|
7
|
+
interface NIHSelectProps {
|
|
8
|
+
label?: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const NIHSelect: FC<NIHSelectProps> = ({ label }) => {
|
|
12
|
+
const { searchParams, setSearchParams } = useContext(SearchParamsCtx)
|
|
13
|
+
const navigate = useNavigate()
|
|
14
|
+
const location = useLocation()
|
|
15
|
+
|
|
16
|
+
const { brain_initiative } = searchParams
|
|
17
|
+
|
|
18
|
+
const setBrainInitiative = (selectedValue: string): void => {
|
|
19
|
+
const newSelectedFunding = selectedValue === "NIH" ? "true" : ""
|
|
20
|
+
|
|
21
|
+
// Update only brain_initiative in the search params context
|
|
22
|
+
setSearchParams((prevState) => ({
|
|
23
|
+
...prevState,
|
|
24
|
+
brain_initiative: newSelectedFunding,
|
|
25
|
+
}))
|
|
26
|
+
|
|
27
|
+
const params = new URLSearchParams(location.search)
|
|
28
|
+
|
|
29
|
+
if (newSelectedFunding) {
|
|
30
|
+
params.set(
|
|
31
|
+
"query",
|
|
32
|
+
JSON.stringify({ brain_initiative: newSelectedFunding }),
|
|
33
|
+
)
|
|
34
|
+
} else {
|
|
35
|
+
params.delete("query")
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
navigate(`/search/nih?${params.toString()}`, { replace: true })
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
if (
|
|
43
|
+
brain_initiative === "true" &&
|
|
44
|
+
!location.search.includes("brain_initiative")
|
|
45
|
+
) {
|
|
46
|
+
const params = new URLSearchParams(location.search)
|
|
47
|
+
params.set("query", JSON.stringify({ brain_initiative: "true" }))
|
|
48
|
+
navigate(`/search/nih?${params.toString()}`, { replace: true })
|
|
49
|
+
}
|
|
50
|
+
}, [brain_initiative, location.search, navigate])
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<SingleSelect
|
|
54
|
+
className="nih-facet facet-open"
|
|
55
|
+
label={label}
|
|
56
|
+
selected={brain_initiative === "true" ? "NIH" : ""}
|
|
57
|
+
setSelected={setBrainInitiative}
|
|
58
|
+
items={["NIH"]}
|
|
59
|
+
/>
|
|
60
|
+
)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export default NIHSelect
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useContext } from "react"
|
|
1
|
+
import React, { useContext, useEffect } from "react"
|
|
2
2
|
import type { FC } from "react"
|
|
3
3
|
import { useLocation } from "react-router-dom"
|
|
4
4
|
import {
|
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
DiagnosisSelect,
|
|
23
23
|
KeywordInput,
|
|
24
24
|
ModalitySelect,
|
|
25
|
+
NIHSelect,
|
|
25
26
|
ScannerManufacturers,
|
|
26
27
|
ScannerManufacturersModelNames,
|
|
27
28
|
SectionSelect,
|
|
@@ -83,6 +84,7 @@ export const setDefaultSearch = (
|
|
|
83
84
|
EEG: ["EEG"],
|
|
84
85
|
iEEG: ["iEEG"],
|
|
85
86
|
MEG: ["MEG"],
|
|
87
|
+
NIH: ["NIH"],
|
|
86
88
|
}
|
|
87
89
|
if (
|
|
88
90
|
modality &&
|
|
@@ -106,12 +108,16 @@ const SearchContainer: FC<SearchContainerProps> = ({ portalContent }) => {
|
|
|
106
108
|
|
|
107
109
|
const { searchParams, setSearchParams } = useContext(SearchParamsCtx)
|
|
108
110
|
const modality = portalContent?.modality || null
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
111
|
+
const selected_grant = portalContent?.portalName || null
|
|
112
|
+
|
|
113
|
+
useEffect(() => {
|
|
114
|
+
setDefaultSearch(
|
|
115
|
+
modality,
|
|
116
|
+
searchParams,
|
|
117
|
+
setSearchParams,
|
|
118
|
+
new URLSearchParams(location.search),
|
|
119
|
+
)
|
|
120
|
+
}, [modality, searchParams, setSearchParams, location.search])
|
|
115
121
|
|
|
116
122
|
const { loading, data, fetchMore, variables } = useSearchResults()
|
|
117
123
|
const loadMore = loading ? () => {} : () => {
|
|
@@ -136,13 +142,14 @@ const SearchContainer: FC<SearchContainerProps> = ({ portalContent }) => {
|
|
|
136
142
|
return (
|
|
137
143
|
<>
|
|
138
144
|
<Helmet>
|
|
139
|
-
<title>OpenNeuro - {
|
|
145
|
+
<title>OpenNeuro - {modality || selected_grant || ""} Search</title>
|
|
140
146
|
</Helmet>
|
|
141
147
|
<SearchPage
|
|
142
148
|
portalContent={portalContent}
|
|
143
|
-
renderAggregateCounts={() =>
|
|
144
|
-
|
|
145
|
-
|
|
149
|
+
renderAggregateCounts={() =>
|
|
150
|
+
portalContent.modality
|
|
151
|
+
? <AggregateCountsContainer modality={portalContent.modality} />
|
|
152
|
+
: null}
|
|
146
153
|
renderFilterBlock={() => (
|
|
147
154
|
<FiltersBlockContainer
|
|
148
155
|
loading={loading}
|
|
@@ -153,12 +160,13 @@ const SearchContainer: FC<SearchContainerProps> = ({ portalContent }) => {
|
|
|
153
160
|
renderSearchHeader={() => (
|
|
154
161
|
<>
|
|
155
162
|
{portalContent
|
|
156
|
-
? "Search " + modality + " Portal"
|
|
163
|
+
? "Search " + (modality || selected_grant || "") + " Portal"
|
|
157
164
|
: "Search All Datasets"}
|
|
158
165
|
</>
|
|
159
166
|
)}
|
|
160
167
|
renderSearchFacets={() => (
|
|
161
168
|
<>
|
|
169
|
+
<NIHSelect label={"Search NIH Brain Initiative Datasets"} />
|
|
162
170
|
<NeurobagelSearch />
|
|
163
171
|
<KeywordInput />
|
|
164
172
|
<AdminUser>
|
|
@@ -128,6 +128,7 @@ export const getSelectParams = ({
|
|
|
128
128
|
tracerNames,
|
|
129
129
|
tracerRadionuclides,
|
|
130
130
|
bidsDatasetType_selected,
|
|
131
|
+
brain_initiative,
|
|
131
132
|
}) => ({
|
|
132
133
|
keywords,
|
|
133
134
|
modality_selected,
|
|
@@ -150,6 +151,7 @@ export const getSelectParams = ({
|
|
|
150
151
|
tracerNames,
|
|
151
152
|
tracerRadionuclides,
|
|
152
153
|
bidsDatasetType_selected,
|
|
154
|
+
brain_initiative,
|
|
153
155
|
})
|
|
154
156
|
|
|
155
157
|
/**
|
|
@@ -2,31 +2,39 @@ import React from "react"
|
|
|
2
2
|
import type { FC } from "react"
|
|
3
3
|
import { Route, Routes } from "react-router-dom"
|
|
4
4
|
import SearchContainer from "./search-container"
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
portalContent,
|
|
7
|
+
portalGrantContent,
|
|
8
|
+
} from "@openneuro/components/content"
|
|
6
9
|
|
|
7
10
|
const SearchRoutes: FC = () => (
|
|
8
11
|
<Routes>
|
|
9
12
|
<Route path="/" element={<SearchContainer />} />
|
|
10
13
|
<Route
|
|
11
|
-
path="modality/mri"
|
|
14
|
+
path="/modality/mri"
|
|
12
15
|
element={<SearchContainer portalContent={portalContent.mri} />}
|
|
13
16
|
/>
|
|
14
17
|
<Route
|
|
15
|
-
path="modality/eeg"
|
|
18
|
+
path="/modality/eeg"
|
|
16
19
|
element={<SearchContainer portalContent={portalContent.eeg} />}
|
|
17
20
|
/>
|
|
18
21
|
<Route
|
|
19
|
-
path="modality/ieeg"
|
|
22
|
+
path="/modality/ieeg"
|
|
20
23
|
element={<SearchContainer portalContent={portalContent.ieeg} />}
|
|
21
24
|
/>
|
|
22
25
|
<Route
|
|
23
|
-
path="modality/meg"
|
|
26
|
+
path="/modality/meg"
|
|
24
27
|
element={<SearchContainer portalContent={portalContent.meg} />}
|
|
25
28
|
/>
|
|
26
29
|
<Route
|
|
27
|
-
path="modality/pet"
|
|
30
|
+
path="/modality/pet"
|
|
28
31
|
element={<SearchContainer portalContent={portalContent.pet} />}
|
|
29
32
|
/>
|
|
33
|
+
<Route
|
|
34
|
+
path="/nih"
|
|
35
|
+
element={<SearchContainer portalContent={portalGrantContent.nih} />}
|
|
36
|
+
/>
|
|
30
37
|
</Routes>
|
|
31
38
|
)
|
|
39
|
+
|
|
32
40
|
export default SearchRoutes
|
|
@@ -87,6 +87,10 @@ const searchQuery = gql`
|
|
|
87
87
|
issues {
|
|
88
88
|
severity
|
|
89
89
|
}
|
|
90
|
+
validation {
|
|
91
|
+
errors
|
|
92
|
+
warnings
|
|
93
|
+
}
|
|
90
94
|
description {
|
|
91
95
|
Name
|
|
92
96
|
Authors
|
|
@@ -150,6 +154,7 @@ export const useSearchResults = () => {
|
|
|
150
154
|
tracerRadionuclides,
|
|
151
155
|
sortBy_selected,
|
|
152
156
|
bidsDatasetType_selected,
|
|
157
|
+
brain_initiative,
|
|
153
158
|
} = searchParams
|
|
154
159
|
|
|
155
160
|
const boolQuery = new BoolQuery()
|
|
@@ -206,6 +211,7 @@ export const useSearchResults = () => {
|
|
|
206
211
|
)
|
|
207
212
|
}
|
|
208
213
|
}
|
|
214
|
+
|
|
209
215
|
if (isActiveRange(ageRange)) {
|
|
210
216
|
boolQuery.addClause(
|
|
211
217
|
"filter",
|
|
@@ -237,6 +243,15 @@ export const useSearchResults = () => {
|
|
|
237
243
|
),
|
|
238
244
|
)
|
|
239
245
|
}
|
|
246
|
+
if (brain_initiative) {
|
|
247
|
+
boolQuery.addClause(
|
|
248
|
+
"filter",
|
|
249
|
+
matchQuery(
|
|
250
|
+
"brainInitiative",
|
|
251
|
+
brain_initiative,
|
|
252
|
+
),
|
|
253
|
+
)
|
|
254
|
+
}
|
|
240
255
|
if (tasks.length) {
|
|
241
256
|
boolQuery.addClause(
|
|
242
257
|
"must",
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
export interface User {
|
|
2
|
+
id: string
|
|
3
|
+
name: string
|
|
4
|
+
location?: string
|
|
5
|
+
github?: string
|
|
6
|
+
institution?: string
|
|
7
|
+
email: string
|
|
8
|
+
avatar?: string
|
|
9
|
+
orcid?: string
|
|
10
|
+
links?: string[]
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface UserRoutesProps {
|
|
14
|
+
user: User
|
|
15
|
+
hasEdit: boolean
|
|
16
|
+
}
|
|
17
|
+
export interface UserCardProps {
|
|
18
|
+
user: User
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface UserAccountViewProps {
|
|
22
|
+
user: {
|
|
23
|
+
name: string
|
|
24
|
+
email: string
|
|
25
|
+
orcid?: string
|
|
26
|
+
links?: string[]
|
|
27
|
+
location?: string
|
|
28
|
+
institution?: string
|
|
29
|
+
github?: string
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface Dataset {
|
|
34
|
+
id: string
|
|
35
|
+
created: string
|
|
36
|
+
name: string
|
|
37
|
+
public: boolean
|
|
38
|
+
analytics: {
|
|
39
|
+
views: number
|
|
40
|
+
downloads: number
|
|
41
|
+
}
|
|
42
|
+
stars?: { userId: string; datasetId: string }[]
|
|
43
|
+
followers?: { userId: string; datasetId: string }[]
|
|
44
|
+
latestSnapshot?: {
|
|
45
|
+
id: string
|
|
46
|
+
size: number
|
|
47
|
+
issues: { severity: string }[]
|
|
48
|
+
created?: string
|
|
49
|
+
description?: {
|
|
50
|
+
Authors: string[]
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
draft?: {
|
|
54
|
+
size?: number
|
|
55
|
+
created?: string
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface DatasetCardProps {
|
|
60
|
+
dataset: Dataset
|
|
61
|
+
hasEdit: boolean
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface UserDatasetsViewProps {
|
|
65
|
+
user: User
|
|
66
|
+
hasEdit: boolean
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface AccountContainerProps {
|
|
70
|
+
user: User
|
|
71
|
+
hasEdit: boolean
|
|
72
|
+
}
|