diff --git a/app/Http/Controllers/Admin/AdminUserRoleHistoryController.php b/app/Http/Controllers/Admin/AdminUserRoleHistoryController.php
new file mode 100644
index 000000000..2175414eb
--- /dev/null
+++ b/app/Http/Controllers/Admin/AdminUserRoleHistoryController.php
@@ -0,0 +1,108 @@
+setAdminPrefs();
+
+ $meta_title = $title = 'User Role History';
+
+ // Get all roles for filter
+ $roles = Role::all()->pluck('name', 'id')->toArray();
+
+ // Build query
+ $query = UserRoleHistory::with(['user', 'oldRole', 'newRole', 'changedByUser'])
+ ->orderBy('created_at', 'desc');
+
+ // Apply filters
+ if ($request->has('user_id') && !empty($request->input('user_id'))) {
+ $query->where('user_id', $request->input('user_id'));
+ }
+
+ if ($request->has('username') && !empty($request->input('username'))) {
+ $query->whereHas('user', function ($q) use ($request) {
+ $q->where('username', 'like', '%' . $request->input('username') . '%');
+ });
+ }
+
+ if ($request->has('role_id') && !empty($request->input('role_id'))) {
+ $query->where(function ($q) use ($request) {
+ $q->where('old_role_id', $request->input('role_id'))
+ ->orWhere('new_role_id', $request->input('role_id'));
+ });
+ }
+
+ if ($request->has('change_reason') && !empty($request->input('change_reason'))) {
+ $query->where('change_reason', 'like', '%' . $request->input('change_reason') . '%');
+ }
+
+ if ($request->has('date_from') && !empty($request->input('date_from'))) {
+ $query->where('created_at', '>=', $request->input('date_from') . ' 00:00:00');
+ }
+
+ if ($request->has('date_to') && !empty($request->input('date_to'))) {
+ $query->where('created_at', '<=', $request->input('date_to') . ' 23:59:59');
+ }
+
+ // Pagination
+ $page = $request->has('page') && is_numeric($request->input('page')) ? $request->input('page') : 1;
+ $perPage = config('nntmux.items_per_page', 50);
+
+ $results = $query->paginate($perPage, ['*'], 'page', $page);
+
+ $this->viewData = array_merge($this->viewData, [
+ 'title' => $title,
+ 'meta_title' => $meta_title,
+ 'history' => $results,
+ 'roles' => $roles,
+ 'filters' => [
+ 'user_id' => $request->input('user_id', ''),
+ 'username' => $request->input('username', ''),
+ 'role_id' => $request->input('role_id', ''),
+ 'change_reason' => $request->input('change_reason', ''),
+ 'date_from' => $request->input('date_from', ''),
+ 'date_to' => $request->input('date_to', ''),
+ ],
+ ]);
+
+ return view('admin.user-role-history.index', $this->viewData);
+ }
+
+ /**
+ * Display role history for a specific user
+ */
+ public function show(Request $request, int $userId)
+ {
+ $this->setAdminPrefs();
+
+ $user = User::findOrFail($userId);
+ $meta_title = $title = 'Role History for ' . $user->username;
+
+ $history = UserRoleHistory::with(['oldRole', 'newRole', 'changedByUser'])
+ ->where('user_id', $userId)
+ ->orderBy('created_at', 'desc')
+ ->paginate(config('nntmux.items_per_page', 50));
+
+ $this->viewData = array_merge($this->viewData, [
+ 'title' => $title,
+ 'meta_title' => $meta_title,
+ 'user' => $user,
+ 'history' => $history,
+ ]);
+
+ return view('admin.user-role-history.show', $this->viewData);
+ }
+}
+
diff --git a/resources/views/admin/user-role-history/index.blade.php b/resources/views/admin/user-role-history/index.blade.php
new file mode 100644
index 000000000..9927b57a9
--- /dev/null
+++ b/resources/views/admin/user-role-history/index.blade.php
@@ -0,0 +1,212 @@
+@extends('layouts.admin')
+
+@section('content')
+
+
+
+
+
+
+
+
+
+
+
+
+
+ |
+ Date
+ |
+
+ User
+ |
+
+ Old Role
+ |
+
+ New Role
+ |
+
+ Old Expiry
+ |
+
+ New Expiry
+ |
+
+ Stacked
+ |
+
+ Reason
+ |
+
+ Changed By
+ |
+
+ Actions
+ |
+
+
+
+ @forelse($history as $record)
+
+ |
+ {{ $record->created_at->format('Y-m-d H:i:s') }}
+ |
+
+ @if($record->user)
+
+ {{ $record->user->username }}
+
+ @else
+ User #{{ $record->user_id }}
+ @endif
+ |
+
+ @if($record->oldRole)
+
+ {{ $record->oldRole->name }}
+
+ @else
+ -
+ @endif
+ |
+
+ @if($record->newRole)
+
+ {{ $record->newRole->name }}
+
+ @else
+ -
+ @endif
+ |
+
+ {{ $record->old_expiry_date ? $record->old_expiry_date->format('Y-m-d H:i') : '-' }}
+ |
+
+ {{ $record->new_expiry_date ? $record->new_expiry_date->format('Y-m-d H:i') : '-' }}
+ |
+
+ @if($record->is_stacked)
+
+ Yes
+
+ @else
+ No
+ @endif
+ |
+
+ {{ $record->change_reason ?? '-' }}
+ |
+
+ @if($record->changedByUser)
+ {{ $record->changedByUser->username }}
+ @else
+ System
+ @endif
+ |
+
+
+ @if($record->user)
+
+
+
+
+
+
+ @endif
+
+ |
+
+ @empty
+
+ |
+ No role history records found
+ |
+
+ @endforelse
+
+
+
+
+
+ @if($history->hasPages())
+
+ {{ $history->links() }}
+
+ @endif
+
+
+@endsection
+
diff --git a/resources/views/admin/user-role-history/show.blade.php b/resources/views/admin/user-role-history/show.blade.php
new file mode 100644
index 000000000..005468607
--- /dev/null
+++ b/resources/views/admin/user-role-history/show.blade.php
@@ -0,0 +1,173 @@
+@extends('layouts.admin')
+
+@section('content')
+
+
+
+
+
+
+
+
+
+
+
{{ $user->username }}
+
+
+
+
{{ $user->email }}
+
+
+
+
+ @if($user->role)
+
+ {{ $user->role->name }}
+
+ @else
+ No role assigned
+ @endif
+
+
+
+ @if($user->rolechangedate)
+
+
+
+ {{ \Carbon\Carbon::parse($user->rolechangedate)->format('Y-m-d H:i:s') }}
+ @if(\Carbon\Carbon::parse($user->rolechangedate)->isPast())
+
+ Expired
+
+ @else
+
+ Active
+
+ @endif
+
+
+ @endif
+
+
+
+
+
+
+
+ |
+ Date
+ |
+
+ Old Role
+ |
+
+ New Role
+ |
+
+ Old Expiry
+ |
+
+ New Expiry
+ |
+
+ Effective Date
+ |
+
+ Stacked
+ |
+
+ Reason
+ |
+
+ Changed By
+ |
+
+
+
+ @forelse($history as $record)
+
+ |
+ {{ $record->created_at->format('Y-m-d H:i:s') }}
+ |
+
+ @if($record->oldRole)
+
+ {{ $record->oldRole->name }}
+
+ @else
+ -
+ @endif
+ |
+
+ @if($record->newRole)
+
+ {{ $record->newRole->name }}
+
+ @else
+ -
+ @endif
+ |
+
+ {{ $record->old_expiry_date ? $record->old_expiry_date->format('Y-m-d H:i') : '-' }}
+ |
+
+ {{ $record->new_expiry_date ? $record->new_expiry_date->format('Y-m-d H:i') : '-' }}
+ |
+
+ {{ $record->effective_date ? $record->effective_date->format('Y-m-d H:i') : '-' }}
+ |
+
+ @if($record->is_stacked)
+
+ Yes
+
+ @else
+ No
+ @endif
+ |
+
+ {{ $record->change_reason ?? '-' }}
+ |
+
+ @if($record->changedByUser)
+ {{ $record->changedByUser->username }}
+ @else
+ System
+ @endif
+ |
+
+ @empty
+
+ |
+ No role history records found for this user
+ |
+
+ @endforelse
+
+
+
+
+
+ @if($history->hasPages())
+
+ {{ $history->links() }}
+
+ @endif
+
+
+@endsection
+
diff --git a/resources/views/admin/users/edit.blade.php b/resources/views/admin/users/edit.blade.php
index 9750c68f8..82e71833d 100644
--- a/resources/views/admin/users/edit.blade.php
+++ b/resources/views/admin/users/edit.blade.php
@@ -5,9 +5,16 @@
diff --git a/resources/views/admin/users/index.blade.php b/resources/views/admin/users/index.blade.php
index 3003d1616..ac1e98bf4 100644
--- a/resources/views/admin/users/index.blade.php
+++ b/resources/views/admin/users/index.blade.php
@@ -315,6 +315,11 @@
title="Edit">
+
+
+
@if(!$user->verified)