@openneuro/app 4.38.0-alpha.1 → 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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openneuro/app",
3
- "version": "4.38.0-alpha.1",
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",
@@ -77,5 +77,5 @@
77
77
  "publishConfig": {
78
78
  "access": "public"
79
79
  },
80
- "gitHead": "c7a1350704df419d17999699e056030b9aa0bd66"
80
+ "gitHead": "a2a301d754891d76a04c1ae91fde268419f0b5ca"
81
81
  }
@@ -9,6 +9,18 @@ export const DRAFT_FRAGMENT = gql`
9
9
  readme
10
10
  head
11
11
  size
12
+ fileCheck {
13
+ datasetId
14
+ hexsha
15
+ refs
16
+ remote
17
+ annexFsck {
18
+ errorMessages
19
+ file
20
+ key
21
+ success
22
+ }
23
+ }
12
24
  description {
13
25
  Name
14
26
  Authors
@@ -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
- if (noErrors && hasAuthor) {
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 validation={validation} authors={description.Authors}>
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
@@ -48,6 +48,7 @@ export const TabRoutesDraft = ({ dataset, hasEdit }) => {
48
48
  snapshots={dataset.snapshots}
49
49
  validation={dataset.draft.validation}
50
50
  description={dataset.draft.description}
51
+ fileCheck={dataset.draft.fileCheck}
51
52
  />
52
53
  }
53
54
  />