Skip to content

Conversation

@thinknoack
Copy link
Contributor

@thinknoack thinknoack commented Jul 28, 2025

Adds EVENTS for managing contributor access to datasets. Allows users to formally request contributor status, and provides dataset administrators with a dedicated UI to view, accept, or deny these requests. All actions are tracked and displayed in a new, unified events log on the dataset and admins get a log in their account notifications.

Actual contributor Roles/Perms are TODO - this PR just handles events resolvers and event related UI

Key Work & Changes

  • New Contributor Request Workflow

  • Request Contributor Button: allows users who do not have permissions to a dataset to formally request contributor access.

  • New GraphQL mutations are used to log these requests as events.

  • Admin UI: The DatasetEvents component can now managing these requests

  • Events & Notifications System

  • New Event Types: contributorRequest and contributorResponse

  • The DatasetEvents component has been refactored

  • Detailed Event Items component is responsible for displaying each event

  • New CSS added for events UI and actions.

  • tests have been adjusted for various issues related to updates. additional tests needed.

@thinknoack thinknoack marked this pull request as draft July 28, 2025 18:35
@thinknoack thinknoack changed the title adding new events for contributorRequest and contributorResponse WIP adding new events for contributorRequest and contributorResponse Jul 28, 2025
@codecov
Copy link

codecov bot commented Jul 28, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 71.32%. Comparing base (9aa4c81) to head (1ea7a13).
⚠️ Report is 80 commits behind head on master.

Additional details and impacted files
@@             Coverage Diff             @@
##           master    #3528       +/-   ##
===========================================
+ Coverage   44.57%   71.32%   +26.75%     
===========================================
  Files         620       51      -569     
  Lines       31685     1960    -29725     
  Branches     1485        0     -1485     
===========================================
- Hits        14123     1398    -12725     
+ Misses      17423      562    -16861     
+ Partials      139        0      -139     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@thinknoack thinknoack changed the title WIP adding new events for contributorRequest and contributorResponse Add Contributor Request/Response Workflow and Events UI Jul 31, 2025
@thinknoack thinknoack requested a review from nellh August 1, 2025 18:01
Copy link
Contributor

@nellh nellh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'd be good to try to break things like this up into smaller PRs in the future, this could probably be several component PRs and an API change one. Most of it is looking pretty good and working. Made a few recommendations. It looks intentional that the notification status buttons are failing, that is fine to save for another PR.

# Notes associated with the event
note: String
# top-level datasetId field
datasetId: ID
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to duplicate the datasetId from the parent in the model. Why does it need a second copy of it?

Copy link
Contributor Author

@thinknoack thinknoack Aug 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i was thinking that we might have various places outside of dataset that we are querying for events. The new notifications resolver queries for DatasetEvent objects by userID. Outside of a dataset- datasetId is the only way for the frontend to know which dataset an event belongs to. Does that make sense to do here?

Comment on lines 53 to 58
await updateNotificationStatus({
variables: {
datasetEventId: id,
status: backendStatus,
},
})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ran into 400 error, Cannot query field "updateNotificationStatus" on type "Mutation". on triggering this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, I put that on hold, and have a question about how to store that data on a user if many users will share the same notification.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this has been updated in #3548

Comment on lines 78 to 83
setTimeout(() => {
setUpdatedNote("")
if (editingNoteId === event.id) {
startEditingNote(null, "")
}
}, 50)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is setTimeout necessary here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not really. I was trying to account for a slow refetchEvents where there could be what feels like two "renders" in the UI. I don't imagine it to ever be an issue and when I remove it locally it is pretty unnoticeable. I will remove it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed

Comment on lines 4 to 22
interface ProcessedDatasetEvent {
id: string
note: string
timestamp: string
user: {
id: string
name: string
email: string
orcid: string
}
event: {
type: string
requestId?: string
status?: string
}
success: boolean
hasBeenRespondedTo?: boolean
responseStatus?: string
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The event type seems to be defined a few times. It would be good to unify these and import the shared type.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will do

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Comment on lines 62 to 84
const processedEvents = useMemo(() => {
const responsesMap = new Map()
rawEvents.forEach((event) => {
if (event.event.type === "contributorResponse" && event.event.requestId) {
responsesMap.set(event.event.requestId, event)
}
})

const enrichedEvents = rawEvents.map((event) => {
if (event.event.type === "contributorRequest") {
const response = responsesMap.get(event.id)
if (response) {
return {
...event,
hasBeenRespondedTo: true,
responseStatus: response.event.status,
}
}
}
return event
})
return enrichedEvents
}, [rawEvents])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This step could be done on the API side instead. If the client needs this, the API should just return it or provide an option for it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moved to the backend

Comment on lines 46 to 51
// Determine if the current user already has any permissions (read, write, admin)
// If they do, the button should not be displayed.
const hasPermissions = datasetPermissions?.some((p) =>
p.user.id === currentUserId &&
(p.level === "admin" || p.level === "rw" || p.level === "read")
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should check if the user is a contributor, rather than if they have permissions.

Copy link
Contributor Author

@thinknoack thinknoack Aug 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a perm check for the dataset.contributors array. needs to be reviewed once that field is added.

@thinknoack thinknoack requested a review from nellh August 11, 2025 20:28
@thinknoack thinknoack marked this pull request as ready for review August 11, 2025 20:28
@thinknoack thinknoack self-assigned this Aug 13, 2025
…ts by adding a new UserNotificationStatus model and updating resolvers to populate the status field.
thinknoack and others added 26 commits September 15, 2025 17:28
…ntributor-onUser

Feature/ mutations for event adding contributors and accept/deny adding contributors
Feat(api) add a user specific virtual notification status to dataset events
@nellh nellh merged commit d768026 into master Oct 1, 2025
10 of 13 checks passed
@nellh nellh deleted the feature/request-contrib-role branch October 1, 2025 21:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants