-
Notifications
You must be signed in to change notification settings - Fork 29
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
lib-user: add group manage modal with group update inputs, group delete, and members management #6100
Conversation
…s page manage modal
d9c7fb0
to
c50733f
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code read is generally looking good, and the storybooks too. Could you join this group, so I have a test group where I'm admin and multiple members? https://www.zooniverse.org/groups/1326995?join_token=0e834515f7390aa4 (staging)
Noting that the GroupModal + GroupForm is relatively tall, so it takes up the majority of my viewport height which risks hiding the Save button. The figma design for managing a group does not include the extra vertical space and borders that exist in this PR. Is the figma link the correct reference?
I've left several more suggestions and comments below...
...er/src/components/GroupStats/components/GroupUpdateFormContainer/GroupUpdateFormContainer.js
Outdated
Show resolved
Hide resolved
...er/src/components/GroupStats/components/GroupUpdateFormContainer/GroupUpdateFormContainer.js
Outdated
Show resolved
Hide resolved
packages/lib-user/src/components/GroupStats/components/MembersList/MembersList.js
Outdated
Show resolved
Hide resolved
async function handleDeleteMembership({ membershipId }) { | ||
const deleteMembershipResponse = await deletePanoptesMembership({ membershipId }) | ||
if (deleteMembershipResponse.ok) { | ||
const updatedMemberships = memberships.filter(membership => membership.id !== membershipId) | ||
setMemberships(updatedMemberships) | ||
} | ||
} | ||
|
||
async function handleUpdateMembership({ membershipId, data }) { | ||
const updateMembershipResponse = await updatePanoptesMembership({ membershipId, data }) | ||
if (updateMembershipResponse.ok) { | ||
const updatedMemberships = memberships.map(membership => { | ||
if (membership.id === membershipId) { | ||
membership.roles = data.roles | ||
} | ||
return membership | ||
}) | ||
setMemberships(updatedMemberships) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This sequence is an "optimistic UI" pattern where you're assuming the deletePanoptesMembership
or updatePanoptesMembership
request to panoptes is successful, and therefore don't need to call usePanoptesMemberships
again. Just wondering if you thought about the pros/cons of triggering usePanoptesMemberships
upon delete or update, and then simply using data: membershipsData
as source of "state" for this component rather than useState
.
(I don't have a great answer, mainly just for my own understanding if an "optimistic UI" pattern is the easiest for this component, that's okay!)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not exactly sure how I'd implement it using membershipsData
as state, as I think I'd have to add a param to the usePanoptesMemberships
hook so it knew to update? Open to suggestion, but this was what felt easiest initially. Definitely have to refactor for sad paths in a few places, maybe worth creating a dedicated card/issue.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought about this concept a bit more, and I think it's worth looking into SWR's mutations. This youtube tutorial is also a great example of SWR data fetching + mutation in practice.
The majority of data handling in the user groups and stats pages simply fetches data from panoptes, which you've implemented with SWR hooks. This works well because the fetched/cached data can't be updated on the user groups or stats pages (for instance, stats are modified on classify pages or a user's display name is updated on their profile settings page). However, group memberships is the major feature that requires modifying data in panoptes while also updating the currently-viewed UI on the fly.
SWR provides hook useSWRMutation
for cases where you need to POST, PUT, or DEL to the backend, yet continue to use the data returned from a GET hook like usePanoptesMemberships
in MembersListContainer. useSWRMutation
is an optimistic UI pattern, but without creating a separate useState
variable.
The second reason I think it's worth looking into SWR mutations is to handle the forced page refresh I commented on. Forcing a page refresh after modifying group membership data feels like an anti-pattern in a React application where components in the UI should re-render when passed new data props. GroupStatsContainer also utilizes usePanoptesMemberships
and passes membershipsData
to GroupStats. With SWR mutations you could rely on membershipsData
to update after a mutation and cause a GroupStats re-render without a total page refresh.
Please let me know your thoughts! And if you'd like to separate changes to membership data handling into another PR or continue on this one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The useSWRMutation
SWR hook noted sounds great to me, certainly preferable. My only hesitation is adding to this PR's changes, but if that's ok with you then it's ok with me and I'd like to try refactoring accordingly.
Other user/group stats PRs aren't dependent on this PR so it won't hold anything up if this gets additional work.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Feel free to add to this PR if easiest in the flow of features for lib-user! You could also create a PR requesting to merge into this one with isolated changes for data mutation. Happy to review either way.
packages/lib-user/src/components/GroupStats/components/MembersList/MembersListContainer.js
Outdated
Show resolved
Hide resolved
.../lib-user/src/components/GroupStats/components/MembersList/components/MemberListItem.spec.js
Outdated
Show resolved
Hide resolved
packages/lib-user/src/components/shared/GroupForm/components/RadioInputLabel.js
Outdated
Show resolved
Hide resolved
packages/lib-user/src/components/shared/GroupForm/components/FieldLabel.js
Outdated
Show resolved
Hide resolved
packages/lib-user/src/components/shared/GroupModal/GroupModal.stories.js
Show resolved
Hide resolved
d809af0
to
53723fe
Compare
Continuing to review this! One more important check though - I see both of your accounts joined https://local.zooniverse.org:3000/groups/1326995 (staging), so I tested out the group manage model and I'm confused about whether group members are related to Top Contributors. I removed one of your accounts from the group, so the manage modal looks like this: But the account I removed still shows up on Top Contributors like this: Is that expected? The Top Contributors is every account that's ever classified while in the group, even if they're no longer in the group? |
@goplayoutside3 that's a good question. I think that's expected, but I'm going to tag @yuenmichelle1 for the ERAS perspective on group members included/excluded in a response for request that includes 'top_contributors'. |
lib-user: Refactor group and membership changes with useSWRMutation
Regarding active/inactive members and stats inclusion, per related Slack thread:
Planned follow-up for a subsequent PR:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good, and the modal styling update is nice 👍
I'd still like to see an in-code comment addressing joinStatusSuccess
for documentation (PR comment),
In this PR, you've also added more util functions for fetching data, but none of the requests have unit tests yet. I noticed nock
is setup, but unused in lib-user, so I suggest creating some unit tests for the util functions soon to ensure reliability of the utils over time. There are lots of nock
examples in app-project.
Package
Describe your changes
How to Review
Helpful explanations that will make your reviewer happy:
Checklist
PR Creator - Please cater the checklist to fit the review needed for your code changes.
PR Reviewer - Use the checklist during your review. Each point should be checkmarked or discussed before PR approval.
General
yarn panic && yarn bootstrap
ordocker-compose up --build
and FEM works as expectedGeneral UX
Example Staging Project: i-fancy-cats
New Feature