@openneuro/app 4.34.2 → 4.35.0-alpha.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.
- package/package.json +3 -3
- package/src/scripts/authentication/profile.ts +3 -5
- package/src/scripts/common/containers/header.tsx +1 -2
- package/src/scripts/common/partials/freshdesk-widget.jsx +5 -1
- package/src/scripts/components/header/Header.tsx +4 -10
- package/src/scripts/components/header/LandingExpandedHeader.tsx +11 -9
- package/src/scripts/components/header/header.scss +4 -2
- package/src/scripts/components/logo/Logo.tsx +1 -1
- package/src/scripts/components/modal/UserLoginModal.tsx +12 -11
- package/src/scripts/components/modal/__tests__/UserLoginModal.spec.tsx +1 -1
- package/src/scripts/components/page/Page.tsx +1 -2
- package/src/scripts/components/search-page/SearchResultItem.tsx +14 -13
- package/src/scripts/components/search-page/SearchResultsList.tsx +0 -19
- package/src/scripts/dataset/draft-container.tsx +0 -1
- package/src/scripts/dataset/snapshot-container.tsx +13 -11
- package/src/scripts/errors/freshdesk-widget.jsx +5 -1
- package/src/scripts/index.tsx +15 -2
- package/src/scripts/pages/__tests__/orcid-link.spec.tsx +13 -0
- package/src/scripts/pages/orcid-link.tsx +60 -0
- package/src/scripts/queries/user.ts +71 -0
- package/src/scripts/routes.tsx +2 -0
- package/src/scripts/scss/variables.scss +13 -9
- package/src/scripts/search/search-container.tsx +0 -9
- package/src/scripts/types/user-types.ts +2 -0
- package/src/scripts/users/__tests__/user-account-view.spec.tsx +36 -22
- package/src/scripts/users/__tests__/user-query.spec.tsx +3 -3
- package/src/scripts/users/__tests__/user-routes.spec.tsx +28 -11
- package/src/scripts/users/__tests__/user-tabs.spec.tsx +12 -9
- package/src/scripts/users/scss/user-menu.scss +133 -0
- package/src/scripts/users/scss/usernotifications.module.scss +1 -1
- package/src/scripts/users/user-account-view.tsx +35 -21
- package/src/scripts/users/user-container.tsx +2 -1
- package/src/scripts/users/user-menu.tsx +114 -0
- package/src/scripts/users/user-notification-accordion.tsx +2 -2
- package/src/scripts/users/user-query.tsx +6 -36
- package/src/scripts/users/user-routes.tsx +7 -5
- package/src/scripts/users/user-tabs.tsx +4 -3
- package/src/scripts/validation/__tests__/__snapshots__/validation-issues.spec.tsx.snap +10 -1
- package/src/scripts/validation/validation-issues.tsx +7 -3
- package/src/scripts/components/user/UserMenu.tsx +0 -72
- package/src/scripts/components/user/user-menu.scss +0 -88
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
import { Link } from "react-router-dom"
|
|
3
|
+
import { Dropdown } from "../components/dropdown/Dropdown"
|
|
4
|
+
import { useUser } from "../queries/user"
|
|
5
|
+
import "./scss/user-menu.scss"
|
|
6
|
+
|
|
7
|
+
export interface UserMenuProps {
|
|
8
|
+
signOutAndRedirect: () => void
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const UserMenu = (
|
|
12
|
+
{ signOutAndRedirect }: UserMenuProps,
|
|
13
|
+
) => {
|
|
14
|
+
//const inboxCount = 99
|
|
15
|
+
|
|
16
|
+
const { user } = useUser()
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<span className="user-menu-wrap">
|
|
20
|
+
{
|
|
21
|
+
/* {user?.orcid && (
|
|
22
|
+
<span className="notifications-link">
|
|
23
|
+
<Link to={`/user/${user?.orcid}/notifications/unread`}>
|
|
24
|
+
<i className="fa fa-inbox">
|
|
25
|
+
{inboxCount > 0 && (
|
|
26
|
+
<span className="count">
|
|
27
|
+
{inboxCount > 99
|
|
28
|
+
? (
|
|
29
|
+
<span>
|
|
30
|
+
99<span>+</span>
|
|
31
|
+
</span>
|
|
32
|
+
)
|
|
33
|
+
: inboxCount}
|
|
34
|
+
</span>
|
|
35
|
+
)}
|
|
36
|
+
</i>
|
|
37
|
+
<span className="sr-only">Account Info</span>
|
|
38
|
+
</Link>
|
|
39
|
+
</span>
|
|
40
|
+
)} */
|
|
41
|
+
}
|
|
42
|
+
<Dropdown
|
|
43
|
+
className={"user-menu-dropdown"}
|
|
44
|
+
label={user?.avatar
|
|
45
|
+
? (
|
|
46
|
+
<img
|
|
47
|
+
className="user-menu-label avatar"
|
|
48
|
+
src={user?.avatar}
|
|
49
|
+
alt="User Avatar"
|
|
50
|
+
/>
|
|
51
|
+
)
|
|
52
|
+
: <div className="user-menu-label">My Account</div>}
|
|
53
|
+
children={
|
|
54
|
+
<div className="user-menu-dropdown-list">
|
|
55
|
+
<ul>
|
|
56
|
+
<li className="dropdown-header">
|
|
57
|
+
<p>
|
|
58
|
+
<span>Hello</span> <br />
|
|
59
|
+
{user?.name} <br />
|
|
60
|
+
{user?.email}
|
|
61
|
+
</p>
|
|
62
|
+
<p>
|
|
63
|
+
<span>signed in via {user?.provider}</span>
|
|
64
|
+
</p>
|
|
65
|
+
</li>
|
|
66
|
+
<li>
|
|
67
|
+
{
|
|
68
|
+
/* {user?.orcid
|
|
69
|
+
? <Link to={`/user/${user?.orcid}`}>My Datasets</Link>
|
|
70
|
+
: <Link to="/search?mydatasets">My Datasets</Link>} */
|
|
71
|
+
}
|
|
72
|
+
<Link to="/search?mydatasets">My Datasets</Link>
|
|
73
|
+
</li>
|
|
74
|
+
|
|
75
|
+
{
|
|
76
|
+
/* {user?.orcid && (
|
|
77
|
+
<li>
|
|
78
|
+
<Link to={`/user/${user?.orcid}/account`}>
|
|
79
|
+
Account Info
|
|
80
|
+
</Link>
|
|
81
|
+
</li>
|
|
82
|
+
)} */
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
<li className="user-menu-link">
|
|
86
|
+
<Link to="/keygen">Obtain an API Key</Link>
|
|
87
|
+
</li>
|
|
88
|
+
{user?.provider !== "orcid" && (
|
|
89
|
+
<li className="user-menu-link">
|
|
90
|
+
<a href="/crn/auth/orcid?link=true">
|
|
91
|
+
Link ORCID to my account
|
|
92
|
+
</a>
|
|
93
|
+
</li>
|
|
94
|
+
)}
|
|
95
|
+
{user?.admin && (
|
|
96
|
+
<li className="user-menu-link">
|
|
97
|
+
<Link to="/admin">Admin</Link>
|
|
98
|
+
</li>
|
|
99
|
+
)}
|
|
100
|
+
<li className="user-menu-link">
|
|
101
|
+
<a
|
|
102
|
+
onClick={() => signOutAndRedirect()}
|
|
103
|
+
className="btn-submit-other"
|
|
104
|
+
>
|
|
105
|
+
Sign Out
|
|
106
|
+
</a>
|
|
107
|
+
</li>
|
|
108
|
+
</ul>
|
|
109
|
+
</div>
|
|
110
|
+
}
|
|
111
|
+
/>
|
|
112
|
+
</span>
|
|
113
|
+
)
|
|
114
|
+
}
|
|
@@ -84,7 +84,7 @@ export const NotificationAccordion = ({ notification, onUpdate }) => {
|
|
|
84
84
|
>
|
|
85
85
|
<img
|
|
86
86
|
className={`${styles.accordionicon} ${styles.saveicon}`}
|
|
87
|
-
src={
|
|
87
|
+
src={iconUnread}
|
|
88
88
|
alt=""
|
|
89
89
|
/>
|
|
90
90
|
<span className="sr-only">Save</span>
|
|
@@ -114,7 +114,7 @@ export const NotificationAccordion = ({ notification, onUpdate }) => {
|
|
|
114
114
|
>
|
|
115
115
|
<img
|
|
116
116
|
className={`${styles.accordionicon} ${styles.unreadicon}`}
|
|
117
|
-
src={
|
|
117
|
+
src={iconSaved}
|
|
118
118
|
alt=""
|
|
119
119
|
/>
|
|
120
120
|
<span className="sr-only">Mark as Unread</span>
|
|
@@ -3,45 +3,15 @@ import { useParams } from "react-router-dom"
|
|
|
3
3
|
import { UserRoutes } from "./user-routes"
|
|
4
4
|
import FourOFourPage from "../errors/404page"
|
|
5
5
|
import { isValidOrcid } from "../utils/validationUtils"
|
|
6
|
-
import { gql, useQuery } from "@apollo/client"
|
|
7
6
|
import { isAdmin } from "../authentication/admin-user"
|
|
8
7
|
import { useCookies } from "react-cookie"
|
|
9
8
|
import { getProfile } from "../authentication/profile"
|
|
10
|
-
|
|
11
|
-
// GraphQL query to fetch user by ORCID
|
|
12
|
-
export const GET_USER_BY_ORCID = gql`
|
|
13
|
-
query User($userId: ID!) {
|
|
14
|
-
user(id: $userId) {
|
|
15
|
-
id
|
|
16
|
-
name
|
|
17
|
-
orcid
|
|
18
|
-
email
|
|
19
|
-
avatar
|
|
20
|
-
location
|
|
21
|
-
institution
|
|
22
|
-
links
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
`
|
|
26
|
-
|
|
27
|
-
export const UPDATE_USER = gql`
|
|
28
|
-
mutation updateUser($id: ID!, $location: String, $links: [String], $institution: String) {
|
|
29
|
-
updateUser(id: $id, location: $location, links: $links, institution: $institution) {
|
|
30
|
-
id
|
|
31
|
-
location
|
|
32
|
-
links
|
|
33
|
-
institution
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
`
|
|
9
|
+
import { useUser } from "../queries/user"
|
|
37
10
|
|
|
38
11
|
export const UserQuery: React.FC = () => {
|
|
39
12
|
const { orcid } = useParams()
|
|
40
13
|
const isOrcidValid = orcid && isValidOrcid(orcid)
|
|
41
|
-
const {
|
|
42
|
-
variables: { userId: orcid },
|
|
43
|
-
skip: !isOrcidValid,
|
|
44
|
-
})
|
|
14
|
+
const { user, loading, error } = useUser()
|
|
45
15
|
|
|
46
16
|
const [cookies] = useCookies()
|
|
47
17
|
const profile = getProfile(cookies)
|
|
@@ -53,16 +23,16 @@ export const UserQuery: React.FC = () => {
|
|
|
53
23
|
|
|
54
24
|
if (loading) return <div>Loading...</div>
|
|
55
25
|
|
|
56
|
-
if (error || !
|
|
26
|
+
if (error || !user || user?.orcid !== orcid) {
|
|
57
27
|
return <FourOFourPage />
|
|
58
28
|
}
|
|
59
29
|
|
|
60
30
|
if (!profile || !profile.sub) {
|
|
61
31
|
return <FourOFourPage />
|
|
62
32
|
}
|
|
63
|
-
|
|
64
33
|
// is admin or profile matches id from the user data being returned
|
|
65
|
-
const
|
|
34
|
+
const isUser = (user?.id === profile?.sub) ? true : false
|
|
35
|
+
const hasEdit = isAdminUser || (user?.id === profile?.sub) ? true : false
|
|
66
36
|
// Render user data with UserRoutes
|
|
67
|
-
return <UserRoutes user={
|
|
37
|
+
return <UserRoutes user={user} hasEdit={hasEdit} isUser={isUser} />
|
|
68
38
|
}
|
|
@@ -14,13 +14,17 @@ import {
|
|
|
14
14
|
|
|
15
15
|
import type { UserRoutesProps } from "../types/user-types"
|
|
16
16
|
|
|
17
|
-
export const UserRoutes: React.FC<UserRoutesProps> = (
|
|
17
|
+
export const UserRoutes: React.FC<UserRoutesProps> = (
|
|
18
|
+
{ user, hasEdit, isUser },
|
|
19
|
+
) => {
|
|
18
20
|
return (
|
|
19
21
|
<Routes>
|
|
20
22
|
<Route path="/*" element={<FourOFourPage />} />
|
|
21
23
|
<Route
|
|
22
24
|
path="*"
|
|
23
|
-
element={
|
|
25
|
+
element={
|
|
26
|
+
<UserAccountContainer user={user} hasEdit={hasEdit} isUser={isUser} />
|
|
27
|
+
}
|
|
24
28
|
>
|
|
25
29
|
<Route
|
|
26
30
|
path=""
|
|
@@ -28,9 +32,7 @@ export const UserRoutes: React.FC<UserRoutesProps> = ({ user, hasEdit }) => {
|
|
|
28
32
|
/>
|
|
29
33
|
<Route
|
|
30
34
|
path="account"
|
|
31
|
-
element={hasEdit
|
|
32
|
-
? <UserAccountView user={user} />
|
|
33
|
-
: <FourOThreePage />}
|
|
35
|
+
element={hasEdit ? <UserAccountView /> : <FourOThreePage />}
|
|
34
36
|
/>
|
|
35
37
|
<Route
|
|
36
38
|
path="notifications/*"
|
|
@@ -4,10 +4,11 @@ import styles from "./scss/usertabs.module.scss"
|
|
|
4
4
|
|
|
5
5
|
export interface UserAccountTabsProps {
|
|
6
6
|
hasEdit: boolean
|
|
7
|
+
isUser?: boolean
|
|
7
8
|
}
|
|
8
9
|
|
|
9
10
|
export const UserAccountTabs: React.FC<UserAccountTabsProps> = (
|
|
10
|
-
{ hasEdit },
|
|
11
|
+
{ hasEdit, isUser },
|
|
11
12
|
) => {
|
|
12
13
|
const ulRef = useRef<HTMLUListElement>(null)
|
|
13
14
|
const [activePosition, setActivePosition] = useState<number>(0)
|
|
@@ -46,7 +47,7 @@ export const UserAccountTabs: React.FC<UserAccountTabsProps> = (
|
|
|
46
47
|
className={({ isActive }) => (isActive ? styles.active : "")}
|
|
47
48
|
onClick={handleClick}
|
|
48
49
|
>
|
|
49
|
-
User Datasets
|
|
50
|
+
{isUser ? "My" : "User"} Datasets
|
|
50
51
|
</NavLink>
|
|
51
52
|
</li>
|
|
52
53
|
<li>
|
|
@@ -56,7 +57,7 @@ export const UserAccountTabs: React.FC<UserAccountTabsProps> = (
|
|
|
56
57
|
className={({ isActive }) => (isActive ? styles.active : "")}
|
|
57
58
|
onClick={handleClick}
|
|
58
59
|
>
|
|
59
|
-
|
|
60
|
+
Notifications
|
|
60
61
|
</NavLink>
|
|
61
62
|
</li>
|
|
62
63
|
<li>
|
|
@@ -9,8 +9,17 @@ exports[`Issue component > renders one issue 1`] = `
|
|
|
9
9
|
class="e-meta"
|
|
10
10
|
>
|
|
11
11
|
<label>
|
|
12
|
-
|
|
12
|
+
Location:
|
|
13
13
|
</label>
|
|
14
|
+
/dataset_description.json
|
|
15
|
+
</div>
|
|
16
|
+
<div
|
|
17
|
+
class="e-meta"
|
|
18
|
+
>
|
|
19
|
+
<label>
|
|
20
|
+
Subcode:
|
|
21
|
+
</label>
|
|
22
|
+
DatasetType
|
|
14
23
|
</div>
|
|
15
24
|
<div
|
|
16
25
|
class="e-meta"
|
|
@@ -18,15 +18,19 @@ export function Issue({ datasetIssues, issue, groupBy }: IssueProps) {
|
|
|
18
18
|
{groupBy === "location"
|
|
19
19
|
? (
|
|
20
20
|
<div className="e-meta">
|
|
21
|
-
<label>{issue.code}
|
|
22
|
-
<span>{issue.subCode ? ` - ${issue.subCode}` : ""}</span>
|
|
21
|
+
<label>Code:</label> {issue.code}
|
|
23
22
|
</div>
|
|
24
23
|
)
|
|
25
24
|
: (
|
|
26
25
|
<div className="e-meta">
|
|
27
|
-
<label>{issue.location}
|
|
26
|
+
<label>Location:</label> {issue.location}
|
|
28
27
|
</div>
|
|
29
28
|
)}
|
|
29
|
+
{issue.subCode && (
|
|
30
|
+
<div className="e-meta">
|
|
31
|
+
<label>Subcode:</label> {issue.subCode}
|
|
32
|
+
</div>
|
|
33
|
+
)}
|
|
30
34
|
<div className="e-meta">
|
|
31
35
|
<label>Rule:</label> {issue.rule}
|
|
32
36
|
</div>
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import React from "react"
|
|
2
|
-
import { Link } from "react-router-dom"
|
|
3
|
-
import { Dropdown } from "../dropdown/Dropdown"
|
|
4
|
-
import "./user-menu.scss"
|
|
5
|
-
|
|
6
|
-
export interface UserMenuProps {
|
|
7
|
-
profile: {
|
|
8
|
-
name: string
|
|
9
|
-
admin: boolean
|
|
10
|
-
email: string
|
|
11
|
-
provider: string
|
|
12
|
-
}
|
|
13
|
-
signOutAndRedirect: () => void
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export const UserMenu = ({ profile, signOutAndRedirect }: UserMenuProps) => {
|
|
17
|
-
return (
|
|
18
|
-
<Dropdown
|
|
19
|
-
className={"user-menu-dropdown"}
|
|
20
|
-
label={<div className="user-menu-label">My Account</div>}
|
|
21
|
-
children={
|
|
22
|
-
<div className="user-menu-dropdown-list">
|
|
23
|
-
<ul>
|
|
24
|
-
<li className="dropdown-header">
|
|
25
|
-
<p>
|
|
26
|
-
<span>Hello</span> <br />
|
|
27
|
-
{profile.name}
|
|
28
|
-
</p>
|
|
29
|
-
<p>
|
|
30
|
-
<span>signed in as</span>
|
|
31
|
-
<br />
|
|
32
|
-
{profile.email}
|
|
33
|
-
{" "}
|
|
34
|
-
</p>
|
|
35
|
-
<p>
|
|
36
|
-
<span>via</span>
|
|
37
|
-
<br /> {profile.provider}
|
|
38
|
-
</p>
|
|
39
|
-
</li>
|
|
40
|
-
<li>
|
|
41
|
-
<Link to="/search?mydatasets">My Datasets</Link>
|
|
42
|
-
</li>
|
|
43
|
-
<li className="user-menu-link">
|
|
44
|
-
<Link to="/keygen">Obtain an API Key</Link>
|
|
45
|
-
</li>
|
|
46
|
-
{profile.provider !== "orcid" && (
|
|
47
|
-
<li className="user-menu-link">
|
|
48
|
-
<a href="/crn/auth/orcid?link=true">
|
|
49
|
-
{" "}
|
|
50
|
-
Link ORCID to my account{" "}
|
|
51
|
-
</a>
|
|
52
|
-
</li>
|
|
53
|
-
)}
|
|
54
|
-
{profile.admin && (
|
|
55
|
-
<li className="user-menu-link">
|
|
56
|
-
<Link to="/admin">Admin</Link>
|
|
57
|
-
</li>
|
|
58
|
-
)}
|
|
59
|
-
<li className="user-menu-link">
|
|
60
|
-
<a
|
|
61
|
-
onClick={() => signOutAndRedirect()}
|
|
62
|
-
className="btn-submit-other"
|
|
63
|
-
>
|
|
64
|
-
Sign Out
|
|
65
|
-
</a>
|
|
66
|
-
</li>
|
|
67
|
-
</ul>
|
|
68
|
-
</div>
|
|
69
|
-
}
|
|
70
|
-
/>
|
|
71
|
-
)
|
|
72
|
-
}
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
@import '../scss/variables';
|
|
2
|
-
|
|
3
|
-
.user-menu-dropdown {
|
|
4
|
-
position: relative;
|
|
5
|
-
.menu {
|
|
6
|
-
box-shadow: 0px 0px 5px -4px black;
|
|
7
|
-
background-color: #fff;
|
|
8
|
-
border-radius: $border-radius-default;
|
|
9
|
-
width: 100%;
|
|
10
|
-
min-width: 250px;
|
|
11
|
-
right: 0px;
|
|
12
|
-
top: 60px;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
.user-menu-dropdown-list {
|
|
16
|
-
ul {
|
|
17
|
-
list-style: none;
|
|
18
|
-
margin: 0;
|
|
19
|
-
padding: 20px;
|
|
20
|
-
li {
|
|
21
|
-
text-align: center;
|
|
22
|
-
border-bottom: 1px solid $newspaper;
|
|
23
|
-
&.dropdown-header {
|
|
24
|
-
padding: 10px 0;
|
|
25
|
-
font-size: 14px;
|
|
26
|
-
p:first-child {
|
|
27
|
-
margin-top: 0;
|
|
28
|
-
}
|
|
29
|
-
span {
|
|
30
|
-
color: #3d3d3d;
|
|
31
|
-
font-weight: bold;
|
|
32
|
-
font-size: 12px;
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
a {
|
|
36
|
-
display: block;
|
|
37
|
-
text-decoration: none;
|
|
38
|
-
padding: 10px 0;
|
|
39
|
-
&:hover {
|
|
40
|
-
background-color: #eee;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
&:last-child {
|
|
44
|
-
border: 0;
|
|
45
|
-
margin: 0;
|
|
46
|
-
padding: 0;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
.active {
|
|
53
|
-
img {
|
|
54
|
-
opacity: 0.5;
|
|
55
|
-
}
|
|
56
|
-
i {
|
|
57
|
-
color: $on-light-aqua;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
.header-account-btn {
|
|
63
|
-
display: flex;
|
|
64
|
-
align-items: center;
|
|
65
|
-
background: #eee;
|
|
66
|
-
border-radius: $border-radius-default;
|
|
67
|
-
|
|
68
|
-
button.on-no-background.icon-text {
|
|
69
|
-
color: $on-dark-aqua;
|
|
70
|
-
margin: 5px 20px 0;
|
|
71
|
-
font-size: 16px;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
.header-account-btn .toggle {
|
|
75
|
-
color: #204e5a;
|
|
76
|
-
font-weight: bold;
|
|
77
|
-
font-size: 14px;
|
|
78
|
-
padding: 20px 24px;
|
|
79
|
-
@media (max-width: 800px) {
|
|
80
|
-
padding: 10px;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
@media (max-width: 800px) {
|
|
84
|
-
.header-account-btn .dropdown-wrapper .menu {
|
|
85
|
-
right: -25vw;
|
|
86
|
-
left: -25vw;
|
|
87
|
-
}
|
|
88
|
-
}
|