@openneuro/app 4.30.2 → 4.31.0

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.
Files changed (58) hide show
  1. package/package.json +5 -5
  2. package/src/assets/activity-icon.png +0 -0
  3. package/src/assets/icon-archived.png +0 -0
  4. package/src/assets/icon-saved.png +0 -0
  5. package/src/assets/icon-unread.png +0 -0
  6. package/src/client.jsx +1 -1
  7. package/src/scripts/datalad/dataset/dataset-query-fragments.js +4 -14
  8. package/src/scripts/dataset/__tests__/__snapshots__/snapshot-container.spec.tsx.snap +4 -304
  9. package/src/scripts/dataset/components/ValidationBlock.tsx +13 -15
  10. package/src/scripts/dataset/components/__tests__/ValidationBlock.spec.tsx +2 -0
  11. package/src/scripts/dataset/draft-container.tsx +2 -1
  12. package/src/scripts/dataset/files/__tests__/__snapshots__/file-tree.spec.jsx.snap +1 -9
  13. package/src/scripts/dataset/fragments/__tests__/{dataset-alert-draft.spec.tsx → dataset-alert.spec.tsx} +33 -1
  14. package/src/scripts/dataset/fragments/{dataset-alert-draft.tsx → dataset-alert.tsx} +30 -18
  15. package/src/scripts/dataset/routes/delete-page.tsx +72 -39
  16. package/src/scripts/dataset/routes/snapshot.tsx +23 -17
  17. package/src/scripts/dataset/routes/tab-routes-draft.tsx +5 -2
  18. package/src/scripts/dataset/snapshot-container.tsx +11 -0
  19. package/src/scripts/search/__tests__/search-params-ctx.spec.tsx +3 -0
  20. package/src/scripts/search/initial-search-params.tsx +2 -0
  21. package/src/scripts/search/inputs/__tests__/nihselect.spec.tsx +36 -0
  22. package/src/scripts/search/inputs/index.ts +2 -0
  23. package/src/scripts/search/inputs/nih-select.tsx +63 -0
  24. package/src/scripts/search/search-container.tsx +20 -12
  25. package/src/scripts/search/search-params-ctx.tsx +2 -0
  26. package/src/scripts/search/search-routes.tsx +14 -6
  27. package/src/scripts/search/use-search-results.tsx +15 -0
  28. package/src/scripts/types/user-types.ts +72 -0
  29. package/src/scripts/uploader/upload-issues.tsx +2 -2
  30. package/src/scripts/users/__tests__/datasest-card.spec.tsx +201 -0
  31. package/src/scripts/users/__tests__/user-card.spec.tsx +30 -3
  32. package/src/scripts/users/__tests__/user-query.spec.tsx +6 -0
  33. package/src/scripts/users/__tests__/user-routes.spec.tsx +42 -18
  34. package/src/scripts/users/components/user-dataset-filters.tsx +157 -0
  35. package/src/scripts/users/dataset-card.tsx +121 -0
  36. package/src/scripts/users/fragments/query.js +42 -0
  37. package/src/scripts/users/scss/datasetcard.module.scss +153 -0
  38. package/src/scripts/users/scss/usernotifications.module.scss +159 -0
  39. package/src/scripts/users/user-account-view.tsx +1 -12
  40. package/src/scripts/users/user-card.tsx +1 -14
  41. package/src/scripts/users/user-container.tsx +1 -17
  42. package/src/scripts/users/user-datasets-view.tsx +58 -43
  43. package/src/scripts/users/user-notification-accordion.tsx +160 -0
  44. package/src/scripts/users/user-notification-list.tsx +27 -0
  45. package/src/scripts/users/user-notifications-tab-content.tsx +85 -0
  46. package/src/scripts/users/user-notifications-view.tsx +102 -4
  47. package/src/scripts/users/user-query.tsx +6 -14
  48. package/src/scripts/users/user-routes.tsx +18 -19
  49. package/src/scripts/utils/__tests__/user-datasets.spec.tsx +86 -0
  50. package/src/scripts/utils/gtag.js +3 -2
  51. package/src/scripts/utils/user-datasets.tsx +60 -0
  52. package/src/scripts/validation/__tests__/__snapshots__/validation-issues.spec.tsx.snap +1 -122
  53. package/src/scripts/validation/validation-results-query.ts +44 -0
  54. package/src/scripts/validation/validation-results.tsx +31 -7
  55. package/src/scripts/validation/validation.tsx +58 -49
  56. package/src/scripts/workers/schema.worker.ts +2 -7
  57. package/tsconfig.json +1 -2
  58. 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 DeletePageProps {
13
- dataset: {
14
- permissions: Record<string, object>
15
- id: string
16
- }
8
+ interface DeletePageAction {
9
+ datasetId: string
10
+ hasEdit: boolean
17
11
  }
18
12
 
19
- const DeletePage = ({ dataset }: DeletePageProps): React.ReactElement => {
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
- <DatasetPageBorder>
37
- <HeaderRow3>Delete Dataset</HeaderRow3>
38
- <AdminUser>
39
- <DeleteDatasetForm
40
- values={values}
41
- onChange={handleInputChange}
42
- hideDisabled={false}
43
- hasEdit={hasEdit}
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
- <small className="warning-text">
47
- * Warning: this action will permanently remove this dataset along
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
- Please contact support to permanently remove a dataset and all
60
- versions of it. Provide a reason for the removal request and if the
61
- dataset is a duplicate or has been supplanted by another provide that
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
- </RegularUser>
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 = ({ issues, children }) => {
19
- const noErrors = issues &&
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 = issues &&
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
- issues,
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 this dataset to GitHub and S3 if it has
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 issues={issues}>
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 path="delete" element={<DeletePage dataset={dataset} />} />
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
- issues={dataset.draft.issues}
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
- setDefaultSearch(
110
- modality,
111
- searchParams,
112
- setSearchParams,
113
- new URLSearchParams(location.search),
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 - {portalContent ? modality : ""} Search</title>
145
+ <title>OpenNeuro - {modality || selected_grant || ""} Search</title>
140
146
  </Helmet>
141
147
  <SearchPage
142
148
  portalContent={portalContent}
143
- renderAggregateCounts={() => (
144
- <AggregateCountsContainer modality={portalContent.modality} />
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 { portalContent } from "@openneuro/components/content"
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
+ }