Skip to content

Commit 8f5d187

Browse files
authored
Merge pull request #3595 from OpenNeuroOrg/app/contributor-list-display
Api/contributor events
2 parents da1eaf5 + 80141e4 commit 8f5d187

File tree

14 files changed

+404
-221
lines changed

14 files changed

+404
-221
lines changed

packages/openneuro-app/src/scripts/dataset/components/dataset-event-item.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ export const DatasetEventItem: React.FC<DatasetEventItemProps> = ({
102102
)
103103
} else {
104104
if (event.event.type === "contributorResponse") {
105-
const statusText = event.event.status === "accepted"
105+
const statusText = event.event.resolutionStatus === "accepted"
106106
? "Accepted"
107107
: "Denied"
108108
return (
@@ -183,7 +183,7 @@ export const DatasetEventItem: React.FC<DatasetEventItemProps> = ({
183183
datasetId,
184184
requestId: event.id,
185185
targetUserId: event.user.id,
186-
status,
186+
resolutionStatus: status,
187187
reason: updatedNote.trim(),
188188
},
189189
})

packages/openneuro-app/src/scripts/queries/datasetEvents.ts

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ export const GET_DATASET_EVENTS = gql`
1717
event {
1818
type
1919
requestId
20-
status
2120
targetUserId
2221
resolutionStatus
2322
}
@@ -54,21 +53,21 @@ export const PROCESS_CONTRIBUTOR_REQUEST_MUTATION = gql`
5453
$datasetId: ID!
5554
$requestId: ID!
5655
$targetUserId: ID!
57-
$status: String!
56+
$resolutionStatus: String!
5857
$reason: String
5958
) {
6059
processContributorRequest(
6160
datasetId: $datasetId
6261
requestId: $requestId
6362
targetUserId: $targetUserId
64-
status: $status
63+
resolutionStatus: $resolutionStatus
6564
reason: $reason
6665
) {
6766
id
6867
event {
6968
type
70-
status
7169
requestId
70+
resolutionStatus
7271
}
7372
note
7473
}
@@ -117,3 +116,39 @@ export const DATASET_EVENTS_QUERY = gql`
117116
}
118117
}
119118
`
119+
120+
export const PROCESS_CONTRIBUTOR_CITATION_MUTATION = gql`
121+
mutation ProcessContributorCitation(
122+
$eventId: ID!
123+
$status: String!
124+
$reason: String
125+
) {
126+
processContributorCitation(
127+
eventId: $eventId
128+
status: $status
129+
reason: $reason
130+
) {
131+
id
132+
timestamp
133+
success
134+
note
135+
user {
136+
id
137+
name
138+
}
139+
event {
140+
type
141+
targetUserId
142+
resolutionStatus
143+
contributorData {
144+
name
145+
givenName
146+
familyName
147+
orcid
148+
contributorType
149+
order
150+
}
151+
}
152+
}
153+
}
154+
`

packages/openneuro-app/src/scripts/queries/user.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ export const GET_USER = gql`
4242
message
4343
requestId
4444
targetUserId
45-
status
4645
reason
4746
datasetId
4847
resolutionStatus
@@ -52,6 +51,14 @@ export const GET_USER = gql`
5251
email
5352
orcid
5453
}
54+
contributorData {
55+
name
56+
givenName
57+
familyName
58+
orcid
59+
contributorType
60+
order
61+
}
5562
}
5663
notificationStatus {
5764
status

packages/openneuro-app/src/scripts/types/event-types.ts

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ export interface MappedNotification {
4242
title: string
4343
content: string
4444
status: "unread" | "saved" | "archived"
45-
type: "general" | "approval" | "response"
45+
type: "general" | "approval" | "response" | "citationRequest"
4646
approval?: "pending" | "accepted" | "denied"
4747
originalNotification: Event
4848
datasetId?: string
@@ -72,30 +72,49 @@ export const mapRawEventToMappedNotification = (
7272
let approval: MappedNotification["approval"]
7373
let requesterUser: User | undefined
7474
let adminUser: User | undefined
75+
const capitalize = (str: string) =>
76+
str ? str.charAt(0).toUpperCase() + str.slice(1) : str
7577

7678
switch (type) {
7779
case "contributorRequest":
78-
title = "Contributor Request for Dataset"
80+
title = `[${type}] User Contributor Request for Dataset`
7981
mappedType = "approval"
8082
approval = resolutionStatus ?? "pending"
8183
requesterUser = user
8284
break
8385
case "contributorResponse":
84-
title = `Contributor ${eventStatus} for Dataset`
86+
title = `[${type}] User Contributor ${
87+
capitalize(eventStatus ?? "")
88+
} for Dataset`
8589
mappedType = "response"
8690
approval = eventStatus as "accepted" | "denied"
8791
adminUser = user
8892
break
93+
case "contributorCitation": {
94+
mappedType = "citationRequest"
95+
const status: "pending" | "accepted" | "denied" =
96+
(resolutionStatus as "pending" | "accepted" | "denied") ?? "pending"
97+
approval = status
98+
const targetName = event.target?.name || "Unknown User"
99+
if (status === "pending") {
100+
title =
101+
`[${type}] An admin has requested ${targetName} be added as an Author`
102+
} else {
103+
const capitalizedStatus = status.charAt(0).toUpperCase() +
104+
status.slice(1)
105+
title = `[${type}] ${targetName} has ${capitalizedStatus} authorship`
106+
}
107+
break
108+
}
89109
case "note":
90-
title = note || "Admin Note on Dataset"
110+
title = `[${type}] ${note || "Admin Note on Dataset"}`
91111
break
92112
default:
93-
title = note || `Dataset ${type || "Unknown Type"}`
113+
title = `[${type}] ${note || `Dataset ${type || "Unknown Type"}`}`
94114
break
95115
}
96116

97117
const datasetId = dataset?.id || rawDatasetId || event.datasetId || ""
98-
99118
const notificationStatus =
100119
(rawNotification.notificationStatus?.status?.toLowerCase() as
101120
| "unread"

packages/openneuro-app/src/scripts/users/user-notification-accordion-actions.tsx

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ interface NotificationActionButtonsProps {
1010
notification: MappedNotification
1111
isProcessing: boolean
1212
onUpdate: (id: string, updates: Partial<MappedNotification>) => void
13-
setError: (error: string | null) => void
1413
handleProcessAction: (action: "accepted" | "denied") => void
1514
handleStatusChange: (
1615
newStatus: "unread" | "saved" | "archived",
@@ -27,7 +26,7 @@ export const NotificationActionButtons: React.FC<
2726
}) => {
2827
const { status, type, approval } = notification
2928
const isContributorRequest = type === "approval"
30-
29+
const isCitationRequest = type === "citationRequest"
3130
return (
3231
<div className={styles.actions}>
3332
{isContributorRequest && (
@@ -41,7 +40,7 @@ export const NotificationActionButtons: React.FC<
4140
disabled={approval === "accepted" || isProcessing}
4241
>
4342
<i className="fa fa-check" />{" "}
44-
{approval === "accepted" ? "Approved" : "Approve"}
43+
{approval === "accepted" ? "Accepted" : "Accept"}
4544
</button>
4645
)}
4746

@@ -60,6 +59,27 @@ export const NotificationActionButtons: React.FC<
6059
</>
6160
)}
6261

62+
{isCitationRequest && approval !== "accepted" && approval !== "denied" &&
63+
(
64+
<>
65+
<button
66+
className={`${styles.notificationapprove}`}
67+
onClick={() => handleProcessAction("accepted")}
68+
disabled={isProcessing}
69+
>
70+
<i className="fa fa-check" /> Accept
71+
</button>
72+
73+
<button
74+
className={`${styles.notificationdeny}`}
75+
onClick={() => handleProcessAction("denied")}
76+
disabled={isProcessing}
77+
>
78+
<i className="fa fa-times" /> Deny
79+
</button>
80+
</>
81+
)}
82+
6383
{status === "unread" && (
6484
<>
6585
<StatusActionButton

packages/openneuro-app/src/scripts/users/user-notification-accordion.tsx

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ export const NotificationAccordion = ({
4040

4141
const isContributorRequest = type === "approval"
4242
const isContributorResponse = type === "response"
43+
const isCitationRequest = type === "citationRequest"
4344

4445
const { data: targetUserData, loading: targetUserLoading } = useQuery(
4546
GET_USER,
@@ -58,8 +59,6 @@ export const NotificationAccordion = ({
5859
const [currentApprovalAction, setCurrentApprovalAction] = useState<
5960
"accepted" | "denied" | null
6061
>(null)
61-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
62-
const [localError, setLocalError] = useState<string | null>(null)
6362
const [processContributorRequest, { loading: processRequestLoading }] =
6463
useMutation(PROCESS_CONTRIBUTOR_REQUEST_MUTATION)
6564
const isProcessing = processRequestLoading || targetUserLoading
@@ -74,7 +73,6 @@ export const NotificationAccordion = ({
7473
setShowReasonInput(false)
7574
setReasonInput("")
7675
setCurrentApprovalAction(null)
77-
setLocalError(null)
7876
}
7977
}, [isOpen])
8078

@@ -83,14 +81,12 @@ export const NotificationAccordion = ({
8381
setShowReasonInput(true)
8482
setReasonInput("")
8583
setCurrentApprovalAction(action)
86-
setLocalError(null)
8784
}, [])
8885

8986
const handleReasonSubmit = useCallback(async () => {
9087
if (!reasonInput.trim()) {
9188
const errorMessage = "Please provide a reason for this action."
9289
toast.error(<ToastContent title="Reason Required" body={errorMessage} />)
93-
setLocalError(errorMessage)
9490
return
9591
}
9692

@@ -101,19 +97,16 @@ export const NotificationAccordion = ({
10197
"Missing required data for processing contributor request."
10298
Sentry.captureException(missingDataError)
10399
toast.error(<ToastContent title="Missing Data" body={missingDataError} />)
104-
setLocalError(missingDataError)
105100
return
106101
}
107102

108-
setLocalError(null)
109-
110103
try {
111104
await processContributorRequest({
112105
variables: {
113106
datasetId,
114107
requestId,
115108
targetUserId,
116-
status: currentApprovalAction,
109+
resolutionStatus: currentApprovalAction,
117110
reason: reasonInput,
118111
},
119112
})
@@ -128,16 +121,13 @@ export const NotificationAccordion = ({
128121
setReasonInput("")
129122
setCurrentApprovalAction(null)
130123
} catch (error: unknown) {
131-
let message = "Unknown error"
132-
if (error instanceof Error) {
133-
message = error.message
134-
}
135-
const errorMessage = `Error processing contributor request: ${message}`
124+
const errorMessage = error instanceof Error
125+
? error.message
126+
: "Unknown error"
136127
Sentry.captureException(error)
137128
toast.error(
138129
<ToastContent title="Processing Failed" body={errorMessage} />,
139130
)
140-
setLocalError(errorMessage)
141131
}
142132
}, [
143133
reasonInput,
@@ -153,7 +143,6 @@ export const NotificationAccordion = ({
153143
setShowReasonInput(false)
154144
setReasonInput("")
155145
setCurrentApprovalAction(null)
156-
setLocalError(null)
157146
}, [])
158147

159148
const handleStatusChange = useCallback(
@@ -205,7 +194,6 @@ export const NotificationAccordion = ({
205194
<NotificationActionButtons
206195
notification={notification}
207196
isProcessing={isProcessing}
208-
setError={setLocalError}
209197
onUpdate={onUpdate}
210198
handleProcessAction={handleProcessAction}
211199
handleStatusChange={handleStatusChange}
@@ -230,6 +218,7 @@ export const NotificationAccordion = ({
230218
content={content}
231219
isContributorRequest={isContributorRequest}
232220
isContributorResponse={isContributorResponse}
221+
isCitationRequest={isCitationRequest}
233222
approval={approval}
234223
requesterUser={requesterUser}
235224
adminUser={adminUser}

packages/openneuro-app/src/scripts/users/user-notifications-accordion-body.tsx

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ interface NotificationBodyContentProps {
66
content?: string
77
isContributorRequest: boolean
88
isContributorResponse: boolean
9+
isCitationRequest: boolean
910
approval?: "pending" | "accepted" | "denied"
1011
requesterUser?: User
1112
adminUser?: User
@@ -19,6 +20,7 @@ export const NotificationBodyContent: React.FC<NotificationBodyContentProps> = (
1920
content,
2021
isContributorRequest,
2122
isContributorResponse,
23+
isCitationRequest,
2224
approval,
2325
requesterUser,
2426
adminUser,
@@ -32,7 +34,7 @@ export const NotificationBodyContent: React.FC<NotificationBodyContentProps> = (
3234
return (
3335
<p>
3436
Contributor request from <Username user={requesterUser} /> was{" "}
35-
<strong>approved</strong>.
37+
<strong>Accepted</strong>.
3638
</p>
3739
)
3840
} else if (approval === "denied") {
@@ -68,6 +70,16 @@ export const NotificationBodyContent: React.FC<NotificationBodyContentProps> = (
6870
</div>
6971
</>
7072
)
73+
} else if (isCitationRequest) {
74+
return (
75+
<>
76+
{targetUserLoading ? <span>for ...</span> : (
77+
<>
78+
Sent to <Username user={targetUser} />
79+
</>
80+
)}
81+
</>
82+
)
7183
}
7284
return <>{content}</>
7385
}

packages/openneuro-app/src/scripts/users/user-notifications-context.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import React, { createContext, useCallback, useContext, useState } from "react"
2+
import * as Sentry from "@sentry/react"
23
import { useMutation } from "@apollo/client"
34
import { UPDATE_NOTIFICATION_STATUS_MUTATION } from "../queries/datasetEvents"
45
import type { MappedNotification } from "../types/event-types"
5-
import * as Sentry from "@sentry/react"
66

77
interface NotificationsContextValue {
88
notifications: MappedNotification[]

0 commit comments

Comments
 (0)