php-uptelligence/View/Blade/admin/digest-preferences.blade.php
Snider e0d2325a20 refactor: move namespace from Core\Uptelligence to Core\Mod\Uptelligence
Aligns module namespace with Core PHP Framework conventions where
modules live under the Core\Mod\ namespace hierarchy. This follows
the monorepo separation work started in 40d893a.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-27 16:32:55 +00:00

250 lines
13 KiB
PHP

<admin:module title="Digest Preferences" subtitle="Configure email notifications for vendor updates">
<x-slot:actions>
<div class="flex items-center gap-2">
<core:button href="{{ route('hub.admin.uptelligence') }}" wire:navigate variant="ghost" icon="arrow-left" size="sm">
Back to Dashboard
</core:button>
</div>
</x-slot:actions>
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
{{-- Settings Panel --}}
<div class="lg:col-span-2 space-y-6">
{{-- Enable/Disable Card --}}
<flux:card class="p-6">
<div class="flex items-center justify-between">
<div>
<flux:heading size="lg">Email Digests</flux:heading>
<flux:subheading>Receive periodic summaries of vendor updates and pending tasks</flux:subheading>
</div>
<flux:switch
wire:click="toggleEnabled"
:checked="$isEnabled"
label="{{ $isEnabled ? 'Enabled' : 'Disabled' }}"
/>
</div>
</flux:card>
{{-- Frequency Selection --}}
<flux:card class="p-6">
<flux:heading size="lg" class="mb-4">Frequency</flux:heading>
<flux:radio.group wire:model.live="frequency" class="space-y-3">
@foreach(\Core\Mod\Uptelligence\Models\UptelligenceDigest::getFrequencyOptions() as $value => $label)
<flux:radio
value="{{ $value }}"
label="{{ $label }}"
description="{{ match($value) {
'daily' => 'Sent every morning at 9am UK time',
'weekly' => 'Sent every Monday at 9am UK time',
'monthly' => 'Sent on the 1st of each month at 9am UK time',
default => ''
} }}"
/>
@endforeach
</flux:radio.group>
</flux:card>
{{-- Content Types --}}
<flux:card class="p-6">
<flux:heading size="lg" class="mb-4">Include in Digest</flux:heading>
<div class="space-y-4">
<flux:checkbox
wire:click="toggleType('releases')"
:checked="in_array('releases', $selectedTypes)"
label="New Releases"
description="Version updates and changelog summaries from tracked vendors"
/>
<flux:checkbox
wire:click="toggleType('todos')"
:checked="in_array('todos', $selectedTypes)"
label="Pending Tasks"
description="Summary of porting tasks grouped by priority"
/>
<flux:checkbox
wire:click="toggleType('security')"
:checked="in_array('security', $selectedTypes)"
label="Security Updates"
description="Highlight security-related updates that need attention"
/>
</div>
</flux:card>
{{-- Vendor Filter --}}
<flux:card class="p-6">
<div class="flex items-center justify-between mb-4">
<div>
<flux:heading size="lg">Vendor Filter</flux:heading>
<flux:subheading>Select which vendors to include (leave empty for all)</flux:subheading>
</div>
@if(!empty($selectedVendorIds))
<flux:button wire:click="selectAllVendors" variant="ghost" size="sm">
Clear Filter
</flux:button>
@endif
</div>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-3 max-h-64 overflow-y-auto">
@foreach($this->vendors as $vendor)
<flux:checkbox
wire:click="toggleVendor({{ $vendor->id }})"
:checked="empty($selectedVendorIds) || in_array($vendor->id, $selectedVendorIds)"
label="{{ $vendor->name }}"
>
<x-slot:description>
@if($vendor->source_type === 'licensed')
<flux:icon name="lock-closed" class="size-3 inline text-amber-500" />
@elseif($vendor->source_type === 'oss')
<flux:icon name="globe-alt" class="size-3 inline text-green-500" />
@else
<flux:icon name="puzzle-piece" class="size-3 inline text-blue-500" />
@endif
{{ $vendor->slug }}
</x-slot:description>
</flux:checkbox>
@endforeach
</div>
@if($this->vendors->isEmpty())
<div class="text-center py-8 text-zinc-500">
<flux:icon name="building-office" class="size-8 opacity-50 mx-auto mb-2" />
<span>No vendors tracked yet</span>
</div>
@endif
</flux:card>
{{-- Priority Threshold --}}
<flux:card class="p-6">
<flux:heading size="lg" class="mb-4">Priority Threshold</flux:heading>
<flux:subheading class="mb-4">Only include tasks at or above this priority level</flux:subheading>
<flux:select wire:model.live="minPriority">
<flux:option :value="null">All priorities</flux:option>
<flux:option value="4">Medium and above (4+)</flux:option>
<flux:option value="6">High and above (6+)</flux:option>
<flux:option value="8">Critical only (8+)</flux:option>
</flux:select>
</flux:card>
{{-- Actions --}}
<div class="flex items-center justify-between">
<flux:button wire:click="sendTestDigest" variant="ghost" icon="paper-airplane">
Send Test Digest
</flux:button>
<div class="flex items-center gap-3">
<flux:button wire:click="showPreview" variant="ghost" icon="eye">
Preview
</flux:button>
<flux:button wire:click="save" variant="primary" icon="check">
Save Preferences
</flux:button>
</div>
</div>
</div>
{{-- Preview Panel --}}
<div class="lg:col-span-1">
<flux:card class="p-6 sticky top-6">
<flux:heading size="lg" class="mb-4">Preview</flux:heading>
<flux:subheading class="mb-6">What your next digest would include</flux:subheading>
@php $preview = $this->preview; @endphp
@if(!$preview['has_content'])
<div class="text-center py-8 text-zinc-500">
<flux:icon name="inbox" class="size-8 opacity-50 mx-auto mb-2" />
<span>No content to preview</span>
<p class="text-sm mt-1">There are no updates matching your filters</p>
</div>
@else
<div class="space-y-4">
{{-- Security Alert --}}
@if($preview['security_count'] > 0)
<div class="p-3 bg-red-50 dark:bg-red-900/20 rounded-lg border border-red-200 dark:border-red-800">
<div class="flex items-center gap-2 text-red-700 dark:text-red-400">
<flux:icon name="shield-exclamation" class="size-5" />
<span class="font-medium">{{ $preview['security_count'] }} security update{{ $preview['security_count'] !== 1 ? 's' : '' }}</span>
</div>
</div>
@endif
{{-- Releases --}}
@if($preview['releases']->isNotEmpty())
<div>
<h4 class="text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-2">Recent Releases</h4>
<ul class="space-y-1 text-sm text-zinc-600 dark:text-zinc-400">
@foreach($preview['releases'] as $release)
<li class="flex items-center justify-between">
<span>{{ $release['vendor_name'] }}</span>
<span class="font-mono text-xs">{{ $release['version'] }}</span>
</li>
@endforeach
</ul>
</div>
@endif
{{-- Todos Summary --}}
@if(($preview['todos']['total'] ?? 0) > 0)
<div>
<h4 class="text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-2">Pending Tasks</h4>
<div class="grid grid-cols-2 gap-2 text-sm">
@if($preview['todos']['critical'] > 0)
<div class="flex items-center justify-between p-2 bg-red-50 dark:bg-red-900/20 rounded">
<span class="text-red-700 dark:text-red-400">Critical</span>
<span class="font-medium">{{ $preview['todos']['critical'] }}</span>
</div>
@endif
@if($preview['todos']['high'] > 0)
<div class="flex items-center justify-between p-2 bg-orange-50 dark:bg-orange-900/20 rounded">
<span class="text-orange-700 dark:text-orange-400">High</span>
<span class="font-medium">{{ $preview['todos']['high'] }}</span>
</div>
@endif
@if($preview['todos']['medium'] > 0)
<div class="flex items-center justify-between p-2 bg-yellow-50 dark:bg-yellow-900/20 rounded">
<span class="text-yellow-700 dark:text-yellow-400">Medium</span>
<span class="font-medium">{{ $preview['todos']['medium'] }}</span>
</div>
@endif
@if($preview['todos']['low'] > 0)
<div class="flex items-center justify-between p-2 bg-zinc-50 dark:bg-zinc-800 rounded">
<span class="text-zinc-600 dark:text-zinc-400">Low</span>
<span class="font-medium">{{ $preview['todos']['low'] }}</span>
</div>
@endif
</div>
</div>
@endif
{{-- Top Vendors --}}
@if($preview['top_vendors']->isNotEmpty())
<div>
<h4 class="text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-2">Top Vendors</h4>
<ul class="space-y-1 text-sm text-zinc-600 dark:text-zinc-400">
@foreach($preview['top_vendors'] as $vendor)
<li class="flex items-center justify-between">
<span>{{ $vendor->name }}</span>
<flux:badge size="sm" color="blue">{{ $vendor->pending_count }} pending</flux:badge>
</li>
@endforeach
</ul>
</div>
@endif
{{-- Next Send --}}
@if($preview['next_send'])
<div class="pt-4 border-t border-zinc-200 dark:border-zinc-700 text-sm text-zinc-500">
<p><strong>Frequency:</strong> {{ $preview['frequency_label'] }}</p>
<p><strong>Next send:</strong> {{ $preview['next_send'] }}</p>
</div>
@endif
</div>
@endif
</flux:card>
</div>
</div>
</admin:module>