Преглед на файлове

fix: 🐛 Sorting on admin/users datatable

IceToast преди 2 години
родител
ревизия
a0268f1c47
променени са 2 файла, в които са добавени 124 реда и са изтрити 73 реда
  1. 42 29
      app/Http/Controllers/Admin/UserController.php
  2. 82 44
      themes/default/views/admin/users/index.blade.php

+ 42 - 29
app/Http/Controllers/Admin/UserController.php

@@ -7,6 +7,7 @@ use App\Events\UserUpdateCreditsEvent;
 use App\Http\Controllers\Controller;
 use App\Http\Controllers\Controller;
 use App\Models\User;
 use App\Models\User;
 use App\Notifications\DynamicNotification;
 use App\Notifications\DynamicNotification;
+use App\Traits\DatatablesSortable;
 use Exception;
 use Exception;
 use Illuminate\Contracts\Foundation\Application;
 use Illuminate\Contracts\Foundation\Application;
 use Illuminate\Contracts\View\Factory;
 use Illuminate\Contracts\View\Factory;
@@ -26,6 +27,8 @@ use Spatie\QueryBuilder\QueryBuilder;
 
 
 class UserController extends Controller
 class UserController extends Controller
 {
 {
+    use DatatablesSortable;
+
     private Pterodactyl $pterodactyl;
     private Pterodactyl $pterodactyl;
 
 
     public function __construct(Pterodactyl $pterodactyl)
     public function __construct(Pterodactyl $pterodactyl)
@@ -132,7 +135,7 @@ class UserController extends Controller
             ]);
             ]);
         }
         }
 
 
-        if (! is_null($request->input('new_password'))) {
+        if (!is_null($request->input('new_password'))) {
             $request->validate([
             $request->validate([
                 'new_password' => 'required|string|min:8',
                 'new_password' => 'required|string|min:8',
                 'new_password_confirmation' => 'required|same:new_password',
                 'new_password_confirmation' => 'required|same:new_password',
@@ -259,7 +262,7 @@ class UserController extends Controller
     public function toggleSuspended(User $user)
     public function toggleSuspended(User $user)
     {
     {
         try {
         try {
-            ! $user->isSuspended() ? $user->suspend() : $user->unSuspend();
+            !$user->isSuspended() ? $user->suspend() : $user->unSuspend();
         } catch (Exception $exception) {
         } catch (Exception $exception) {
             return redirect()->back()->with('error', $exception->getMessage());
             return redirect()->back()->with('error', $exception->getMessage());
         }
         }
@@ -270,32 +273,42 @@ class UserController extends Controller
     /**
     /**
      * @throws Exception
      * @throws Exception
      */
      */
-    public function dataTable()
+    public function dataTable(Request $request)
     {
     {
-        $query = User::with(['discordUser', 'servers'])->select('users.*');
+        $query =  User::withCount(['servers'])->with('discordUser');
+        // manually count referrals in user_referrals table
+        $query->addSelect(DB::raw('(SELECT COUNT(*) FROM user_referrals WHERE user_referrals.referral_id = users.id) as referrals_count'));
+
+
+        if ($request->has('order')) {
+            $query = $this->sortByColumn($request->input('order'), $request->input('columns'), $query);
+        }
+
 
 
         return datatables($query)
         return datatables($query)
             ->addColumn('avatar', function (User $user) {
             ->addColumn('avatar', function (User $user) {
-                return '<img width="28px" height="28px" class="rounded-circle ml-1" src="'.$user->getAvatar().'">';
+                return '<img width="28px" height="28px" class="rounded-circle ml-1" src="' . $user->getAvatar() . '">';
             })
             })
             ->addColumn('credits', function (User $user) {
             ->addColumn('credits', function (User $user) {
-                return '<i class="fas fa-coins mr-2"></i> '.$user->credits();
+                return '<i class="fas fa-coins mr-2"></i> ' . $user->credits();
             })
             })
             ->addColumn('verified', function (User $user) {
             ->addColumn('verified', function (User $user) {
                 return $user->getVerifiedStatus();
                 return $user->getVerifiedStatus();
             })
             })
-            ->addColumn('servers', function (User $user) {
-                return $user->servers->count();
+            ->addColumn('servers_count', function (User $user) {
+                return $user->servers_count;
             })
             })
-            ->addColumn('referrals', function (User $user) {
-                return DB::table('user_referrals')->where('referral_id', '=', $user->id)->count();
+            ->addColumn('referrals_count', function (User $user) {
+                return $user->referrals_count;
             })
             })
             ->addColumn('discordId', function (User $user) {
             ->addColumn('discordId', function (User $user) {
                 return $user->discordUser ? $user->discordUser->id : '';
                 return $user->discordUser ? $user->discordUser->id : '';
             })
             })
             ->addColumn('last_seen', function (User $user) {
             ->addColumn('last_seen', function (User $user) {
-                return ['display' => $user->last_seen ? $user->last_seen->diffForHumans() : '',
-                    'raw' => $user->last_seen ? strtotime($user->last_seen) : '', ];
+                return [
+                    'display' => $user->last_seen ? $user->last_seen->diffForHumans() : __('Never'),
+                    'raw' => $user->last_seen ? strtotime($user->last_seen) : '',
+                ];
             })
             })
             ->addColumn('actions', function (User $user) {
             ->addColumn('actions', function (User $user) {
                 $suspendColor = $user->isSuspended() ? 'btn-success' : 'btn-warning';
                 $suspendColor = $user->isSuspended() ? 'btn-success' : 'btn-warning';
@@ -303,19 +316,19 @@ class UserController extends Controller
                 $suspendText = $user->isSuspended() ? __('Unsuspend') : __('Suspend');
                 $suspendText = $user->isSuspended() ? __('Unsuspend') : __('Suspend');
 
 
                 return '
                 return '
-                <a data-content="'.__('Login as User').'" data-toggle="popover" data-trigger="hover" data-placement="top" href="'.route('admin.users.loginas', $user->id).'" class="btn btn-sm btn-primary mr-1"><i class="fas fa-sign-in-alt"></i></a>
-                <a data-content="'.__('Verify').'" data-toggle="popover" data-trigger="hover" data-placement="top" href="'.route('admin.users.verifyEmail', $user->id).'" class="btn btn-sm btn-secondary mr-1"><i class="fas fa-envelope"></i></a>
-                <a data-content="'.__('Show').'" data-toggle="popover" data-trigger="hover" data-placement="top"  href="'.route('admin.users.show', $user->id).'" class="btn btn-sm text-white btn-warning mr-1"><i class="fas fa-eye"></i></a>
-                <a data-content="'.__('Edit').'" data-toggle="popover" data-trigger="hover" data-placement="top"  href="'.route('admin.users.edit', $user->id).'" class="btn btn-sm btn-info mr-1"><i class="fas fa-pen"></i></a>
-               <form class="d-inline" method="post" action="'.route('admin.users.togglesuspend', $user->id).'">
-                            '.csrf_field().'
-                           <button data-content="'.$suspendText.'" data-toggle="popover" data-trigger="hover" data-placement="top" class="btn btn-sm '.$suspendColor.' text-white mr-1"><i class="far '.$suspendIcon.'"></i></button>
-                       </form>
-                <form class="d-inline" onsubmit="return submitResult();" method="post" action="'.route('admin.users.destroy', $user->id).'">
-                            '.csrf_field().'
-                            '.method_field('DELETE').'
-                           <button data-content="'.__('Delete').'" data-toggle="popover" data-trigger="hover" data-placement="top" class="btn btn-sm btn-danger mr-1"><i class="fas fa-trash"></i></button>
-                       </form>
+                <a data-content="' . __('Login as User') . '" data-toggle="popover" data-trigger="hover" data-placement="top" href="' . route('admin.users.loginas', $user->id) . '" class="btn btn-sm btn-primary mr-1"><i class="fas fa-sign-in-alt"></i></a>
+                <a data-content="' . __('Verify') . '" data-toggle="popover" data-trigger="hover" data-placement="top" href="' . route('admin.users.verifyEmail', $user->id) . '" class="btn btn-sm btn-secondary mr-1"><i class="fas fa-envelope"></i></a>
+                <a data-content="' . __('Show') . '" data-toggle="popover" data-trigger="hover" data-placement="top"  href="' . route('admin.users.show', $user->id) . '" class="btn btn-sm text-white btn-warning mr-1"><i class="fas fa-eye"></i></a>
+                <a data-content="' . __('Edit') . '" data-toggle="popover" data-trigger="hover" data-placement="top"  href="' . route('admin.users.edit', $user->id) . '" class="btn btn-sm btn-info mr-1"><i class="fas fa-pen"></i></a>
+                <form class="d-inline" method="post" action="' . route('admin.users.togglesuspend', $user->id) . '">
+                             ' . csrf_field() . '
+                            <button data-content="' . $suspendText . '" data-toggle="popover" data-trigger="hover" data-placement="top" class="btn btn-sm ' . $suspendColor . ' text-white mr-1"><i class="far ' . $suspendIcon . '"></i></button>
+                          </form>
+                <form class="d-inline" onsubmit="return submitResult();" method="post" action="' . route('admin.users.destroy', $user->id) . '">
+                             ' . csrf_field() . '
+                             ' . method_field('DELETE') . '
+                            <button data-content="' . __('Delete') . '" data-toggle="popover" data-trigger="hover" data-placement="top" class="btn btn-sm btn-danger mr-1"><i class="fas fa-trash"></i></button>
+                        </form>
                 ';
                 ';
             })
             })
             ->editColumn('role', function (User $user) {
             ->editColumn('role', function (User $user) {
@@ -334,14 +347,14 @@ class UserController extends Controller
                         break;
                         break;
                 }
                 }
 
 
-                return '<span class="badge '.$badgeColor.'">'.$user->role.'</span>';
+                return '<span class="badge ' . $badgeColor . '">' . $user->role . '</span>';
             })
             })
             ->editColumn('name', function (User $user) {
             ->editColumn('name', function (User $user) {
-                return '<a class="text-info" target="_blank" href="'.config('SETTINGS::SYSTEM:PTERODACTYL:URL').'/admin/users/view/'.$user->pterodactyl_id.'">'.strip_tags($user->name).'</a>';
+                return '<a class="text-info" target="_blank" href="' . config('SETTINGS::SYSTEM:PTERODACTYL:URL') . '/admin/users/view/' . $user->pterodactyl_id . '">' . strip_tags($user->name) . '</a>';
             })
             })
-            /*->orderColumn('last_seen', function ($query) {
+            ->orderColumn('last_seen', function ($query) {
                 $query->orderBy('last_seen', "desc");
                 $query->orderBy('last_seen', "desc");
-            })*/
+            })
             ->rawColumns(['avatar', 'name', 'credits', 'role', 'usage', 'referrals', 'actions', 'last_seen'])
             ->rawColumns(['avatar', 'name', 'credits', 'role', 'usage', 'referrals', 'actions', 'last_seen'])
             ->make(true);
             ->make(true);
     }
     }

+ 82 - 44
themes/default/views/admin/users/index.blade.php

@@ -6,12 +6,13 @@
         <div class="container-fluid">
         <div class="container-fluid">
             <div class="row mb-2">
             <div class="row mb-2">
                 <div class="col-sm-6">
                 <div class="col-sm-6">
-                    <h1>{{__('Users')}}</h1>
+                    <h1>{{ __('Users') }}</h1>
                 </div>
                 </div>
                 <div class="col-sm-6">
                 <div class="col-sm-6">
                     <ol class="breadcrumb float-sm-right">
                     <ol class="breadcrumb float-sm-right">
-                        <li class="breadcrumb-item"><a href="{{route('home')}}">{{__('Dashboard')}}</a></li>
-                        <li class="breadcrumb-item"><a class="text-muted" href="{{route('admin.users.index')}}">{{__('Users')}}</a></li>
+                        <li class="breadcrumb-item"><a href="{{ route('home') }}">{{ __('Dashboard') }}</a></li>
+                        <li class="breadcrumb-item"><a class="text-muted"
+                                href="{{ route('admin.users.index') }}">{{ __('Users') }}</a></li>
                     </ol>
                     </ol>
                 </div>
                 </div>
             </div>
             </div>
@@ -27,9 +28,9 @@
 
 
                 <div class="card-header">
                 <div class="card-header">
                     <div class="d-flex justify-content-between">
                     <div class="d-flex justify-content-between">
-                        <h5 class="card-title"><i class="fas fa-users mr-2"></i>{{__('Users')}}</h5>
-                        <a href="{{route('admin.users.notifications')}}" class="btn btn-sm btn-primary"><i
-                                class="fas fa-paper-plane mr-1"></i>{{__('Notify')}}</a>
+                        <h5 class="card-title"><i class="fas fa-users mr-2"></i>{{ __('Users') }}</h5>
+                        <a href="{{ route('admin.users.notifications') }}" class="btn btn-sm btn-primary"><i
+                                class="fas fa-paper-plane mr-1"></i>{{ __('Notify') }}</a>
                     </div>
                     </div>
                 </div>
                 </div>
 
 
@@ -37,21 +38,21 @@
 
 
                     <table id="datatable" class="table table-striped">
                     <table id="datatable" class="table table-striped">
                         <thead>
                         <thead>
-                        <tr>
-                            <th>discordId</th>
-                            <th>ip</th>
-                            <th>pterodactyl_id</th>
-                            <th>{{__('Avatar')}}</th>
-                            <th>{{__('Name')}}</th>
-                            <th>{{__('Role')}}</th>
-                            <th>{{__('Email')}}</th>
-                            <th>{{CREDITS_DISPLAY_NAME}}</th>
-                            <th>{{__('Servers')}}</th>
-                            <th>{{__("Referrals")}}</th>
-                            <th>{{__('Verified')}}</th>
-                            <th>{{__('Last seen')}}</th>
-                            <th></th>
-                        </tr>
+                            <tr>
+                                <th>discordId</th>
+                                <th>ip</th>
+                                <th>pterodactyl_id</th>
+                                <th>{{ __('Avatar') }}</th>
+                                <th>{{ __('Name') }}</th>
+                                <th>{{ __('Role') }}</th>
+                                <th>{{ __('Email') }}</th>
+                                <th>{{ CREDITS_DISPLAY_NAME }}</th>
+                                <th>{{ __('Servers') }}</th>
+                                <th>{{ __('Referrals') }}</th>
+                                <th>{{ __('Verified') }}</th>
+                                <th>{{ __('Last seen') }}</th>
+                                <th></th>
+                            </tr>
                         </thead>
                         </thead>
                         <tbody>
                         <tbody>
                         </tbody>
                         </tbody>
@@ -68,41 +69,78 @@
 
 
     <script>
     <script>
         function submitResult() {
         function submitResult() {
-            return confirm("{{__('Are you sure you wish to delete?')}}") !== false;
+            return confirm("{{ __('Are you sure you wish to delete?') }}") !== false;
         }
         }
 
 
-        document.addEventListener("DOMContentLoaded", function () {
+        document.addEventListener("DOMContentLoaded", function() {
             $('#datatable').DataTable({
             $('#datatable').DataTable({
                 language: {
                 language: {
-                    url: '//cdn.datatables.net/plug-ins/1.11.3/i18n/{{config("SETTINGS::LOCALE:DATATABLES")}}.json'
+                    url: '//cdn.datatables.net/plug-ins/1.11.3/i18n/{{ config('SETTINGS::LOCALE:DATATABLES') }}.json'
                 },
                 },
                 processing: true,
                 processing: true,
                 serverSide: true, //why was this set to false before? increased loadingtimes by 10 seconds
                 serverSide: true, //why was this set to false before? increased loadingtimes by 10 seconds
                 stateSave: true,
                 stateSave: true,
-                ajax: "{{route('admin.users.datatable')}}",
-                order: [[ 11, "asc" ]],
-                columns: [
-                    {data: 'discordId', visible: false, name: 'discordUser.id'},
-                    {data: 'pterodactyl_id', visible: false},
-                    {data: 'ip', visible: false},
-                    {data: 'avatar' , sortable : false},
-                    {data: 'name'},
-                    {data: 'role'},
-                    {data: 'email', name: 'users.email'},
-                    {data: 'credits' , name : 'users.credits'},
-                    {data: 'servers'},
-                    {data: 'referrals'},
-                    {data: 'verified' , sortable : false},
-                    {data: 'last_seen', type: 'num', render: {_: 'display', sort: 'raw'}},
-                    {data: 'actions' , sortable : false},
+                ajax: "{{ route('admin.users.datatable') }}",
+
+                columns: [{
+                        data: 'discordId',
+                        visible: false,
+                        name: 'discordUser.id'
+                    },
+                    {
+                        data: 'pterodactyl_id',
+                        visible: false
+                    },
+                    {
+                        data: 'ip',
+                        visible: false
+                    },
+                    {
+                        data: 'avatar',
+                        sortable: false
+                    },
+                    {
+                        data: 'name'
+                    },
+                    {
+                        data: 'role'
+                    },
+                    {
+                        data: 'email',
+                        name: 'users.email'
+                    },
+                    {
+                        data: 'credits',
+                        name: 'users.credits'
+                    },
+                    {
+                        data: 'servers_count'
+                    },
+                    {
+                        data: 'referrals_count',
+                        sortable: false
+                    },
+                    {
+                        data: 'verified',
+                        sortable: false
+                    },
+                    {
+                        data: 'last_seen',
+                        type: 'num',
+                        render: {
+                            _: 'display',
+                            sort: 'raw'
+                        }
+                    },
+                    {
+                        data: 'actions',
+                        sortable: false
+                    },
                 ],
                 ],
-                fnDrawCallback: function( oSettings ) {
+                fnDrawCallback: function(oSettings) {
                     $('[data-toggle="popover"]').popover();
                     $('[data-toggle="popover"]').popover();
                 }
                 }
             });
             });
         });
         });
     </script>
     </script>
-
-
-
 @endsection
 @endsection