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'])
{
$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,
*/
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);
}
$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';
}
}
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;
// 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',
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;
.dropdown-search-dropdown .dropdown-search-list {
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;
}
\ No newline at end of file
}
}
+.flex-2 {
+ min-height: 0;
+ flex: 2;
+ max-width: 100%;
+}
+
+.flex-3 {
+ min-height: 0;
+ flex: 3;
+ max-width: 100%;
+}
+
.flex-none {
flex: none;
}
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
<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'),
@endforeach
</div>
@else
- <div class="grid third">
+ <div class="grid third">
@foreach($books as $key => $book)
@include('entities.grid-item', ['entity' => $book])
@endforeach
- </div>
+ </div>
@endif
<div>
{!! $books->render() !!}
$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')
</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
<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,
</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(),
-
<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>
@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
<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'),
<div class="flex-container-row wrap justify-space-between items-center">
<h1 class="list-heading">{{ trans('settings.users') }}</h1>
+ <div>
+ <a href="{{ url("/settings/users/create") }}" class="outline button mt-none">{{ trans('settings.users_add_new') }}</a>
+ </div>
+ </div>
+
+ <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") }}">
- @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 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>
- <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>
+ <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>
<span title="MFA Configured" class="text-pos">@icon('lock')</span>
@endif
</a>
- </td>
- <td>
- @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">
- @if($user->last_activity_at)
- <small title="{{ $user->last_activity_at->format('Y-m-d H:i:s') }}">{{ $user->last_activity_at->diffForHumans() }}</small>
- @endif
- </td>
- </tr>
+ </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
+ </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
+ </div>
+ </div>
+ </div>
@endforeach
- </table>
+ </div>
<div>
{{ $users->links() }}