Add join token analytics dashboard and align Filament views
This commit is contained in:
@@ -1,54 +1,90 @@
|
||||
@php
|
||||
use Filament\Support\Enums\GridDirection;
|
||||
use Filament\Support\Icons\Heroicon;
|
||||
use Illuminate\View\ComponentAttributeBag;
|
||||
|
||||
$statusColors = [
|
||||
'done' => 'success',
|
||||
'deploying' => 'warning',
|
||||
'pending' => 'warning',
|
||||
'unreachable' => 'danger',
|
||||
'error' => 'danger',
|
||||
'failed' => 'danger',
|
||||
];
|
||||
|
||||
$statusIcons = [
|
||||
'done' => Heroicon::CheckCircle,
|
||||
'deploying' => Heroicon::ArrowPath,
|
||||
'pending' => Heroicon::Clock,
|
||||
'unreachable' => Heroicon::ExclamationTriangle,
|
||||
'error' => Heroicon::XCircle,
|
||||
'failed' => Heroicon::XCircle,
|
||||
];
|
||||
|
||||
$serviceColors = [
|
||||
'running' => 'success',
|
||||
'done' => 'success',
|
||||
'starting' => 'warning',
|
||||
'deploying' => 'warning',
|
||||
'unhealthy' => 'danger',
|
||||
'error' => 'danger',
|
||||
'failed' => 'danger',
|
||||
];
|
||||
|
||||
$cardsGrid = (new ComponentAttributeBag())->grid(['default' => 1, 'lg' => 2]);
|
||||
$serviceGrid = (new ComponentAttributeBag())->grid(['default' => 1], GridDirection::Column);
|
||||
@endphp
|
||||
|
||||
<x-filament-widgets::widget>
|
||||
<x-filament::section heading="Infra Status (Dokploy)">
|
||||
<div class="grid gap-4 md:grid-cols-2">
|
||||
<div {{ $cardsGrid }}>
|
||||
@forelse($composes as $compose)
|
||||
<div class="rounded-2xl border border-slate-200/70 bg-white/80 p-4 shadow-sm dark:border-white/10 dark:bg-slate-900/60">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<p class="text-sm font-semibold text-slate-600 dark:text-slate-200">{{ $compose['label'] }}</p>
|
||||
<p class="text-xs text-slate-500 dark:text-slate-400">{{ $compose['name'] ?? '–' }}</p>
|
||||
<p class="text-[11px] text-slate-400 dark:text-slate-500">{{ $compose['compose_id'] }}</p>
|
||||
</div>
|
||||
<span @class([
|
||||
'rounded-full px-3 py-1 text-xs font-semibold',
|
||||
'bg-emerald-100 text-emerald-800' => $compose['status'] === 'done',
|
||||
'bg-amber-100 text-amber-800' => in_array($compose['status'], ['deploying', 'pending']),
|
||||
'bg-rose-100 text-rose-800' => in_array($compose['status'], ['unreachable', 'error', 'failed']),
|
||||
'bg-slate-100 text-slate-600' => ! in_array($compose['status'], ['done', 'deploying', 'pending', 'unreachable', 'error', 'failed']),
|
||||
])>
|
||||
<x-filament::card
|
||||
:heading="$compose['label']"
|
||||
:description="$compose['name'] ?? '–'"
|
||||
>
|
||||
<x-slot name="afterHeader">
|
||||
<x-filament::badge
|
||||
:color="$statusColors[$compose['status']] ?? 'gray'"
|
||||
:icon="$statusIcons[$compose['status']] ?? Heroicon::QuestionMarkCircle"
|
||||
>
|
||||
{{ ucfirst($compose['status']) }}
|
||||
</span>
|
||||
</div>
|
||||
</x-filament::badge>
|
||||
<x-filament::badge color="gray" :icon="Heroicon::Identification">
|
||||
{{ $compose['compose_id'] }}
|
||||
</x-filament::badge>
|
||||
</x-slot>
|
||||
|
||||
@if(isset($compose['error']))
|
||||
<p class="mt-3 text-xs text-rose-600 dark:text-rose-400">{{ $compose['error'] }}</p>
|
||||
<x-filament::badge color="danger" :icon="Heroicon::ExclamationTriangle">
|
||||
{{ $compose['error'] }}
|
||||
</x-filament::badge>
|
||||
@else
|
||||
<div class="mt-3 space-y-1">
|
||||
<p class="text-xs font-semibold text-slate-500 dark:text-slate-400">Services</p>
|
||||
<div {{ $serviceGrid }}>
|
||||
@forelse($compose['services'] as $service)
|
||||
<div class="flex items-center justify-between rounded-lg bg-slate-50 px-3 py-1 text-[11px] font-medium text-slate-700 dark:bg-slate-800 dark:text-slate-200">
|
||||
<span>{{ $service['name'] }}</span>
|
||||
<span @class([
|
||||
'rounded-full px-2 py-0.5 text-[10px] uppercase tracking-wide',
|
||||
'bg-emerald-200/70 text-emerald-900' => in_array($service['status'], ['running', 'done']),
|
||||
'bg-amber-200/70 text-amber-900' => in_array($service['status'], ['starting', 'deploying']),
|
||||
'bg-rose-200/70 text-rose-900' => in_array($service['status'], ['error', 'failed', 'unhealthy']),
|
||||
'bg-slate-200/70 text-slate-900' => ! in_array($service['status'], ['running', 'done', 'starting', 'deploying', 'error', 'failed', 'unhealthy']),
|
||||
])>
|
||||
{{ strtoupper($service['status'] ?? 'N/A') }}
|
||||
</span>
|
||||
</div>
|
||||
<x-filament::badge
|
||||
:color="$serviceColors[$service['status']] ?? 'gray'"
|
||||
:icon="array_key_exists($service['status'] ?? '', $serviceColors) ? Heroicon::Server : Heroicon::QuestionMarkCircle"
|
||||
>
|
||||
{{ $service['name'] }}: {{ strtoupper($service['status'] ?? 'N/A') }}
|
||||
</x-filament::badge>
|
||||
@empty
|
||||
<p class="text-xs text-slate-500 dark:text-slate-400">No services reported.</p>
|
||||
<x-filament::badge color="gray" :icon="Heroicon::QuestionMarkCircle">
|
||||
No services reported.
|
||||
</x-filament::badge>
|
||||
@endforelse
|
||||
<x-filament::badge color="gray" :icon="Heroicon::Clock">
|
||||
Last deploy:
|
||||
{{ $compose['last_deploy'] ? \Illuminate\Support\Carbon::parse($compose['last_deploy'])->diffForHumans() : '—' }}
|
||||
</x-filament::badge>
|
||||
</div>
|
||||
<p class="mt-3 text-xs text-slate-500 dark:text-slate-400">
|
||||
Last deploy: {{ $compose['last_deploy'] ? \Illuminate\Support\Carbon::parse($compose['last_deploy'])->diffForHumans() : '—' }}
|
||||
</p>
|
||||
@endif
|
||||
</div>
|
||||
</x-filament::card>
|
||||
@empty
|
||||
<p class="text-sm text-slate-500 dark:text-slate-300">No Dokploy compose stacks configured.</p>
|
||||
<x-filament::empty-state
|
||||
heading="No Dokploy compose stacks configured."
|
||||
:icon="Heroicon::QuestionMarkCircle"
|
||||
/>
|
||||
@endforelse
|
||||
</div>
|
||||
</x-filament::section>
|
||||
|
||||
@@ -1,76 +1,90 @@
|
||||
@php
|
||||
use Filament\Support\Enums\GridDirection;
|
||||
use Filament\Support\Icons\Heroicon;
|
||||
use Illuminate\View\ComponentAttributeBag;
|
||||
|
||||
$statusColors = [
|
||||
'healthy' => 'success',
|
||||
'pending' => 'warning',
|
||||
'degraded' => 'danger',
|
||||
'unconfigured' => 'danger',
|
||||
'unknown' => 'gray',
|
||||
];
|
||||
|
||||
$statusIcons = [
|
||||
'healthy' => Heroicon::CheckCircle,
|
||||
'pending' => Heroicon::Clock,
|
||||
'degraded' => Heroicon::ExclamationTriangle,
|
||||
'unconfigured' => Heroicon::XCircle,
|
||||
'unknown' => Heroicon::QuestionMarkCircle,
|
||||
];
|
||||
|
||||
$cardsGrid = (new ComponentAttributeBag())->grid(['default' => 1, 'lg' => 2]);
|
||||
$metricsGrid = (new ComponentAttributeBag())->grid(['default' => 1], GridDirection::Column);
|
||||
@endphp
|
||||
|
||||
<x-filament-widgets::widget>
|
||||
<x-filament::section heading="{{ __('admin.integrations_health.heading') }}">
|
||||
<div class="grid gap-4 md:grid-cols-2">
|
||||
<div {{ $cardsGrid }}>
|
||||
@forelse($providers as $provider)
|
||||
<div class="rounded-2xl border border-slate-200/70 bg-white/80 p-4 shadow-sm dark:border-white/10 dark:bg-slate-900/60">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<p class="text-sm font-semibold text-slate-700 dark:text-slate-100">{{ $provider['label'] }}</p>
|
||||
<p class="text-xs text-slate-500 dark:text-slate-400">
|
||||
{{ $provider['config_label'] }}:
|
||||
<span class="font-semibold {{ $provider['is_configured'] ? 'text-emerald-600 dark:text-emerald-400' : 'text-rose-600 dark:text-rose-400' }}">
|
||||
{{ $provider['is_configured'] ? __('admin.integrations_health.configured') : __('admin.integrations_health.unconfigured') }}
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<span @class([
|
||||
'rounded-full px-3 py-1 text-xs font-semibold',
|
||||
'bg-emerald-100 text-emerald-800' => $provider['status'] === 'healthy',
|
||||
'bg-amber-100 text-amber-800' => $provider['status'] === 'pending',
|
||||
'bg-rose-100 text-rose-800' => in_array($provider['status'], ['degraded', 'unconfigured']),
|
||||
'bg-slate-100 text-slate-600' => $provider['status'] === 'unknown',
|
||||
])>
|
||||
<x-filament::card
|
||||
:heading="$provider['label']"
|
||||
:description="$provider['config_label']"
|
||||
>
|
||||
<x-slot name="afterHeader">
|
||||
<x-filament::badge :color="$provider['is_configured'] ? 'success' : 'danger'">
|
||||
{{ $provider['is_configured'] ? __('admin.integrations_health.configured') : __('admin.integrations_health.unconfigured') }}
|
||||
</x-filament::badge>
|
||||
<x-filament::badge
|
||||
:color="$statusColors[$provider['status']] ?? 'gray'"
|
||||
:icon="$statusIcons[$provider['status']] ?? Heroicon::QuestionMarkCircle"
|
||||
>
|
||||
{{ $provider['status_label'] }}
|
||||
</span>
|
||||
</div>
|
||||
</x-filament::badge>
|
||||
</x-slot>
|
||||
|
||||
<div class="mt-4 grid gap-2 text-xs text-slate-600 dark:text-slate-300">
|
||||
<div class="flex items-center justify-between">
|
||||
<span>{{ __('admin.integrations_health.last_received') }}</span>
|
||||
<span class="font-semibold">
|
||||
{{ optional(data_get($provider, 'last_event.received_at'))->diffForHumans() ?? '—' }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex items-center justify-between">
|
||||
<span>{{ __('admin.integrations_health.last_processed') }}</span>
|
||||
<span class="font-semibold">
|
||||
{{ optional(data_get($provider, 'last_processed.processed_at'))->diffForHumans() ?? '—' }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex items-center justify-between">
|
||||
<span>{{ __('admin.integrations_health.processing_lag') }}</span>
|
||||
<span class="font-semibold">
|
||||
{{ $provider['processing_lag']['label'] ?? '—' }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex items-center justify-between">
|
||||
<span>{{ __('admin.integrations_health.pending_events') }}</span>
|
||||
<span class="font-semibold">{{ number_format($provider['pending_count']) }}</span>
|
||||
</div>
|
||||
<div class="flex items-center justify-between">
|
||||
<span>{{ __('admin.integrations_health.recent_failures') }}</span>
|
||||
<span class="font-semibold">{{ number_format($provider['recent_failures']) }}</span>
|
||||
</div>
|
||||
<div class="flex items-center justify-between">
|
||||
<span>{{ __('admin.integrations_health.queue_backlog') }}</span>
|
||||
<span class="font-semibold">{{ number_format($provider['queue_backlog']) }}</span>
|
||||
</div>
|
||||
<div class="flex items-center justify-between">
|
||||
<span>{{ __('admin.integrations_health.failed_jobs') }}</span>
|
||||
<span class="font-semibold">{{ number_format($provider['failed_jobs']) }}</span>
|
||||
</div>
|
||||
<div {{ $metricsGrid }}>
|
||||
<x-filament::badge color="gray" :icon="Heroicon::Clock">
|
||||
{{ __('admin.integrations_health.last_received') }}:
|
||||
{{ optional(data_get($provider, 'last_event.received_at'))->diffForHumans() ?? '—' }}
|
||||
</x-filament::badge>
|
||||
<x-filament::badge color="gray" :icon="Heroicon::ArrowPath">
|
||||
{{ __('admin.integrations_health.last_processed') }}:
|
||||
{{ optional(data_get($provider, 'last_processed.processed_at'))->diffForHumans() ?? '—' }}
|
||||
</x-filament::badge>
|
||||
<x-filament::badge color="gray" :icon="Heroicon::Clock">
|
||||
{{ __('admin.integrations_health.processing_lag') }}:
|
||||
{{ $provider['processing_lag']['label'] ?? '—' }}
|
||||
</x-filament::badge>
|
||||
<x-filament::badge color="gray" :icon="Heroicon::QueueList">
|
||||
{{ __('admin.integrations_health.pending_events') }}:
|
||||
{{ number_format($provider['pending_count']) }}
|
||||
</x-filament::badge>
|
||||
<x-filament::badge color="gray" :icon="Heroicon::ExclamationTriangle">
|
||||
{{ __('admin.integrations_health.recent_failures') }}:
|
||||
{{ number_format($provider['recent_failures']) }}
|
||||
</x-filament::badge>
|
||||
<x-filament::badge color="gray" :icon="Heroicon::RectangleStack">
|
||||
{{ __('admin.integrations_health.queue_backlog') }}:
|
||||
{{ number_format($provider['queue_backlog']) }}
|
||||
</x-filament::badge>
|
||||
<x-filament::badge color="gray" :icon="Heroicon::XCircle">
|
||||
{{ __('admin.integrations_health.failed_jobs') }}:
|
||||
{{ number_format($provider['failed_jobs']) }}
|
||||
</x-filament::badge>
|
||||
@if(! empty(data_get($provider, 'last_failed.error_message')))
|
||||
<x-filament::badge color="danger" :icon="Heroicon::ExclamationTriangle">
|
||||
{{ __('admin.integrations_health.last_error') }}:
|
||||
{{ data_get($provider, 'last_failed.error_message') }}
|
||||
</x-filament::badge>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@if(! empty(data_get($provider, 'last_failed.error_message')))
|
||||
<div class="mt-3 rounded-lg bg-rose-50 px-3 py-2 text-xs text-rose-700 dark:bg-rose-900/30 dark:text-rose-200">
|
||||
{{ __('admin.integrations_health.last_error') }}: {{ data_get($provider, 'last_failed.error_message') }}
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</x-filament::card>
|
||||
@empty
|
||||
<p class="text-sm text-slate-500 dark:text-slate-300">
|
||||
{{ __('admin.integrations_health.empty') }}
|
||||
</p>
|
||||
<x-filament::empty-state
|
||||
:heading="__('admin.integrations_health.empty')"
|
||||
:icon="Heroicon::QuestionMarkCircle"
|
||||
/>
|
||||
@endforelse
|
||||
</div>
|
||||
</x-filament::section>
|
||||
|
||||
Reference in New Issue
Block a user