Update stacked role view

This commit is contained in:
DariusIII
2025-12-14 20:11:36 +01:00
parent b0f0e2ac51
commit 21ccf85d1f
3 changed files with 183 additions and 40 deletions
+34
View File
@@ -427,6 +427,40 @@ final class User extends Authenticatable
]); ]);
} }
/**
* Get all pending stacked role changes for this user.
* Returns an array of stacked role changes that haven't been activated yet (effective_date is in the future).
*
* @return \Illuminate\Support\Collection<int, array{
* role: Role|null,
* role_name: string,
* start_date: Carbon,
* end_date: Carbon,
* is_current_pending: bool
* }>
*/
public function getAllPendingStackedRoles(): \Illuminate\Support\Collection
{
// Get all stacked role changes from history where effective_date is in the future
$stackedHistory = UserRoleHistory::where('user_id', $this->id)
->where('is_stacked', true)
->where('effective_date', '>', now())
->orderBy('effective_date', 'asc')
->get();
return $stackedHistory->map(function ($history) {
$role = Role::find($history->new_role_id);
return [
'role' => $role,
'role_name' => $role?->name ?? 'Unknown Role',
'start_date' => $history->effective_date,
'end_date' => $history->new_expiry_date,
'is_current_pending' => $this->pending_roles_id === $history->new_role_id
&& $this->pending_role_start_date?->equalTo($history->effective_date),
];
});
}
/** /**
* Get comprehensive role expiry information. * Get comprehensive role expiry information.
* *
+105 -39
View File
@@ -322,51 +322,117 @@
</div> </div>
<!-- Pending Role Information --> <!-- Pending Role Information -->
@if(!is_array($user) && !empty($user->pending_roles_id) && !empty($user->pending_role_start_date)) @if(!is_array($user))
<div class="border-2 border-blue-300 dark:border-blue-600 rounded-lg p-4 bg-gradient-to-br from-blue-50 via-blue-50 to-white dark:from-blue-900/30 dark:via-blue-900/20 dark:to-gray-800 shadow-md"> @php
<div class="flex items-center justify-between mb-3"> $allPendingRoles = $user->getAllPendingStackedRoles();
<label class="text-sm font-semibold text-blue-700 dark:text-blue-300 flex items-center"> @endphp
<i class="fa fa-layer-group mr-2 text-lg"></i> @if($allPendingRoles->count() > 0)
Pending Stacked Role <div class="border-2 border-blue-300 dark:border-blue-600 rounded-lg p-4 bg-gradient-to-br from-blue-50 via-blue-50 to-white dark:from-blue-900/30 dark:via-blue-900/20 dark:to-gray-800 shadow-md">
</label> <div class="flex items-center justify-between mb-3">
<span class="px-3 py-1 inline-flex items-center text-xs leading-5 font-bold rounded-full bg-blue-200 dark:bg-blue-800 text-blue-800 dark:text-blue-100 animate-pulse"> <label class="text-sm font-semibold text-blue-700 dark:text-blue-300 flex items-center">
<i class="fa fa-clock mr-1"></i> SCHEDULED <i class="fa fa-layer-group mr-2 text-lg"></i>
</span> Pending Stacked Role{{ $allPendingRoles->count() > 1 ? 's' : '' }}
<span class="ml-2 px-2 py-0.5 bg-blue-600 text-white text-xs rounded-full">{{ $allPendingRoles->count() }}</span>
</label>
<span class="px-3 py-1 inline-flex items-center text-xs leading-5 font-bold rounded-full bg-blue-200 dark:bg-blue-800 text-blue-800 dark:text-blue-100 animate-pulse">
<i class="fa fa-clock mr-1"></i> SCHEDULED
</span>
</div>
<div class="space-y-3">
@foreach($allPendingRoles as $index => $pendingRoleInfo)
<div class="bg-white dark:bg-gray-700 rounded-md p-3 border border-blue-200 dark:border-blue-700 @if(!$loop->last) mb-2 @endif">
<div class="flex items-center mb-2">
<span class="flex items-center justify-center w-6 h-6 bg-blue-600 text-white text-xs font-bold rounded-full mr-2">
{{ $index + 1 }}
</span>
<span class="text-sm font-bold text-blue-700 dark:text-blue-300">{{ $pendingRoleInfo['role_name'] }}</span>
</div>
<div class="ml-8 space-y-1 text-sm">
<div class="flex items-center justify-between">
<span class="text-xs font-medium text-gray-600 dark:text-gray-300">
<i class="fa fa-play-circle text-green-500 mr-1"></i>Starts:
</span>
<span class="font-semibold text-gray-900 dark:text-gray-100">{{ $pendingRoleInfo['start_date']->format('M j, Y g:i A') }}</span>
</div>
@if($pendingRoleInfo['end_date'])
<div class="flex items-center justify-between">
<span class="text-xs font-medium text-gray-600 dark:text-gray-300">
<i class="fa fa-stop-circle text-red-400 mr-1"></i>Ends:
</span>
<span class="font-semibold text-gray-900 dark:text-gray-100">{{ $pendingRoleInfo['end_date']->format('M j, Y g:i A') }}</span>
</div>
@endif
<div class="flex items-center justify-between">
<span class="text-xs text-gray-600 dark:text-gray-400">Time until activation:</span>
<span class="text-sm font-semibold text-blue-600 dark:text-blue-400">{{ $pendingRoleInfo['start_date']->diffForHumans() }}</span>
</div>
</div>
</div>
@endforeach
</div>
<div class="flex items-center justify-between mt-3">
<p class="text-xs text-blue-700 dark:text-blue-300">
<i class="fa fa-info-circle mr-1"></i>
These roles will automatically activate in sequence as each previous role expires
</p>
@if(!empty($user->pending_roles_id))
<label class="inline-flex items-center px-3 py-2 rounded-md bg-red-50 dark:bg-red-900/30 border border-red-200 dark:border-red-700 hover:bg-red-100 dark:hover:bg-red-900/50 cursor-pointer transition-all">
<input type="checkbox" name="cancel_pending_role" value="1"
class="rounded border-red-300 dark:border-red-600 bg-white dark:bg-gray-700 text-red-600 dark:text-red-500 shadow-sm focus:ring-2 focus:ring-red-500 dark:focus:ring-red-400 focus:border-red-500 dark:focus:border-red-400 cursor-pointer">
<span class="ml-2 text-xs text-red-700 dark:text-red-300 font-semibold">Cancel all pending roles</span>
</label>
@endif
</div>
</div> </div>
@elseif(!empty($user->pending_roles_id) && !empty($user->pending_role_start_date))
@php {{-- Fallback to old display if no history records but pending_roles_id is set --}}
$pendingRole = \Spatie\Permission\Models\Role::find($user->pending_roles_id); <div class="border-2 border-blue-300 dark:border-blue-600 rounded-lg p-4 bg-gradient-to-br from-blue-50 via-blue-50 to-white dark:from-blue-900/30 dark:via-blue-900/20 dark:to-gray-800 shadow-md">
$pendingStartDate = \Carbon\Carbon::parse($user->pending_role_start_date); <div class="flex items-center justify-between mb-3">
$daysUntilActivation = $pendingStartDate->diffInDays(now()); <label class="text-sm font-semibold text-blue-700 dark:text-blue-300 flex items-center">
@endphp <i class="fa fa-layer-group mr-2 text-lg"></i>
Pending Stacked Role
<div class="bg-white dark:bg-gray-700 rounded-md p-3 mb-3 border border-blue-200 dark:border-blue-700"> </label>
<div class="flex items-center justify-between mb-2"> <span class="px-3 py-1 inline-flex items-center text-xs leading-5 font-bold rounded-full bg-blue-200 dark:bg-blue-800 text-blue-800 dark:text-blue-100 animate-pulse">
<span class="text-xs font-medium text-gray-600 dark:text-gray-300">Will change to:</span> <i class="fa fa-clock mr-1"></i> SCHEDULED
<span class="text-sm font-bold text-blue-700 dark:text-blue-300">{{ $pendingRole->name ?? 'Unknown' }}</span> </span>
</div> </div>
<div class="flex items-center justify-between mb-2">
<span class="text-xs font-medium text-gray-600 dark:text-gray-300">Activation date:</span> @php
<span class="text-sm font-semibold text-gray-900 dark:text-gray-100">{{ $pendingStartDate->format('M j, Y g:i A') }}</span> $pendingRole = \Spatie\Permission\Models\Role::find($user->pending_roles_id);
$pendingStartDate = \Carbon\Carbon::parse($user->pending_role_start_date);
$daysUntilActivation = $pendingStartDate->diffInDays(now());
@endphp
<div class="bg-white dark:bg-gray-700 rounded-md p-3 mb-3 border border-blue-200 dark:border-blue-700">
<div class="flex items-center justify-between mb-2">
<span class="text-xs font-medium text-gray-600 dark:text-gray-300">Will change to:</span>
<span class="text-sm font-bold text-blue-700 dark:text-blue-300">{{ $pendingRole->name ?? 'Unknown' }}</span>
</div>
<div class="flex items-center justify-between mb-2">
<span class="text-xs font-medium text-gray-600 dark:text-gray-300">Activation date:</span>
<span class="text-sm font-semibold text-gray-900 dark:text-gray-100">{{ $pendingStartDate->format('M j, Y g:i A') }}</span>
</div>
<div class="flex items-center justify-between">
<span class="text-xs text-gray-600 dark:text-gray-400">Time until activation:</span>
<span class="text-sm font-semibold text-blue-600 dark:text-blue-400">{{ $pendingStartDate->diffForHumans() }}</span>
</div>
</div> </div>
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<span class="text-xs text-gray-600 dark:text-gray-400">Time until activation:</span> <p class="text-xs text-blue-700 dark:text-blue-300">
<span class="text-sm font-semibold text-blue-600 dark:text-blue-400">{{ $pendingStartDate->diffForHumans() }}</span> <i class="fa fa-info-circle mr-1"></i>
This role will automatically activate when the current role expires
</p>
<label class="inline-flex items-center px-3 py-2 rounded-md bg-red-50 dark:bg-red-900/30 border border-red-200 dark:border-red-700 hover:bg-red-100 dark:hover:bg-red-900/50 cursor-pointer transition-all">
<input type="checkbox" name="cancel_pending_role" value="1"
class="rounded border-red-300 dark:border-red-600 bg-white dark:bg-gray-700 text-red-600 dark:text-red-500 shadow-sm focus:ring-2 focus:ring-red-500 dark:focus:ring-red-400 focus:border-red-500 dark:focus:border-red-400 cursor-pointer">
<span class="ml-2 text-xs text-red-700 dark:text-red-300 font-semibold">Cancel pending role</span>
</label>
</div> </div>
</div> </div>
@endif
<div class="flex items-center justify-between">
<p class="text-xs text-blue-700 dark:text-blue-300">
<i class="fa fa-info-circle mr-1"></i>
This role will automatically activate when the current role expires
</p>
<label class="inline-flex items-center px-3 py-2 rounded-md bg-red-50 dark:bg-red-900/30 border border-red-200 dark:border-red-700 hover:bg-red-100 dark:hover:bg-red-900/50 cursor-pointer transition-all">
<input type="checkbox" name="cancel_pending_role" value="1"
class="rounded border-red-300 dark:border-red-600 bg-white dark:bg-gray-700 text-red-600 dark:text-red-500 shadow-sm focus:ring-2 focus:ring-red-500 dark:focus:ring-red-400 focus:border-red-500 dark:focus:border-red-400 cursor-pointer">
<span class="ml-2 text-xs text-red-700 dark:text-red-300 font-semibold">Cancel pending role</span>
</label>
</div>
</div>
@endif @endif
<!-- Role Stacking Option --> <!-- Role Stacking Option -->
+44 -1
View File
@@ -139,7 +139,50 @@
</div> </div>
@endif @endif
@if($user->hasPendingRole()) @php
$allPendingRoles = $user->getAllPendingStackedRoles();
@endphp
@if($allPendingRoles->count() > 0)
<div class="flex border-b border-gray-200 dark:border-gray-700 pb-3">
<div class="w-1/3 text-gray-600">Pending Role{{ $allPendingRoles->count() > 1 ? 's' : '' }}</div>
<div class="w-2/3">
<div class="space-y-3">
@foreach($allPendingRoles as $index => $pendingRoleInfo)
<div class="@if(!$loop->last) pb-3 border-b border-gray-100 dark:border-gray-700 @endif">
<div class="flex items-center">
<span class="flex items-center justify-center w-6 h-6 bg-blue-100 dark:bg-blue-900 text-blue-600 dark:text-blue-400 text-xs font-bold rounded-full mr-2">
{{ $index + 1 }}
</span>
<i class="fa fa-layer-group text-blue-400 mr-2"></i>
<span class="font-medium text-blue-600 dark:text-blue-400">{{ $pendingRoleInfo['role_name'] }}</span>
<span class="ml-2 px-2 py-1 bg-blue-100 dark:bg-blue-900 text-blue-800 dark:text-blue-200 text-xs rounded">
<i class="fa fa-clock mr-1"></i>Scheduled
</span>
</div>
<div class="ml-8 mt-1 text-sm text-gray-600 dark:text-gray-400">
<div>
<i class="fa fa-play-circle text-green-500 mr-1"></i>
<span class="font-medium">Starts:</span> {{ $pendingRoleInfo['start_date']->format('M d, Y g:i A') }}
<span class="text-gray-400">({{ $pendingRoleInfo['start_date']->diffForHumans() }})</span>
</div>
@if($pendingRoleInfo['end_date'])
<div class="mt-1">
<i class="fa fa-stop-circle text-red-400 mr-1"></i>
<span class="font-medium">Ends:</span> {{ $pendingRoleInfo['end_date']->format('M d, Y g:i A') }}
</div>
@endif
</div>
</div>
@endforeach
<div class="text-xs text-blue-600 dark:text-blue-400 bg-blue-50 dark:bg-blue-900/20 p-2 rounded mt-2">
<i class="fa fa-info-circle mr-1"></i>
These roles will automatically activate in sequence as each previous role expires
</div>
</div>
</div>
</div>
@elseif($user->hasPendingRole())
{{-- Fallback to old display if no history records but pending_roles_id is set --}}
<div class="flex border-b border-gray-200 dark:border-gray-700 pb-3"> <div class="flex border-b border-gray-200 dark:border-gray-700 pb-3">
<div class="w-1/3 text-gray-600">Pending Role</div> <div class="w-1/3 text-gray-600">Pending Role</div>
<div class="w-2/3"> <div class="w-2/3">