@openneuro/app 4.38.0-alpha.0 → 4.38.0-alpha.2
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 +3 -3
- package/src/scripts/datalad/dataset/dataset-query-fragments.js +12 -0
- package/src/scripts/dataset/fragments/file-check-list.tsx +37 -0
- package/src/scripts/dataset/fragments/scss/file-check-list.module.scss +79 -0
- package/src/scripts/dataset/routes/snapshot.tsx +20 -3
- package/src/scripts/dataset/routes/tab-routes-draft.tsx +1 -0
- package/src/scripts/search/initial-search-params.tsx +2 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openneuro/app",
|
|
3
|
-
"version": "4.38.0-alpha.
|
|
3
|
+
"version": "4.38.0-alpha.2",
|
|
4
4
|
"description": "React JS web frontend for the OpenNeuro platform.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "public/client.js",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"dependencies": {
|
|
17
17
|
"@apollo/client": "3.13.8",
|
|
18
18
|
"@artsy/fresnel": "^1.3.1",
|
|
19
|
-
"@bids/validator": "npm:@jsr/bids__validator@^2.0.
|
|
19
|
+
"@bids/validator": "npm:@jsr/bids__validator@^2.0.8",
|
|
20
20
|
"@emotion/react": "11.11.1",
|
|
21
21
|
"@emotion/styled": "11.11.0",
|
|
22
22
|
"@niivue/niivue": "0.57.0",
|
|
@@ -77,5 +77,5 @@
|
|
|
77
77
|
"publishConfig": {
|
|
78
78
|
"access": "public"
|
|
79
79
|
},
|
|
80
|
-
"gitHead": "
|
|
80
|
+
"gitHead": "a2a301d754891d76a04c1ae91fde268419f0b5ca"
|
|
81
81
|
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
import styles from "./scss/file-check-list.module.scss"
|
|
3
|
+
|
|
4
|
+
export const FileCheckList = ({ fileCheck }) => {
|
|
5
|
+
if (!fileCheck) return null
|
|
6
|
+
return (
|
|
7
|
+
<div className={styles["fileCheck-container"]}>
|
|
8
|
+
{fileCheck.annexFsck.map((item, index) => (
|
|
9
|
+
<div key={index} className={styles["fileCheck-card"]}>
|
|
10
|
+
<div className={styles["fileCheck-header"]}>
|
|
11
|
+
<span className={styles["fileCheck-fileIcon"]}>
|
|
12
|
+
<i className="fas fa-file"></i>
|
|
13
|
+
</span>
|
|
14
|
+
<span className={styles["fileCheck-fileName"]}>{item.file}</span>
|
|
15
|
+
</div>
|
|
16
|
+
<div className={styles["fileCheck-body"]}>
|
|
17
|
+
<div className={styles["fileCheck-key"]}>
|
|
18
|
+
<span className={styles["fileCheck-label"]}>Key:</span>
|
|
19
|
+
<span className={styles["fileCheck-value"]}>{item.key}</span>
|
|
20
|
+
</div>
|
|
21
|
+
{item.errorMessages && item.errorMessages.length > 0 && (
|
|
22
|
+
<div className={styles["fileCheck-errors"]}>
|
|
23
|
+
<span className={styles["fileCheck-errorIcon"]}>
|
|
24
|
+
<i className="fas fa-exclamation"></i>
|
|
25
|
+
</span>
|
|
26
|
+
<span className={styles["fileCheck-label"]}>Errors:</span>
|
|
27
|
+
<ul className={styles["fileCheck-errorList"]}>
|
|
28
|
+
{item.errorMessages.map((msg, i) => <li key={i}>{msg}</li>)}
|
|
29
|
+
</ul>
|
|
30
|
+
</div>
|
|
31
|
+
)}
|
|
32
|
+
</div>
|
|
33
|
+
</div>
|
|
34
|
+
))}
|
|
35
|
+
</div>
|
|
36
|
+
)
|
|
37
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
$error: #B04E4B;
|
|
2
|
+
$offWhite: #e0e0e0;
|
|
3
|
+
$midGray_A: #f7f7f768;
|
|
4
|
+
$errorBG_A: #ffdfde5e;
|
|
5
|
+
|
|
6
|
+
.fileCheck-container {
|
|
7
|
+
display: flex;
|
|
8
|
+
flex-direction: column;
|
|
9
|
+
gap: 1rem;
|
|
10
|
+
font-family: Arial, sans-serif;
|
|
11
|
+
margin-top: 20px;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.fileCheck-card {
|
|
15
|
+
background-color: #ffffff;
|
|
16
|
+
border: 1px solid $offWhite;
|
|
17
|
+
overflow: hidden;
|
|
18
|
+
transition: transform 0.2s;
|
|
19
|
+
&:hover {
|
|
20
|
+
background-color: #faf3f3;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.fileCheck-header {
|
|
26
|
+
display: flex;
|
|
27
|
+
align-items: center;
|
|
28
|
+
padding: 12px 16px;
|
|
29
|
+
background-color: $midGray_A;
|
|
30
|
+
border-bottom: 1px solid $offWhite;
|
|
31
|
+
|
|
32
|
+
.fileCheck-fileIcon {
|
|
33
|
+
margin-right: 8px;
|
|
34
|
+
font-size: 1.2rem;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.fileCheck-fileName {
|
|
38
|
+
font-weight: bold;
|
|
39
|
+
color: #333;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.fileCheck-body {
|
|
44
|
+
padding: 0;
|
|
45
|
+
.fileCheck-key{
|
|
46
|
+
padding: 16px;
|
|
47
|
+
}
|
|
48
|
+
.fileCheck-label {
|
|
49
|
+
font-weight: bold;
|
|
50
|
+
color: #555;
|
|
51
|
+
margin-right: 5px;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.fileCheck-value {
|
|
55
|
+
word-break: break-all;
|
|
56
|
+
color: #777;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.fileCheck-errors {
|
|
61
|
+
margin-top: 0;
|
|
62
|
+
padding: 12px;
|
|
63
|
+
background-color: $errorBG_A;
|
|
64
|
+
border-left: 4px solid $error;
|
|
65
|
+
|
|
66
|
+
.fileCheck-errorIcon {
|
|
67
|
+
color: $error;
|
|
68
|
+
margin-right: 8px;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.fileCheck-errorList {
|
|
72
|
+
margin: 0;
|
|
73
|
+
padding-left: 20px;
|
|
74
|
+
color: $error;
|
|
75
|
+
list-style-type: disc;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
|
|
@@ -9,29 +9,41 @@ import { HeaderRow4 } from "./styles/header-row"
|
|
|
9
9
|
import FileView from "../files/file-view"
|
|
10
10
|
import styled from "@emotion/styled"
|
|
11
11
|
import { apiPath } from "../files/file"
|
|
12
|
+
import { FileCheckList } from "../fragments/file-check-list"
|
|
12
13
|
|
|
13
14
|
const FormRow = styled.div`
|
|
14
15
|
margin-top: 0;
|
|
15
16
|
margin-bottom: 1.3em;
|
|
16
17
|
`
|
|
17
18
|
|
|
18
|
-
export const NoErrors = ({ validation, authors, children }) => {
|
|
19
|
+
export const NoErrors = ({ validation, authors, fileCheck, children }) => {
|
|
19
20
|
const noErrors = validation?.errors === 0
|
|
20
21
|
// zero authors will cause DOI minting to fail
|
|
21
22
|
const hasAuthor = authors?.length > 0
|
|
22
|
-
|
|
23
|
+
const fileCheckFinish = fileCheck !== null
|
|
24
|
+
const noBadFiles = fileCheck?.annexFsck?.length === 0
|
|
25
|
+
if (noBadFiles && noErrors && hasAuthor) {
|
|
23
26
|
return children
|
|
24
27
|
} else {
|
|
25
28
|
const correctErrorsMessage =
|
|
26
29
|
"BIDS validation must be complete and all errors corrected"
|
|
27
30
|
const noAuthorMessage =
|
|
28
31
|
'"Authors" must include at least one entry in dataset_description.json'
|
|
32
|
+
const fileChecksPendingMessage =
|
|
33
|
+
"file integrity checks are pending and may take a few minutes to complete. Please wait a few minutes"
|
|
34
|
+
const badFilesMessage =
|
|
35
|
+
"one or more files in the most recent draft are missing or do not match checksums. Please reupload any files listed below"
|
|
29
36
|
const includedMessages = []
|
|
30
37
|
if (!noErrors) includedMessages.push(correctErrorsMessage)
|
|
31
38
|
if (!hasAuthor) includedMessages.push(noAuthorMessage)
|
|
39
|
+
if (!fileCheckFinish) includedMessages.push(fileChecksPendingMessage)
|
|
40
|
+
if (fileCheckFinish && !noBadFiles) includedMessages.push(badFilesMessage)
|
|
32
41
|
return (
|
|
33
42
|
<span className="text-danger">
|
|
34
43
|
{`${includedMessages.join(" and ")} to create a version`}
|
|
44
|
+
{fileCheckFinish && !noBadFiles && (
|
|
45
|
+
<FileCheckList fileCheck={fileCheck} />
|
|
46
|
+
)}
|
|
35
47
|
</span>
|
|
36
48
|
)
|
|
37
49
|
}
|
|
@@ -42,6 +54,7 @@ const SnapshotRoute = ({
|
|
|
42
54
|
snapshots,
|
|
43
55
|
validation,
|
|
44
56
|
description,
|
|
57
|
+
fileCheck,
|
|
45
58
|
}): React.ReactElement => {
|
|
46
59
|
const [changes, setChanges] = useState([])
|
|
47
60
|
const [semanticLevel, setSemanticLevel] = useState("patch")
|
|
@@ -122,7 +135,11 @@ const SnapshotRoute = ({
|
|
|
122
135
|
setElements={setChanges}
|
|
123
136
|
/>
|
|
124
137
|
</div>
|
|
125
|
-
<NoErrors
|
|
138
|
+
<NoErrors
|
|
139
|
+
validation={validation}
|
|
140
|
+
authors={description.Authors}
|
|
141
|
+
fileCheck={fileCheck}
|
|
142
|
+
>
|
|
126
143
|
{changes.length ? null : (
|
|
127
144
|
<small className="text-danger">
|
|
128
145
|
You must add at least one change message to create a new version
|
|
@@ -8,6 +8,7 @@ const datasetType_available = [
|
|
|
8
8
|
]
|
|
9
9
|
|
|
10
10
|
const datasetStatus_available = [
|
|
11
|
+
{ label: "All", value: "All" },
|
|
11
12
|
{ label: "Public", value: "Public" },
|
|
12
13
|
{ label: "Shared with Me", value: "Shared with Me" },
|
|
13
14
|
{ label: "Invalid", value: "Invalid" },
|
|
@@ -172,7 +173,7 @@ const initialSearchParams: SearchParams = {
|
|
|
172
173
|
datasetType_available,
|
|
173
174
|
datasetType_selected: "All Public",
|
|
174
175
|
datasetStatus_available,
|
|
175
|
-
datasetStatus_selected:
|
|
176
|
+
datasetStatus_selected: "All",
|
|
176
177
|
modality_available,
|
|
177
178
|
modality_selected: null,
|
|
178
179
|
ageRange: [null, null],
|