@openneuro/app 4.29.8 → 4.29.9
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/@types/custom.d.ts +8 -0
- package/src/scripts/dataset/mutations/__tests__/update-permissions.spec.jsx +2 -1
- package/src/scripts/dataset/mutations/update-permissions.tsx +1 -9
- package/src/scripts/routes.tsx +4 -0
- package/src/scripts/users/__tests__/user-account-view.spec.tsx +69 -0
- package/src/scripts/users/__tests__/user-card.spec.tsx +95 -0
- package/src/scripts/users/__tests__/user-query.spec.tsx +60 -0
- package/src/scripts/users/__tests__/user-routes.spec.tsx +71 -0
- package/src/scripts/users/__tests__/user-tabs.spec.tsx +87 -0
- package/src/scripts/users/components/close-button.tsx +20 -0
- package/src/scripts/users/components/edit-button.tsx +20 -0
- package/src/scripts/users/components/edit-list.tsx +79 -0
- package/src/scripts/users/components/edit-string.tsx +49 -0
- package/src/scripts/users/components/editable-content.tsx +63 -0
- package/src/scripts/users/scss/editable-content.scss +15 -0
- package/src/scripts/users/scss/user-meta-blocks.scss +14 -0
- package/src/scripts/users/scss/useraccountview.module.scss +20 -0
- package/src/scripts/users/scss/usercard.module.scss +24 -0
- package/src/scripts/users/scss/usercontainer.module.scss +38 -0
- package/src/scripts/users/scss/usertabs.module.scss +63 -0
- package/src/scripts/users/user-account-view.tsx +62 -0
- package/src/scripts/users/user-card.tsx +84 -0
- package/src/scripts/users/user-container.tsx +48 -0
- package/src/scripts/users/user-datasets-view.tsx +53 -0
- package/src/scripts/users/user-notifications-view.tsx +12 -0
- package/src/scripts/users/user-query.tsx +80 -0
- package/src/scripts/users/user-routes.tsx +45 -0
- package/src/scripts/users/user-tabs.tsx +70 -0
- package/src/scripts/utils/validationUtils.ts +9 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import React, { useState } from "react";
|
|
2
|
+
import { EditList } from "./edit-list";
|
|
3
|
+
import { EditString } from "./edit-string";
|
|
4
|
+
import { EditButton } from "./edit-button";
|
|
5
|
+
import { CloseButton } from "./close-button";
|
|
6
|
+
import { Markdown } from "../../utils/markdown";
|
|
7
|
+
import '../scss/editable-content.scss'
|
|
8
|
+
|
|
9
|
+
interface EditableContentProps {
|
|
10
|
+
editableContent: string[] | string;
|
|
11
|
+
setRows: React.Dispatch<React.SetStateAction<string[] | string>>;
|
|
12
|
+
className: string;
|
|
13
|
+
heading: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const EditableContent: React.FC<EditableContentProps> = ({
|
|
17
|
+
editableContent,
|
|
18
|
+
setRows,
|
|
19
|
+
className,
|
|
20
|
+
heading,
|
|
21
|
+
}) => {
|
|
22
|
+
const [editing, setEditing] = useState(false);
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<div className={`user-meta-block ${className}`}>
|
|
26
|
+
<span className="umb-heading"><h4>{heading}</h4>{editing ? <CloseButton action={() => setEditing(false)} /> : <EditButton action={() => setEditing(true)} />}</span>
|
|
27
|
+
{editing ? (
|
|
28
|
+
<>
|
|
29
|
+
{Array.isArray(editableContent) ? (
|
|
30
|
+
<EditList
|
|
31
|
+
placeholder="Add new item"
|
|
32
|
+
elements={editableContent}
|
|
33
|
+
setElements={setRows as React.Dispatch<React.SetStateAction<string[]>>}
|
|
34
|
+
/>
|
|
35
|
+
) : (
|
|
36
|
+
<EditString
|
|
37
|
+
value={editableContent}
|
|
38
|
+
setValue={setRows as React.Dispatch<React.SetStateAction<string>>}
|
|
39
|
+
placeholder="Edit content"
|
|
40
|
+
/>
|
|
41
|
+
)}
|
|
42
|
+
</>
|
|
43
|
+
) : (
|
|
44
|
+
<>
|
|
45
|
+
{Array.isArray(editableContent) ? (
|
|
46
|
+
<ul>
|
|
47
|
+
{editableContent.map((item, index) => (
|
|
48
|
+
<li key={index}>
|
|
49
|
+
<Markdown>{item}</Markdown>
|
|
50
|
+
</li>
|
|
51
|
+
))}
|
|
52
|
+
</ul>
|
|
53
|
+
) : (
|
|
54
|
+
<Markdown>{editableContent}</Markdown>
|
|
55
|
+
)}
|
|
56
|
+
</>
|
|
57
|
+
)}
|
|
58
|
+
</div>
|
|
59
|
+
);
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
.useraccountview{
|
|
2
|
+
h3{
|
|
3
|
+
margin-top: 0;
|
|
4
|
+
font-size: 22px;
|
|
5
|
+
}
|
|
6
|
+
.accountDetail{
|
|
7
|
+
list-style: none;
|
|
8
|
+
margin: 0;
|
|
9
|
+
padding: 0;
|
|
10
|
+
li{
|
|
11
|
+
margin: 0 0 10px;
|
|
12
|
+
font-size: 16px;
|
|
13
|
+
}
|
|
14
|
+
span{
|
|
15
|
+
font-weight: 700;
|
|
16
|
+
margin-right: 10px;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/* usercard.module.scss */
|
|
2
|
+
.userCard {
|
|
3
|
+
padding: 20px;
|
|
4
|
+
ul{
|
|
5
|
+
list-style: none;
|
|
6
|
+
margin: 0;
|
|
7
|
+
padding: 0;
|
|
8
|
+
li{
|
|
9
|
+
margin-bottom: 5px;
|
|
10
|
+
font-size: 14px;
|
|
11
|
+
i{
|
|
12
|
+
margin-right: 10px;
|
|
13
|
+
color: #666;
|
|
14
|
+
|
|
15
|
+
}
|
|
16
|
+
&.orcid i{
|
|
17
|
+
color: #a6cf39;
|
|
18
|
+
}
|
|
19
|
+
&:last-of-type{
|
|
20
|
+
margin-bottom: 0;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
.usercontainer {
|
|
2
|
+
display: flex;
|
|
3
|
+
min-height: calc(100vh - 350px);
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
.userHeader {
|
|
7
|
+
display: flex;
|
|
8
|
+
align-items: center;
|
|
9
|
+
margin: 10px 0 0;
|
|
10
|
+
height: 100px;
|
|
11
|
+
.avatar {
|
|
12
|
+
border-radius: 50%;
|
|
13
|
+
margin-right: 10px;
|
|
14
|
+
width: 60px;
|
|
15
|
+
height: 60px;
|
|
16
|
+
}
|
|
17
|
+
.username {
|
|
18
|
+
}
|
|
19
|
+
h2 {
|
|
20
|
+
font-size: 35px;
|
|
21
|
+
line-height: 38px;
|
|
22
|
+
font-weight: 400;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.userSidebar {
|
|
27
|
+
width: 350px;
|
|
28
|
+
flex: 0 0 auto;
|
|
29
|
+
padding: 0;
|
|
30
|
+
width: 430px;
|
|
31
|
+
}
|
|
32
|
+
.userViews {
|
|
33
|
+
border-left: 1px solid #e0e0e0;
|
|
34
|
+
flex: 1;
|
|
35
|
+
padding: 0 0 0 50px;
|
|
36
|
+
position: relative;
|
|
37
|
+
max-width: 100%;
|
|
38
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
.userAccountTabLinks {
|
|
2
|
+
ul {
|
|
3
|
+
margin: 0;
|
|
4
|
+
padding: 0;
|
|
5
|
+
list-style-type: none;
|
|
6
|
+
position: relative;
|
|
7
|
+
|
|
8
|
+
&:before,
|
|
9
|
+
&:after {
|
|
10
|
+
content: '';
|
|
11
|
+
width: calc(100% - 60px);
|
|
12
|
+
height: 32px;
|
|
13
|
+
display: inline-block;
|
|
14
|
+
position: absolute;
|
|
15
|
+
left: 0;
|
|
16
|
+
top: 0;
|
|
17
|
+
transform: translateY(var(--active-offset, 0));
|
|
18
|
+
margin-left: 25px;
|
|
19
|
+
border-radius: 8px;
|
|
20
|
+
transition: transform 0.3s ease; /* Always apply transition */
|
|
21
|
+
}
|
|
22
|
+
/* Apply animation when clicked */
|
|
23
|
+
// not working for some reason
|
|
24
|
+
// &.clicked:before,
|
|
25
|
+
// &.clicked:after {
|
|
26
|
+
// transform: translateY(var(--active-offset, 0));
|
|
27
|
+
// transition: transform 0.3s ease; /* Ensure transition */
|
|
28
|
+
// }
|
|
29
|
+
|
|
30
|
+
&:before {
|
|
31
|
+
background: #eee;
|
|
32
|
+
margin: 0 20px 0 35px;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
&:after {
|
|
36
|
+
height: 23px;
|
|
37
|
+
margin: 5px 0 0px 25px;
|
|
38
|
+
width: 5px;
|
|
39
|
+
background: #204e5b;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
li {
|
|
43
|
+
margin: 10px 25px;
|
|
44
|
+
position: relative;
|
|
45
|
+
|
|
46
|
+
a {
|
|
47
|
+
padding: 5px 10px 5px 20px;
|
|
48
|
+
border-radius: 4px;
|
|
49
|
+
display: block;
|
|
50
|
+
text-decoration: none;
|
|
51
|
+
background-color: transparent;
|
|
52
|
+
transition: background-color 0.3s;
|
|
53
|
+
&.active {
|
|
54
|
+
font-weight: bold;
|
|
55
|
+
&:hover {
|
|
56
|
+
color: #204e5b;
|
|
57
|
+
cursor: default;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import React, { useState } from "react";
|
|
2
|
+
import { EditableContent } from "./components/editable-content";
|
|
3
|
+
import styles from './scss/useraccountview.module.scss'
|
|
4
|
+
|
|
5
|
+
interface UserAccountViewProps {
|
|
6
|
+
user: {
|
|
7
|
+
name: string;
|
|
8
|
+
email: string;
|
|
9
|
+
orcid: string;
|
|
10
|
+
links: string[];
|
|
11
|
+
location: string;
|
|
12
|
+
institution: string;
|
|
13
|
+
github?: string;
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const UserAccountView: React.FC<UserAccountViewProps> = ({ user }) => {
|
|
18
|
+
const [userLinks, setLinks] = useState<string[]>(user.links || []);
|
|
19
|
+
const [userLocation, setLocation] = useState<string>(user.location || "");
|
|
20
|
+
const [userInstitution, setInstitution] = useState<string>(user.institution || "");
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<div data-testid="user-account-view" className={styles.useraccountview}>
|
|
24
|
+
<h3>Account</h3>
|
|
25
|
+
<ul className={styles.accountDetail}>
|
|
26
|
+
<li>
|
|
27
|
+
<span>Name:</span>
|
|
28
|
+
{user.name}
|
|
29
|
+
</li>
|
|
30
|
+
<li>
|
|
31
|
+
<span>Email:</span>
|
|
32
|
+
{user.email}
|
|
33
|
+
</li>
|
|
34
|
+
<li>
|
|
35
|
+
<span>ORCID:</span>
|
|
36
|
+
{user.orcid}
|
|
37
|
+
</li>
|
|
38
|
+
{user.github ? <li><span>github:</span>{user.github}</li> : <li>Connect your github</li>}
|
|
39
|
+
</ul>
|
|
40
|
+
|
|
41
|
+
<EditableContent
|
|
42
|
+
editableContent={userLinks}
|
|
43
|
+
setRows={setLinks}
|
|
44
|
+
className="custom-class"
|
|
45
|
+
heading="Links"
|
|
46
|
+
/>
|
|
47
|
+
<EditableContent
|
|
48
|
+
editableContent={userLocation}
|
|
49
|
+
setRows={(newLocation: string) => setLocation(newLocation)}
|
|
50
|
+
className="custom-class"
|
|
51
|
+
heading="Location"
|
|
52
|
+
/>
|
|
53
|
+
<EditableContent
|
|
54
|
+
editableContent={userInstitution}
|
|
55
|
+
setRows={(newInstitution: string) => setInstitution(newInstitution)}
|
|
56
|
+
className="custom-class"
|
|
57
|
+
heading="Institution"
|
|
58
|
+
/>
|
|
59
|
+
</div>
|
|
60
|
+
);
|
|
61
|
+
};
|
|
62
|
+
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import styles from "./scss/usercard.module.scss";
|
|
3
|
+
|
|
4
|
+
export interface User {
|
|
5
|
+
name: string;
|
|
6
|
+
location?: string;
|
|
7
|
+
email: string;
|
|
8
|
+
orcid: string;
|
|
9
|
+
institution?: string;
|
|
10
|
+
links?: string[];
|
|
11
|
+
github?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface UserCardProps {
|
|
15
|
+
user: User;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const UserCard: React.FC<UserCardProps> = ({ user }) => {
|
|
19
|
+
const { location, institution, email, orcid, links = [], github, name } = user;
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<div className={styles.userCard}>
|
|
23
|
+
<ul>
|
|
24
|
+
{institution && (
|
|
25
|
+
<li>
|
|
26
|
+
<i className="fa fa-building"></i>
|
|
27
|
+
{institution}
|
|
28
|
+
</li>
|
|
29
|
+
)}
|
|
30
|
+
{location && (
|
|
31
|
+
<li>
|
|
32
|
+
<i className="fas fa-map-marker-alt"></i>
|
|
33
|
+
{location}
|
|
34
|
+
</li>
|
|
35
|
+
)}
|
|
36
|
+
<li>
|
|
37
|
+
<i className="fas fa-envelope"></i>
|
|
38
|
+
<a
|
|
39
|
+
href={"mailto:" + email}
|
|
40
|
+
target="_blank"
|
|
41
|
+
rel="noopener noreferrer"
|
|
42
|
+
>
|
|
43
|
+
{email}
|
|
44
|
+
</a>
|
|
45
|
+
</li>
|
|
46
|
+
<li className={styles.orcid}>
|
|
47
|
+
<i className="fab fa-orcid" aria-hidden="true"></i>
|
|
48
|
+
<a
|
|
49
|
+
href={`https://orcid.org/${orcid}`}
|
|
50
|
+
target="_blank"
|
|
51
|
+
rel="noopener noreferrer"
|
|
52
|
+
aria-label={`ORCID profile of ${name}`}
|
|
53
|
+
>
|
|
54
|
+
{orcid}
|
|
55
|
+
</a>
|
|
56
|
+
</li>
|
|
57
|
+
{github && (
|
|
58
|
+
<li>
|
|
59
|
+
<i className="fab fa-github"></i>
|
|
60
|
+
<a
|
|
61
|
+
href={`https://github.com/${github}`}
|
|
62
|
+
target="_blank"
|
|
63
|
+
rel="noopener noreferrer"
|
|
64
|
+
aria-label={`Github profile of ${name}`}
|
|
65
|
+
>
|
|
66
|
+
{github}
|
|
67
|
+
</a>
|
|
68
|
+
</li>
|
|
69
|
+
)}
|
|
70
|
+
{links.length > 0 &&
|
|
71
|
+
links
|
|
72
|
+
.filter(Boolean)
|
|
73
|
+
.map((link, index) => (
|
|
74
|
+
<li key={index}>
|
|
75
|
+
<i className="fa fa-link"></i>
|
|
76
|
+
<a href={link} target="_blank" rel="noopener noreferrer">
|
|
77
|
+
{link}
|
|
78
|
+
</a>
|
|
79
|
+
</li>
|
|
80
|
+
))}
|
|
81
|
+
</ul>
|
|
82
|
+
</div>
|
|
83
|
+
);
|
|
84
|
+
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { Outlet } from 'react-router-dom'
|
|
3
|
+
import { UserCard } from './user-card'
|
|
4
|
+
import { UserAccountTabs } from './user-tabs'
|
|
5
|
+
import styles from './scss/usercontainer.module.scss'
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
interface User {
|
|
9
|
+
id: string;
|
|
10
|
+
name: string;
|
|
11
|
+
location: string;
|
|
12
|
+
github?: string;
|
|
13
|
+
institution: string;
|
|
14
|
+
email: string;
|
|
15
|
+
avatar: string;
|
|
16
|
+
orcid: string;
|
|
17
|
+
links: string[];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface AccountContainerProps {
|
|
21
|
+
user: User;
|
|
22
|
+
hasEdit: boolean;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const UserAccountContainer: React.FC<AccountContainerProps> = ({
|
|
26
|
+
user,
|
|
27
|
+
hasEdit,
|
|
28
|
+
}) => {
|
|
29
|
+
return (
|
|
30
|
+
<>
|
|
31
|
+
<div className='container'>
|
|
32
|
+
<header className={styles.userHeader}>
|
|
33
|
+
<img className={styles.avatar} src={user.avatar} alt={user.name} />
|
|
34
|
+
<h2 className={styles.username}>{user.name}</h2>
|
|
35
|
+
</header>
|
|
36
|
+
</div>
|
|
37
|
+
<div className={styles.usercontainer + ' container'}>
|
|
38
|
+
<section className={styles.userSidebar}>
|
|
39
|
+
<UserCard user={user} />
|
|
40
|
+
<UserAccountTabs hasEdit={hasEdit} />
|
|
41
|
+
</section>
|
|
42
|
+
<section className={styles.userViews}>
|
|
43
|
+
<Outlet />
|
|
44
|
+
</section>
|
|
45
|
+
</div>
|
|
46
|
+
</>
|
|
47
|
+
)
|
|
48
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
interface User {
|
|
4
|
+
name: string;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
interface Dataset {
|
|
8
|
+
id: string;
|
|
9
|
+
created: string;
|
|
10
|
+
ownerId: string;
|
|
11
|
+
name: string;
|
|
12
|
+
type: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface UserDatasetsViewProps {
|
|
16
|
+
user: User;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const dummyDatasets: Dataset[] = [
|
|
20
|
+
{
|
|
21
|
+
id: 'ds00001',
|
|
22
|
+
created: '2023-11-01T12:00:00Z',
|
|
23
|
+
ownerId: '1',
|
|
24
|
+
name: 'Dataset 1',
|
|
25
|
+
type: 'public',
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
id: 'ds00002',
|
|
29
|
+
created: '2023-11-02T12:00:00Z',
|
|
30
|
+
ownerId: '2',
|
|
31
|
+
name: 'Dataset 2',
|
|
32
|
+
type: 'private',
|
|
33
|
+
},
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
export const UserDatasetsView: React.FC<UserDatasetsViewProps> = ({ user }) => {
|
|
37
|
+
return (
|
|
38
|
+
<div data-testid="user-datasets-view">
|
|
39
|
+
<h1>{user.name}'s Datasets</h1>
|
|
40
|
+
<div>
|
|
41
|
+
{dummyDatasets.map((dataset) => (
|
|
42
|
+
<div key={dataset.id} data-testid={`dataset-${dataset.id}`}>
|
|
43
|
+
<h2>{dataset.name}</h2>
|
|
44
|
+
<p>Type: {dataset.type}</p>
|
|
45
|
+
<p>Created: {dataset.created}</p>
|
|
46
|
+
</div>
|
|
47
|
+
))}
|
|
48
|
+
</div>
|
|
49
|
+
</div>
|
|
50
|
+
);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export default UserDatasetsView;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
export const UserNotificationsView = ({ user }) => {
|
|
4
|
+
// this is a placeholder for the user notification feature
|
|
5
|
+
return (
|
|
6
|
+
<div data-testid="user-notifications-view">
|
|
7
|
+
<h3>UserNotificationsPAge for {user.name}</h3>
|
|
8
|
+
<p>This should show user info</p>
|
|
9
|
+
</div>
|
|
10
|
+
);
|
|
11
|
+
};
|
|
12
|
+
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// UserQuery.tsx
|
|
2
|
+
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { useParams } from 'react-router-dom';
|
|
5
|
+
import { UserRoutes } from './user-routes';
|
|
6
|
+
import FourOFourPage from '../errors/404page';
|
|
7
|
+
import { isValidOrcid } from "../utils/validationUtils";
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
// Dummy user data
|
|
12
|
+
const dummyUsers: Record<string, User> = {
|
|
13
|
+
'0000-0001-6755-0259': {
|
|
14
|
+
id: '1',
|
|
15
|
+
name: 'Gregory Noack',
|
|
16
|
+
location: 'Stanford, CA',
|
|
17
|
+
github: 'thinknoack',
|
|
18
|
+
institution: 'Stanford University',
|
|
19
|
+
email: 'gregorynoack@thinknoack.com',
|
|
20
|
+
avatar: 'https://dummyimage.com/200x200/000/fff',
|
|
21
|
+
orcid: '0000-0001-6755-0259',
|
|
22
|
+
links: ['onelink.com', 'https://www.twolink.com'],
|
|
23
|
+
},
|
|
24
|
+
'0000-0002-1234-5678': {
|
|
25
|
+
id: '2',
|
|
26
|
+
name: 'Jane Doe',
|
|
27
|
+
location: 'Stanford, CA',
|
|
28
|
+
institution: 'Stanford University',
|
|
29
|
+
email: 'janedoe@example.com',
|
|
30
|
+
avatar: 'https://dummyimage.com/200x200/000/fff',
|
|
31
|
+
orcid: '0000-0002-1234-5678',
|
|
32
|
+
links: ['onelink.com', 'https://www.twolink.com'],
|
|
33
|
+
},
|
|
34
|
+
'0000-0003-2345-6789': {
|
|
35
|
+
id: '3',
|
|
36
|
+
name: 'John Smith',
|
|
37
|
+
location: 'Stanford, CA',
|
|
38
|
+
institution: 'Stanford University',
|
|
39
|
+
email: 'johnsmith@example.com',
|
|
40
|
+
avatar: 'https://dummyimage.com/200x200/000/fff',
|
|
41
|
+
orcid: '0000-0003-2345-6789',
|
|
42
|
+
links: ['onelink.com', 'https://www.twolink.com'],
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
export interface User {
|
|
49
|
+
id: string;
|
|
50
|
+
name: string;
|
|
51
|
+
location: string;
|
|
52
|
+
github?: string;
|
|
53
|
+
institution: string;
|
|
54
|
+
email: string;
|
|
55
|
+
avatar: string;
|
|
56
|
+
orcid: string;
|
|
57
|
+
links: string[];
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export const UserQuery: React.FC = () => {
|
|
61
|
+
const { orcid } = useParams<{ orcid: string }>();
|
|
62
|
+
|
|
63
|
+
// Validate ORCID and return 404 if invalid or missing
|
|
64
|
+
if (!orcid || !isValidOrcid(orcid)) {
|
|
65
|
+
return <FourOFourPage />;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Check if the user exists in the dummyUsers data
|
|
69
|
+
const user = dummyUsers[orcid];
|
|
70
|
+
|
|
71
|
+
if (!user) {
|
|
72
|
+
// If user is not found, render 404 page
|
|
73
|
+
return <FourOFourPage />;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Mocked for now
|
|
77
|
+
const hasEdit = true;
|
|
78
|
+
|
|
79
|
+
return <UserRoutes user={user} hasEdit={hasEdit} />;
|
|
80
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Route, Routes } from "react-router-dom";
|
|
3
|
+
import { UserAccountContainer } from "./user-container";
|
|
4
|
+
import { UserAccountView } from "./user-account-view";
|
|
5
|
+
import { UserNotificationsView } from "./user-notifications-view";
|
|
6
|
+
import { UserDatasetsView } from "./user-datasets-view";
|
|
7
|
+
import FourOFourPage from "../errors/404page";
|
|
8
|
+
import FourOThreePage from "../errors/403page";
|
|
9
|
+
|
|
10
|
+
export interface User {
|
|
11
|
+
id: string;
|
|
12
|
+
name: string;
|
|
13
|
+
location: string;
|
|
14
|
+
github?: string;
|
|
15
|
+
institution: string;
|
|
16
|
+
email: string;
|
|
17
|
+
avatar: string;
|
|
18
|
+
orcid: string;
|
|
19
|
+
links: string[];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface UserRoutesProps {
|
|
23
|
+
user: User;
|
|
24
|
+
hasEdit: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const UserRoutes: React.FC<UserRoutesProps> = ({ user, hasEdit }) => {
|
|
28
|
+
return (
|
|
29
|
+
<Routes>
|
|
30
|
+
<Route path="/*" element={<FourOFourPage />} />
|
|
31
|
+
<Route path="*" element={<UserAccountContainer user={user} hasEdit={hasEdit} />}>
|
|
32
|
+
<Route path="" element={<UserDatasetsView user={user} />} />
|
|
33
|
+
<Route
|
|
34
|
+
path="account"
|
|
35
|
+
element={hasEdit ? <UserAccountView user={user} /> : <FourOThreePage />}
|
|
36
|
+
/>
|
|
37
|
+
<Route
|
|
38
|
+
path="notifications"
|
|
39
|
+
element={hasEdit ? <UserNotificationsView user={user} /> : <FourOThreePage />}
|
|
40
|
+
/>
|
|
41
|
+
<Route path="*" element={<FourOFourPage />} />
|
|
42
|
+
</Route>
|
|
43
|
+
</Routes>
|
|
44
|
+
);
|
|
45
|
+
};
|