Redesigned users list to be responsive and aligned

This commit is contained in:
Dan Brown 2022-10-29 15:23:21 +01:00
parent 2a65331573
commit 986346a0e9
No known key found for this signature in database
GPG Key ID: 46D9F943C24A2EF9
13 changed files with 127 additions and 86 deletions

View File

@ -19,6 +19,9 @@ class AllUsersPaginatedAndSorted
public function run(int $count, array $sortData): LengthAwarePaginator
{
$sort = $sortData['sort'];
if ($sort === 'created_at') {
$sort = 'users.created_at';
}
$query = User::query()->select(['*'])
->scopes(['withLastActivityAt'])

View File

@ -37,15 +37,15 @@ class UserController extends Controller
{
$this->checkPermission('users-manage');
$listDetails = [
'order' => $request->get('order', 'asc'),
'search' => $request->get('search', ''),
'sort' => $request->get('sort', 'name'),
'sort' => setting()->getForCurrentUser('users_sort', 'name'),
'order' => setting()->getForCurrentUser('users_sort_order', 'asc'),
];
$users = (new AllUsersPaginatedAndSorted())->run(20, $listDetails);
$this->setPageTitle(trans('settings.users'));
$users->appends($listDetails);
$users->appends(['search' => $listDetails['search']]);
return view('users.index', [
'users' => $users,
@ -251,7 +251,7 @@ class UserController extends Controller
*/
public function changeSort(Request $request, string $id, string $type)
{
$validSortTypes = ['books', 'bookshelves', 'shelf_books'];
$validSortTypes = ['books', 'bookshelves', 'shelf_books', 'users'];
if (!in_array($type, $validSortTypes)) {
return redirect()->back(500);
}
@ -318,7 +318,7 @@ class UserController extends Controller
$this->checkPermissionOrCurrentUser('users-manage', $userId);
$sort = $request->get('sort');
if (!in_array($sort, ['name', 'created_at', 'updated_at', 'default'])) {
if (!in_array($sort, ['name', 'created_at', 'updated_at', 'default', 'email', 'last_activity_at'])) {
$sort = 'name';
}

View File

@ -62,7 +62,7 @@ class EntityPermissions {
}
removeRowOnButtonClick(button) {
const row = button.closest('.content-permissions-row');
const row = button.closest('.item-list-row');
const roleId = button.dataset.roleId;
const roleName = button.dataset.roleName;

View File

@ -172,6 +172,7 @@ return [
// Users
'users' => 'Users',
'users_index_desc' => 'Create & manage individual user accounts within the system. User accounts are used for login and attribution of content & activity. Access permissions are primarily role-based but user content ownership, among other factors, may also affect permissions & access.',
'user_profile' => 'User Profile',
'users_add_new' => 'Add New User',
'users_search' => 'Search Users',

View File

@ -798,37 +798,6 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
max-width: 500px;
}
.content-permissions {
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1);
}
.content-permissions-row {
border: 1.5px solid;
@include lightDark(border-color, #E2E2E2, #444);
border-bottom-width: 0;
label {
padding-bottom: 0;
}
&:hover {
@include lightDark(background-color, #F2F2F2, #333);
}
}
.content-permissions-row:first-child {
border-radius: 4px 4px 0 0;
}
.content-permissions-row:last-child {
border-radius: 0 0 4px 4px;
border-bottom-width: 1.5px;
}
.content-permissions-row:first-child:last-child {
border-radius: 4px;
}
.content-permissions-row-toggle-all {
visibility: hidden;
}
.content-permissions-row:hover .content-permissions-row-toggle-all {
visibility: visible;
}
.template-item {
cursor: pointer;
position: relative;
@ -970,3 +939,34 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
max-height: 240px;
}
}
.item-list {
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1);
}
.item-list-row {
border: 1.5px solid;
@include lightDark(border-color, #E2E2E2, #444);
border-bottom-width: 0;
label {
padding-bottom: 0;
}
&:hover {
@include lightDark(background-color, #F6F6F6, #333);
}
}
.item-list-row:first-child {
border-radius: 4px 4px 0 0;
}
.item-list-row:last-child {
border-radius: 0 0 4px 4px;
border-bottom-width: 1.5px;
}
.item-list-row:first-child:last-child {
border-radius: 4px;
}
.item-list-row-toggle-all {
visibility: hidden;
}
.item-list-row:hover .item-list-row-toggle-all {
visibility: visible;
}

View File

@ -158,6 +158,18 @@ body.flexbox {
}
}
.flex-2 {
min-height: 0;
flex: 2;
max-width: 100%;
}
.flex-3 {
min-height: 0;
flex: 3;
max-width: 100%;
}
.flex-none {
flex: none;
}
@ -178,6 +190,27 @@ body.flexbox {
align-items: center;
}
/**
* Min width utilities
*/
.min-width-xs {
min-width: 120px;
}
.min-width-s {
min-width: 160px;
}
.min-width-m {
min-width: 200px;
}
.min-width-l {
min-width: 240px;
}
.min-width-xl {
min-width: 280px;
}
.min-width-xxl {
min-width: 320px;
}
/**
* Display and float utilities

View File

@ -3,7 +3,7 @@
<h1 class="list-heading">{{ trans('entities.books') }}</h1>
<div class="text-m-right my-m">
@include('entities.sort', ['options' => [
@include('common.sort', ['options' => [
'name' => trans('common.sort_name'),
'created_at' => trans('common.sort_created_at'),
'updated_at' => trans('common.sort_updated_at'),

View File

@ -5,7 +5,7 @@ $permission - The entity permission containing the permissions.
$inheriting - Boolean if the current row should be marked as inheriting default permissions. Used for "Everyone Else" role.
--}}
<div component="permissions-table" class="content-permissions-row flex-container-row justify-space-between wrap">
<div component="permissions-table" class="item-list-row flex-container-row justify-space-between wrap">
<div class="gap-x-m flex-container-row items-center px-l py-m flex">
<div class="text-large" title="{{ $role->id === 0 ? trans('entities.permissions_role_everyone_else') : trans('common.role') }}">
@icon($role->id === 0 ? 'groups' : 'role')
@ -16,7 +16,7 @@ $inheriting - Boolean if the current row should be marked as inheriting default
</span>
@if($role->id !== 0)
<button type="button"
class="ml-auto flex-none text-small text-primary text-button hover-underline content-permissions-row-toggle-all hide-under-s"
class="ml-auto flex-none text-small text-primary text-button hover-underline item-list-row-toggle-all hide-under-s"
refs="permissions-table@toggle-all"
><strong>{{ trans('common.toggle_all') }}</strong></button>
@endif

View File

@ -35,7 +35,7 @@
<hr>
<div refs="entity-permissions@role-container" class="content-permissions mt-m mb-m">
<div refs="entity-permissions@role-container" class="item-list mt-m mb-m">
@foreach($data->permissionsWithRoles() as $permission)
@include('form.entity-permissions-row', [
'permission' => $permission,
@ -58,7 +58,7 @@
</div>
</div>
<div class="content-permissions mt-m mb-xl">
<div class="item-list mt-m mb-xl">
@include('form.entity-permissions-row', [
'role' => $data->everyoneElseRole(),
'permission' => $data->everyoneElseEntityPermission(),

View File

@ -1,10 +1,9 @@
<main class="content-wrap mt-m card">
<div class="grid half v-center">
<h1 class="list-heading">{{ trans('entities.shelves') }}</h1>
<div class="text-right">
@include('entities.sort', ['options' => $sortOptions, 'order' => $order, 'sort' => $sort, 'type' => 'bookshelves'])
@include('common.sort', ['options' => $sortOptions, 'order' => $order, 'sort' => $sort, 'type' => 'bookshelves'])
</div>
</div>
@ -31,7 +30,8 @@
@else
<p class="text-muted">{{ trans('entities.shelves_empty') }}</p>
@if(userCan('bookshelf-create-all'))
<a href="{{ url("/create-shelf") }}" class="button outline">@icon('edit'){{ trans('entities.create_now') }}</a>
<a href="{{ url("/create-shelf") }}"
class="button outline">@icon('edit'){{ trans('entities.create_now') }}</a>
@endif
@endif

View File

@ -23,7 +23,7 @@
<h1 class="flex fit-content break-text">{{ $shelf->name }}</h1>
<div class="flex"></div>
<div class="flex fit-content text-m-right my-m ml-m">
@include('entities.sort', ['options' => [
@include('common.sort', ['options' => [
'default' => trans('common.sort_default'),
'name' => trans('common.sort_name'),
'created_at' => trans('common.sort_created_at'),

View File

@ -9,37 +9,37 @@
<div class="flex-container-row wrap justify-space-between items-center">
<h1 class="list-heading">{{ trans('settings.users') }}</h1>
<div>
<div class="block inline mr-xs">
<form method="get" action="{{ url("/settings/users") }}">
@foreach(collect($listDetails)->except('search') as $name => $val)
<input type="hidden" name="{{ $name }}" value="{{ $val }}">
@endforeach
<input type="text" name="search" placeholder="{{ trans('settings.users_search') }}" @if($listDetails['search']) value="{{$listDetails['search']}}" @endif>
</form>
</div>
<a href="{{ url("/settings/users/create") }}" class="outline button mt-none">{{ trans('settings.users_add_new') }}</a>
</div>
</div>
<table class="table">
<tr>
<th width="9%"></th>
<th width="36%">
<a href="{{ sortUrl('/settings/users', $listDetails, ['sort' => 'name']) }}">{{ trans('auth.name') }}</a>
/
<a href="{{ sortUrl('/settings/users', $listDetails, ['sort' => 'email']) }}">{{ trans('auth.email') }}</a>
</th>
<th width="35%">{{ trans('settings.role_user_roles') }}</th>
<th class="text-right" width="20%">
<a href="{{ sortUrl('/settings/users', $listDetails, ['sort' => 'last_activity_at']) }}">{{ trans('settings.users_latest_activity') }}</a>
</th>
</tr>
<p class="text-muted">{{ trans('settings.users_index_desc') }}</p>
<div class="flex-container-row items-center justify-space-between gap-m mt-m mb-l wrap">
<div>
<div class="block inline mr-xs">
<form method="get" action="{{ url("/settings/users") }}">
<input type="text" name="search" placeholder="{{ trans('settings.users_search') }}" @if($listDetails['search']) value="{{$listDetails['search']}}" @endif>
</form>
</div>
</div>
<div class="justify-flex-end">
@include('common.sort', ['options' => [
'name' => trans('common.sort_name'),
'email' => trans('auth.email'),
'created_at' => trans('common.sort_created_at'),
'updated_at' => trans('common.sort_updated_at'),
'last_activity_at' => trans('settings.users_latest_activity'),
], 'order' => $listDetails['order'], 'sort' => $listDetails['sort'], 'type' => 'users'])
</div>
</div>
<div class="item-list">
@foreach($users as $user)
<tr>
<td class="text-center" style="line-height: 0;"><img class="avatar med" src="{{ $user->getAvatar(40)}}" alt="{{ $user->name }}"></td>
<td>
<div class="flex-container-row item-list-row items-center wrap py-s">
<div class="px-m py-xs flex-container-row items-center flex-2 gap-l min-width-m">
<img class="avatar med" width="40" height="40" src="{{ $user->getAvatar(40)}}" alt="{{ $user->name }}">
<a href="{{ url("/settings/users/{$user->id}") }}">
{{ $user->name }}
<br>
@ -48,20 +48,24 @@
<span title="MFA Configured" class="text-pos">@icon('lock')</span>
@endif
</a>
</td>
<td>
</div>
<div class="flex-container-row items-center flex-3 min-width-m">
<div class="px-m py-xs flex">
@foreach($user->roles as $index => $role)
<small><a href="{{ url("/settings/roles/{$role->id}") }}">{{$role->display_name}}</a>@if($index !== count($user->roles) -1),@endif</small>
@endforeach
</td>
<td class="text-right text-muted">
</div>
<div class="px-m py-xs flex text-right text-muted">
@if($user->last_activity_at)
<small>{{ trans('settings.users_latest_activity') }}</small>
<br>
<small title="{{ $user->last_activity_at->format('Y-m-d H:i:s') }}">{{ $user->last_activity_at->diffForHumans() }}</small>
@endif
</td>
</tr>
</div>
</div>
</div>
@endforeach
</table>
</div>
<div>
{{ $users->links() }}