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
feat(sh-admin): enhanced user management in admin dashboard #3814
feat(sh-admin): enhanced user management in admin dashboard #3814
Conversation
3ddc0aa
to
2a5a410
Compare
5235739
to
7a6b792
Compare
0239302
to
2753758
Compare
0022bd9
to
3927905
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.
Search and pagination don't play well in the user list view. The computation for total pages doesn't account for search in all cases.
73da442
to
03d8062
Compare
@jamesgeorge007 I've fixed the issue. You can test it now. |
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.
- A search session has to ensure the page number gets reset to
1
. - At times the entire list gets populated while clearing the search entry at once.
@jamesgeorge007 I have resolved these issue. |
e32d416
to
d302f8f
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.
There's an issue arising here while changing the admin status (making a user admin & vice versa) for a user and immediately selecting the row for which the action was performed won't result in the action bar (with toggle admin & delete actions) to show up. It shows up only during the second attempt.
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 requires an update to @hoppscotch/ui and will be handled separately.
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.
LGTM 🚀
Leaving a few pointers below for future reference.
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.
To obtain the total search results, we could introduce a new alias type searchResults
that takes in the searchString alone and returns the filtered entries to compute the total pages. We also use the @include directive to ensure the above field is fetched only if there's a search query supplied indicated by a new includeSearchResults
argument.
query UsersListV2(
$searchString: String
$skip: Int
$take: Int
$includeSearchResults: Boolean!
) {
infra {
allUsersV2(searchString: $searchString, skip: $skip, take: $take) {
uid
displayName
email
isAdmin
photoURL
createdOn
}
searchResults: allUsersV2(searchString: $searchString)
@include(if: $includeSearchResults) {
uid
}
}
}
(x) => x.infra.allUsers, | ||
(x) => x.uid, | ||
UsersListV2Document, | ||
(x) => x.infra.allUsersV2, |
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.
As mentioned above, we could compute the total count with the search results filter applied with modifications to the UsersListV2
GQL query. For the transformation function, we might need to account for the total count of search results x.infra.searchResults.length
that also boils down to the requirement for an object. The newly introduced includeSearchResults
argument can be supplied here and at the places where refetch is invoked where the value is supplied as searchQuery.value.length > 0
from the search handler where we'd need the total count for search results to update the computation for total pages.
<div class="mb-3 flex items-center justify-end"> | ||
<HoppButtonSecondary | ||
outline | ||
filled | ||
:icon="IconLeft" | ||
:disabled="page === 1" | ||
@click="changePage(PageDirection.Previous)" | ||
/> | ||
|
||
<div v-else-if="error">{{ t('users.load_list_error') }}</div> | ||
<div class="flex h-full w-10 items-center justify-center"> | ||
<span>{{ page }}</span> | ||
</div> | ||
|
||
<HoppSmartTable v-else-if="usersList.length" :list="usersList"> | ||
<HoppButtonSecondary | ||
outline | ||
filled | ||
:icon="IconRight" | ||
:disabled="page >= totalPages" | ||
@click="changePage(PageDirection.Next)" | ||
/> | ||
</div> | ||
|
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.
Displaying the pagination element is redundant if there is only one page. But, it might lead to layout shifts if the search results don't span multiple pages. We can perhaps move the pagination element along the line with the Invite Users
& Pending Invites
actions once it is moved out of the HoppSmartTable
component; something to think about and decide later.
<HoppSmartTable | ||
:headings="headings" | ||
:list="finalUsersList" | ||
:loading="showSpinner" |
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.
There's a slight difference in height compared to the spinner and the row with actual data. We could leverage the loading-state
slot to implement a custom spinner to fine-tune the dimensions.
2bc293a
to
7621c5b
Compare
…uncer feature from smart table
…empty state changes
- Show the error state within the table. - Ensure the next pagination action remains disabled if the current page count exceeds the total page count accounting for the case with empty results. - Move the debounce `setTimeout` ID outside the function body to achieve the expected behavior. - Clear `setTimeout` IDs on page unmount.
0b81e6e
to
6b43960
Compare
<HoppSmartTable | ||
v-else-if="teamsList.length" | ||
:headings="headings" | ||
:list="teamsList" | ||
@onRowClicked="goToTeamDetails" |
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.
can we add the loading state inside the table similar to the User table
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.
@JoelJacobStephen, please make a note of these to address separately.
<th class="px-6 py-2">{{ t('teams.id') }}</th> | ||
<th class="px-6 py-2">{{ t('teams.name') }}</th> | ||
<th class="px-6 py-2">{{ t('teams.members') }}</th> | ||
<!-- Empty Heading for the Action Button --> | ||
<th class="px-6 py-2"></th> |
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.
we can add a class and incorporate the cell styles in that.
<td class="flex py-4 px-7 max-w-[16rem]"> | ||
<span class="truncate"> | ||
{{ team.id }} | ||
</span> | ||
</td> | ||
|
||
<td class="py-4 px-7 min-w-[20rem]"> |
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.
can we use similar padding that of the heading here ? px-6 py-2
<HoppSmartInput | ||
v-model="query" | ||
styles="w-full bg-primary py-1" | ||
input-styles="h-full border-none" | ||
:placeholder="t('users.searchbar_placeholder')" | ||
/> |
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.
<HoppSmartInput | |
v-model="query" | |
styles="w-full bg-primary py-1" | |
input-styles="h-full border-none" | |
:placeholder="t('users.searchbar_placeholder')" | |
/> | |
<HoppSmartInput | |
v-model="query" | |
styles="w-full bg-primary py-1" | |
input-styles="h-full border-none" | |
:placeholder="t('users.searchbar_placeholder')" | |
:autofocus="false" | |
/> |
we can set autofocus false
here
<th class="px-6 py-2">{{ t('users.id') }}</th> | ||
<th class="px-6 py-2">{{ t('users.name') }}</th> | ||
<th class="px-6 py-2">{{ t('users.email') }}</th> | ||
<th class="px-6 py-2">{{ t('users.date') }}</th> |
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.
can use common class name to incorporate the styles
Ticket
Description
This PR brings a ton of features to improve user management in Admin Dashboard.
New Features
Additional Changes
Due to the changes introduced in
HoppSmartTable
, all tables in the admin dashboard have been updated to adhere to the new changes.Screenshot
Checks