289 lines
15 KiB
PHP
289 lines
15 KiB
PHP
<div class="space-y-6" wire:key="notification-center">
|
|
{{-- Header --}}
|
|
<div class="flex items-center justify-between">
|
|
<div>
|
|
<flux:heading size="lg">Notification Center</flux:heading>
|
|
<flux:subheading>Configure and monitor notification settings</flux:subheading>
|
|
</div>
|
|
<flux:button wire:click="$set('showSettingsModal', true)" variant="primary" size="sm" icon="cog-6-tooth">
|
|
Global Settings
|
|
</flux:button>
|
|
</div>
|
|
|
|
{{-- Notification Stats --}}
|
|
<div class="grid grid-cols-1 md:grid-cols-4 gap-4">
|
|
<flux:card>
|
|
<div class="text-center">
|
|
<div class="text-2xl font-bold text-blue-600 dark:text-blue-400">{{ $this->stats['notifications_sent_today'] }}</div>
|
|
<div class="text-sm text-gray-600 dark:text-gray-300">Sent Today</div>
|
|
</div>
|
|
</flux:card>
|
|
<flux:card>
|
|
<div class="text-center">
|
|
<div class="text-2xl font-bold text-green-600 dark:text-green-400">{{ $this->stats['delivery_success_rate'] }}%</div>
|
|
<div class="text-sm text-gray-600 dark:text-gray-300">Success Rate</div>
|
|
</div>
|
|
</flux:card>
|
|
<flux:card>
|
|
<div class="text-center">
|
|
<div class="text-2xl font-bold text-yellow-600 dark:text-yellow-400">{{ $this->stats['pending_notifications'] }}</div>
|
|
<div class="text-sm text-gray-600 dark:text-gray-300">Pending</div>
|
|
</div>
|
|
</flux:card>
|
|
<flux:card>
|
|
<div class="text-center">
|
|
<div class="text-2xl font-bold text-red-600 dark:text-red-400">{{ $this->stats['failed_notifications'] }}</div>
|
|
<div class="text-sm text-gray-600 dark:text-gray-300">Failed</div>
|
|
</div>
|
|
</flux:card>
|
|
</div>
|
|
|
|
{{-- Filters --}}
|
|
<flux:card>
|
|
<div class="grid grid-cols-1 md:grid-cols-4 gap-4">
|
|
<flux:input wire:model.live="filters.search" placeholder="Search notifications..." icon="magnifying-glass" />
|
|
|
|
<flux:select wire:model.live="filters.channel">
|
|
<option value="">All Channels</option>
|
|
<option value="email">Email</option>
|
|
<option value="sms">SMS</option>
|
|
<option value="push">Push</option>
|
|
<option value="webhook">Webhook</option>
|
|
</flux:select>
|
|
|
|
<flux:select wire:model.live="filters.status">
|
|
<option value="">All Status</option>
|
|
<option value="sent">Sent</option>
|
|
<option value="delivered">Delivered</option>
|
|
<option value="failed">Failed</option>
|
|
<option value="pending">Pending</option>
|
|
</flux:select>
|
|
|
|
<flux:select wire:model.live="filters.event_type">
|
|
<option value="">All Events</option>
|
|
<option value="geofence_enter">Geofence Enter</option>
|
|
<option value="geofence_exit">Geofence Exit</option>
|
|
<option value="speeding">Speeding</option>
|
|
<option value="device_offline">Device Offline</option>
|
|
<option value="low_battery">Low Battery</option>
|
|
<option value="maintenance_due">Maintenance Due</option>
|
|
</flux:select>
|
|
</div>
|
|
</flux:card>
|
|
|
|
{{-- User Notification Settings --}}
|
|
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
|
{{-- User Settings Table --}}
|
|
<flux:card>
|
|
<div class="mb-4">
|
|
<flux:heading size="base">User Notification Settings</flux:heading>
|
|
<flux:subheading>Configure individual user preferences</flux:subheading>
|
|
</div>
|
|
|
|
<div class="overflow-x-auto">
|
|
<table class="min-w-full divide-y divide-gray-200">
|
|
<thead class="bg-gray-50 dark:bg-gray-700">
|
|
<tr>
|
|
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase">User</th>
|
|
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase">Email</th>
|
|
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase">SMS</th>
|
|
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase">Push</th>
|
|
<th class="px-4 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-300 uppercase">Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700">
|
|
@forelse($userSettings as $setting)
|
|
<tr>
|
|
<td class="px-4 py-4 whitespace-nowrap">
|
|
<div class="text-sm font-medium text-gray-900 dark:text-gray-100">{{ $setting->user->name }}</div>
|
|
<div class="text-xs text-gray-500 dark:text-gray-400">{{ $setting->user->email }}</div>
|
|
</td>
|
|
<td class="px-4 py-4 whitespace-nowrap">
|
|
<flux:badge variant="{{ $setting->email_enabled ? 'success' : 'outline' }}" size="sm">
|
|
{{ $setting->email_enabled ? 'On' : 'Off' }}
|
|
</flux:badge>
|
|
</td>
|
|
<td class="px-4 py-4 whitespace-nowrap">
|
|
<flux:badge variant="{{ $setting->sms_enabled ? 'success' : 'outline' }}" size="sm">
|
|
{{ $setting->sms_enabled ? 'On' : 'Off' }}
|
|
</flux:badge>
|
|
</td>
|
|
<td class="px-4 py-4 whitespace-nowrap">
|
|
<flux:badge variant="{{ $setting->push_enabled ? 'success' : 'outline' }}" size="sm">
|
|
{{ $setting->push_enabled ? 'On' : 'Off' }}
|
|
</flux:badge>
|
|
</td>
|
|
<td class="px-4 py-4 whitespace-nowrap text-right">
|
|
<flux:button wire:click="editUserSettings({{ $setting->user_id }})" variant="outline" size="xs">
|
|
Edit
|
|
</flux:button>
|
|
</td>
|
|
</tr>
|
|
@empty
|
|
<tr>
|
|
<td colspan="5" class="px-4 py-4 text-center text-gray-500">
|
|
No user settings found.
|
|
</td>
|
|
</tr>
|
|
@endforelse
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</flux:card>
|
|
|
|
{{-- Notification Log --}}
|
|
<flux:card>
|
|
<div class="mb-4">
|
|
<flux:heading size="base">Recent Notifications</flux:heading>
|
|
<flux:subheading>Last 24 hours activity</flux:subheading>
|
|
</div>
|
|
|
|
<div class="space-y-3 max-h-96 overflow-y-auto">
|
|
@forelse($recentNotifications as $notification)
|
|
<div class="flex items-start space-x-3 p-3 bg-gray-50 rounded-lg">
|
|
<div class="flex-shrink-0">
|
|
@if($notification->channel === 'email')
|
|
<div class="w-8 h-8 bg-blue-100 rounded-full flex items-center justify-center">
|
|
<flux:icon.envelope class="w-4 h-4 text-blue-600" />
|
|
</div>
|
|
@elseif($notification->channel === 'sms')
|
|
<div class="w-8 h-8 bg-green-100 rounded-full flex items-center justify-center">
|
|
<flux:icon.device-phone-mobile class="w-4 h-4 text-green-600" />
|
|
</div>
|
|
@elseif($notification->channel === 'push')
|
|
<div class="w-8 h-8 bg-purple-100 rounded-full flex items-center justify-center">
|
|
<flux:icon.bell class="w-4 h-4 text-purple-600" />
|
|
</div>
|
|
@else
|
|
<div class="w-8 h-8 bg-gray-100 rounded-full flex items-center justify-center">
|
|
<flux:icon.globe-alt class="w-4 h-4 text-gray-600" />
|
|
</div>
|
|
@endif
|
|
</div>
|
|
<div class="flex-1 min-w-0">
|
|
<div class="text-sm font-medium text-gray-900">{{ $notification->event_type }}</div>
|
|
<div class="text-sm text-gray-500">{{ $notification->recipient }}</div>
|
|
<div class="text-xs text-gray-400">
|
|
{{ $notification->created_at->diffForHumans() }} •
|
|
<span class="
|
|
@if($notification->status === 'delivered') text-green-600
|
|
@elseif($notification->status === 'failed') text-red-600
|
|
@else text-yellow-600 @endif
|
|
">
|
|
{{ ucfirst($notification->status) }}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@empty
|
|
<div class="text-center text-gray-500 py-8">
|
|
No recent notifications
|
|
</div>
|
|
@endforelse
|
|
</div>
|
|
</flux:card>
|
|
</div>
|
|
|
|
{{-- Global Settings Modal --}}
|
|
<flux:modal name="global-settings" x-show="$wire.showSettingsModal" class="md:max-w-lg">
|
|
<form wire:submit="updateGlobalSettings">
|
|
<div class="space-y-6">
|
|
<div>
|
|
<flux:heading size="lg">Global Notification Settings</flux:heading>
|
|
<flux:subheading>Configure system-wide defaults</flux:subheading>
|
|
</div>
|
|
|
|
<div class="space-y-4">
|
|
<div class="grid grid-cols-2 gap-4">
|
|
<div>
|
|
<flux:switch wire:model="globalSettings.email_enabled" label="Email Notifications" />
|
|
</div>
|
|
<div>
|
|
<flux:switch wire:model="globalSettings.sms_enabled" label="SMS Notifications" />
|
|
</div>
|
|
</div>
|
|
|
|
<div class="grid grid-cols-2 gap-4">
|
|
<div>
|
|
<flux:switch wire:model="globalSettings.push_enabled" label="Push Notifications" />
|
|
</div>
|
|
<div>
|
|
<flux:switch wire:model="globalSettings.webhook_enabled" label="Webhook Notifications" />
|
|
</div>
|
|
</div>
|
|
|
|
<flux:input wire:model="globalSettings.max_daily_notifications" label="Max Daily Notifications" type="number" />
|
|
|
|
<div class="grid grid-cols-2 gap-4">
|
|
<flux:input wire:model="globalSettings.quiet_hours_start" label="Quiet Hours Start" type="time" />
|
|
<flux:input wire:model="globalSettings.quiet_hours_end" label="Quiet Hours End" type="time" />
|
|
</div>
|
|
|
|
<flux:input wire:model="globalSettings.webhook_url" label="Webhook URL" type="url" placeholder="https://your-webhook-url.com" />
|
|
</div>
|
|
|
|
<div class="flex justify-end space-x-2">
|
|
<flux:button type="button" variant="ghost" wire:click="closeSettingsModal">Cancel</flux:button>
|
|
<flux:button type="submit" variant="primary">Save Settings</flux:button>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</flux:modal>
|
|
|
|
{{-- User Settings Modal --}}
|
|
<flux:modal name="user-settings" x-show="$wire.showUserSettingsModal" class="md:max-w-lg">
|
|
@if($editingUserSettings)
|
|
<form wire:submit="updateUserSettings">
|
|
<div class="space-y-6">
|
|
<div>
|
|
<flux:heading size="lg">Edit User Settings</flux:heading>
|
|
<flux:subheading>{{ $editingUserSettings->user->name }}</flux:subheading>
|
|
</div>
|
|
|
|
<div class="space-y-4">
|
|
<div class="grid grid-cols-2 gap-4">
|
|
<div>
|
|
<flux:switch wire:model="userSettingsForm.email_enabled" label="Email Notifications" />
|
|
</div>
|
|
<div>
|
|
<flux:switch wire:model="userSettingsForm.sms_enabled" label="SMS Notifications" />
|
|
</div>
|
|
</div>
|
|
|
|
<div class="grid grid-cols-2 gap-4">
|
|
<div>
|
|
<flux:switch wire:model="userSettingsForm.push_enabled" label="Push Notifications" />
|
|
</div>
|
|
<div>
|
|
<flux:switch wire:model="userSettingsForm.webhook_enabled" label="Webhook Notifications" />
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<flux:label>Event Types</flux:label>
|
|
<div class="space-y-2">
|
|
@foreach(['geofence_enter', 'geofence_exit', 'speeding', 'device_offline', 'low_battery', 'maintenance_due'] as $eventType)
|
|
<label class="flex items-center space-x-2">
|
|
<flux:checkbox wire:model="userSettingsForm.event_types" value="{{ $eventType }}" />
|
|
<span class="text-sm">{{ ucwords(str_replace('_', ' ', $eventType)) }}</span>
|
|
</label>
|
|
@endforeach
|
|
</div>
|
|
</div>
|
|
|
|
<div class="grid grid-cols-2 gap-4">
|
|
<flux:input wire:model="userSettingsForm.quiet_hours_start" label="Quiet Hours Start" type="time" />
|
|
<flux:input wire:model="userSettingsForm.quiet_hours_end" label="Quiet Hours End" type="time" />
|
|
</div>
|
|
</div>
|
|
|
|
<div class="flex justify-end space-x-2">
|
|
<flux:button type="button" variant="ghost" wire:click="closeUserSettingsModal">Cancel</flux:button>
|
|
<flux:button type="submit" variant="primary">Update Settings</flux:button>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
@endif
|
|
</flux:modal>
|
|
</div>
|