-
Notifications
You must be signed in to change notification settings - Fork 818
[Bulk User]: Handle bad-data errors w/ handleApiError (+ better side panel refresh handling)
#13769
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
[Bulk User]: Handle bad-data errors w/ handleApiError (+ better side panel refresh handling)
#13769
Conversation
Build Artifacts
|
|
Hi @nucleogenesis - I confirm that now if I try to enrol a user in an already deleted class then I am seeing the "Sorry! Something went wrong" screen and the user can refresh the page. Will you be handling the following similar edge cases in a similar manner within the scope of this PR or I should report them separately:
remove.from.deleted.class.mp4I would expect to see the generic error page in this case too, allowing me to refresh.
assign.coach.mp4
create.a.new.user.and.assign.or.enrol.in.class.mp4
actions.with.deleted.user.mp4
attempts.to.interact.with.permanently.deleted.user.mp4 |
| onMounted(() => { | ||
| // Answering the question "what happens when we refresh?" | ||
| if (props.selectedUsers.size === 0) { | ||
| goBack(); | ||
| } | ||
| }); |
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.
Just reflecting if we should wait until the component is mounted to do this check? I think we can also prevent the load data API calls like this https://github.com/learningequality/kolibri/pull/13769/files#diff-1004c38374e2196707382df426d82066fb2c3e124faa549273ceff802b59695bR203 if the are no selected users.
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 think your link might be pointing to the wrong place it's just bringing me back to this comment.
I did try it w/out the onMounted hook but it didn't work but if there's a better way I'll apply it here.
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've ended up moving this into a beforeRouteEnter guard @AlexVelezLl so that should avoid unneeded API calls.
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 changes you just pushed do seem to simplify things, although having read the code, I'm not sure that I'm the best positioned to be a final approver, since it seems like both @AlexVelezLl and @rtibbles had some concrete recommendations/feedback
| createdMemberships.value = newMemberships; | ||
| } catch (error) { | ||
| showErrorWarning.value = true; | ||
| // Why doesn't this ever get run here? |
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 don't know what this comment means?
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.
Thanks for catching this lol - it is an artifact of my confusion caused by the errors being swallowed up in the apiResource module 😅
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 had far fewer opinions than I thought I would. This is fine to go as long as manual testing of these specific points check out (others will be filed in follow ups).
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.
Refresh the page on each of the side panels ✅
Perform the replication steps noted in #13550 and see that when you get a bad-data error there, you are shown the API error page and that when you click "Refresh" you are taken to a fresh users table. ✅
manual testing confirms both of these. I will leave more robust edge case finding to peter in our beta0 :) good to go
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.
Just flagging that the implementation of the beforeRouteEnter is partially incorrect, because these side panels can also be opened from the new users page. So redirecting them to the main user management page feel a bit weird? It is an edge case though, so will let you decide if this is a blocker or not!
Screenshare.-.2025-09-23.6_29_37.PM.mp4
|
In general I think that it does not makes much sense of having these side panels as route components. IIRC, the reason for having the side panels as route components was to respond to the question of "what happens if the user refresh the page?" and the reason of using routes for displaying the side panels was to keep the side panel open after the user refresh. But in this case this does not apply well because the user selection gets removed when they refresh. And now with this Having these side panels coming from different parent routes have made the implementation of the routes definition, the dependencies definition of the components (as all of them are hidden behind the An option for making it worth could be exploring saving the selected users state in the local storage or route query params, so when the user reloads they keep seeing the side panels with the previously selected users, but I think that this will also add some more complexity to the implementation. |
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.
Adding a blocking review until we figure out how to proceed regarding the previous review.
|
Thanks Alex - I'll definitely apply the changes to ensure the panels all work on the new/removed users pages. I agree re: the routing - but I may put that into a follow-up issue. |
…e about the class The problem with showing an error here is that the removal can be partially successful. The error doesn't show up when removing, but rather when undoing the removal - these changes simply proceed along as if everything went okay because ... you're removing users from the class and, technically, they've already been removed from the class :)
6d9f450 to
1316f1e
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.
Just some thoughts!
| createdMemberships.value = newMemberships; | ||
| } catch (error) { | ||
| showErrorWarning.value = true; | ||
| store.dispatch('handleApiError', { error }); |
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.
Should this specific side panel have a different error handling on its main action? The other side panels handle this by setting showErrorWarning to true.
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 understood the issue Peter reported was that the error warning was unhelpful in this case because knowing something went wrong is half the battle - the other issue is that the most likely solution is to refresh the page & try again so dispatching the API error here ensures that happens.
To be honest, though, I think a more robust errors reporting would be ideal here to let the user know specifically what actually happened (ie, "The class you selected 'Class Name' no longer exists"). I'll think on this a bit more when I spin this up again in a bit. Thanks Alex
| try { | ||
| await Promise.all([ | ||
| enrollments.length | ||
| ? MembershipResource.saveCollection({ data: enrollments }) | ||
| : Promise.resolve(), | ||
| assignments.length | ||
| ? RoleResource.saveCollection({ data: assignments }) | ||
| : Promise.resolve(), | ||
| ]); | ||
| } catch (_) { | ||
| showErrorWarning.value = true; | ||
| } finally { | ||
| loading.value = false; | ||
| } |
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 undoUserRemoval function is executed after the RemoveFromClassSidePanel has been unmounted, since this callback is executed in the undo snackbar, after the modal has been closed. Therefore, setting the showErrorWarning or loading refs here does not have any effect. Perhaps it would be a better idea to show this error in another snackbar?
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.
Pushed a change using a Snackbar - great catch Alex thank you!
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 changes look good to me! Thanks @nucleogenesis!
Summary
apiResource.jsfile, thelogErrorfunction expected an error object to have a particular shape - but the error that this issue fixes was getting swallowed up by the errors thrown when the error object inlogErrorwasn't the shape expected. I added some existence checks to ensure errors don't get annihilated.onMountedhooks to redirect to the root page (using thegoBackmodule function) whenever there are no users selected.This will give the user the ability to refresh the page, which will remove stale data.
References
Fixes #13550
Reviewer guidance