feat: add user impersonation service
This commit is contained in:
86
resources/views/components/impersonation-banner.blade.php
Normal file
86
resources/views/components/impersonation-banner.blade.php
Normal file
@@ -0,0 +1,86 @@
|
||||
{{--
|
||||
Impersonation Banner Component
|
||||
|
||||
This component displays a prominent banner when an admin is impersonating a user.
|
||||
It shows who is being impersonated, remaining time, and provides a stop button.
|
||||
--}}
|
||||
|
||||
@if($isImpersonating)
|
||||
<div class="fixed top-0 left-0 right-0 z-50 bg-amber-600 text-white shadow-lg">
|
||||
<div class="container mx-auto px-4 py-3">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center space-x-4">
|
||||
<!-- Impersonation Icon -->
|
||||
<div class="flex-shrink-0">
|
||||
<svg class="h-6 w-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<!-- Impersonation Info -->
|
||||
<div>
|
||||
<div class="font-semibold text-sm">
|
||||
Impersonating: {{ $impersonationTarget?->name ?? 'Unknown User' }}
|
||||
</div>
|
||||
<div class="text-xs opacity-90">
|
||||
{{ $impersonationTarget?->email }}
|
||||
@if($impersonationRemainingMinutes > 0)
|
||||
• {{ $impersonationRemainingMinutes }} minutes remaining
|
||||
@else
|
||||
• Session expiring soon
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Action Buttons -->
|
||||
<div class="flex items-center space-x-3">
|
||||
<!-- Timer Warning -->
|
||||
@if($impersonationRemainingMinutes <= 5 && $impersonationRemainingMinutes > 0)
|
||||
<div class="text-xs bg-white/20 px-2 py-1 rounded">
|
||||
⚠️ Less than 5 minutes remaining
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<!-- Stop Impersonation Button -->
|
||||
<form action="{{ route('impersonation.stop') }}" method="POST" class="inline">
|
||||
@csrf
|
||||
<button type="submit"
|
||||
class="bg-white text-red-600 hover:bg-red-50 px-4 py-2 rounded-md text-sm font-medium transition-colors duration-200 flex items-center space-x-2 cursor-pointer">
|
||||
<svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18" />
|
||||
</svg>
|
||||
<span>Stop Impersonating</span>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Progress Bar -->
|
||||
@if($impersonationRemainingMinutes > 0)
|
||||
<div class="mt-2 bg-white/20 rounded-full h-1">
|
||||
<div class="bg-white h-1 rounded-full transition-all duration-1000 ease-linear"
|
||||
style="width: {{ ($impersonationRemainingMinutes / 30) * 100 }}%"></div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Add padding to body content to account for fixed banner -->
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Add top padding to main content to account for fixed banner
|
||||
const mainContent = document.querySelector('main') || document.querySelector('[role="main"]') || document.querySelector('.flux-main') || document.body;
|
||||
if (mainContent) {
|
||||
mainContent.style.paddingTop = '80px';
|
||||
}
|
||||
|
||||
// Auto-refresh timer every minute
|
||||
setInterval(function() {
|
||||
// You could implement a small AJAX call here to refresh the remaining time
|
||||
// For now, the page refresh on navigation will update the timer
|
||||
}, 60000);
|
||||
});
|
||||
</script>
|
||||
@endif
|
||||
@@ -26,6 +26,7 @@
|
||||
@yield('custom_header')
|
||||
</head>
|
||||
<body class="min-h-screen bg-white dark:bg-zinc-800">
|
||||
<x-impersonation-banner />
|
||||
<flux:sidebar sticky stashable class="bg-zinc-50 dark:bg-zinc-900 border-r rtl:border-r-0 rtl:border-l border-zinc-200 dark:border-zinc-700">
|
||||
<flux:sidebar.toggle class="lg:hidden" icon="x-mark" />
|
||||
<a class="flex items-center px-2 py-2 cursor-pointer" href="{{ route('home') }}">
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
@yield('custom_header')
|
||||
</head>
|
||||
<body class="min-h-screen bg-white dark:bg-zinc-800">
|
||||
<x-impersonation-banner />
|
||||
<flux:sidebar sticky stashable class="bg-zinc-50 dark:bg-zinc-900 border-r rtl:border-r-0 rtl:border-l border-zinc-200 dark:border-zinc-700">
|
||||
<flux:sidebar.toggle class="lg:hidden" icon="x-mark" />
|
||||
<a class="flex items-center px-2 py-2 cursor-pointer" href="{{ route('dashboard') }}">
|
||||
|
||||
Reference in New Issue
Block a user