260 lines
13 KiB
PHP
260 lines
13 KiB
PHP
<?php
|
|
|
|
use App\Enums\ConsultationStatus;
|
|
use App\Enums\ConsultationType;
|
|
use App\Enums\PostStatus;
|
|
use App\Enums\TimelineStatus;
|
|
use App\Enums\UserStatus;
|
|
use App\Enums\UserType;
|
|
use App\Models\Consultation;
|
|
use App\Models\Post;
|
|
use App\Models\Timeline;
|
|
use App\Models\TimelineUpdate;
|
|
use App\Models\User;
|
|
use Illuminate\Support\Facades\Cache;
|
|
use Livewire\Volt\Component;
|
|
|
|
new class extends Component
|
|
{
|
|
public function getTitle(): string
|
|
{
|
|
return __('admin_metrics.title');
|
|
}
|
|
|
|
public function with(): array
|
|
{
|
|
return [
|
|
'userMetrics' => $this->getUserMetrics(),
|
|
'bookingMetrics' => $this->getBookingMetrics(),
|
|
'timelineMetrics' => $this->getTimelineMetrics(),
|
|
'postMetrics' => $this->getPostMetrics(),
|
|
];
|
|
}
|
|
|
|
private function getUserMetrics(): array
|
|
{
|
|
return Cache::remember('admin.metrics.users', 300, fn () => [
|
|
'total_active' => User::query()
|
|
->where('status', UserStatus::Active)
|
|
->whereIn('user_type', [UserType::Individual, UserType::Company])
|
|
->count(),
|
|
'individual' => User::query()
|
|
->where('user_type', UserType::Individual)
|
|
->where('status', UserStatus::Active)
|
|
->count(),
|
|
'company' => User::query()
|
|
->where('user_type', UserType::Company)
|
|
->where('status', UserStatus::Active)
|
|
->count(),
|
|
'deactivated' => User::query()
|
|
->where('status', UserStatus::Deactivated)
|
|
->whereIn('user_type', [UserType::Individual, UserType::Company])
|
|
->count(),
|
|
'new_this_month' => User::query()
|
|
->whereIn('user_type', [UserType::Individual, UserType::Company])
|
|
->whereMonth('created_at', now()->month)
|
|
->whereYear('created_at', now()->year)
|
|
->count(),
|
|
]);
|
|
}
|
|
|
|
private function getBookingMetrics(): array
|
|
{
|
|
return Cache::remember('admin.metrics.bookings', 300, function () {
|
|
$total = Consultation::query()
|
|
->whereIn('status', [ConsultationStatus::Completed, ConsultationStatus::NoShow])
|
|
->count();
|
|
$noShows = Consultation::query()
|
|
->where('status', ConsultationStatus::NoShow)
|
|
->count();
|
|
|
|
return [
|
|
'pending' => Consultation::query()
|
|
->where('status', ConsultationStatus::Pending)
|
|
->count(),
|
|
'today' => Consultation::query()
|
|
->whereDate('booking_date', today())
|
|
->where('status', ConsultationStatus::Approved)
|
|
->count(),
|
|
'this_week' => Consultation::query()
|
|
->whereBetween('booking_date', [now()->startOfWeek(), now()->endOfWeek()])
|
|
->whereIn('status', [ConsultationStatus::Approved, ConsultationStatus::Pending])
|
|
->count(),
|
|
'this_month' => Consultation::query()
|
|
->whereMonth('booking_date', now()->month)
|
|
->whereYear('booking_date', now()->year)
|
|
->count(),
|
|
'free' => Consultation::query()
|
|
->where('consultation_type', ConsultationType::Free)
|
|
->count(),
|
|
'paid' => Consultation::query()
|
|
->where('consultation_type', ConsultationType::Paid)
|
|
->count(),
|
|
'no_show_rate' => $total > 0 ? round(($noShows / $total) * 100, 1) : 0,
|
|
];
|
|
});
|
|
}
|
|
|
|
private function getTimelineMetrics(): array
|
|
{
|
|
return Cache::remember('admin.metrics.timelines', 300, fn () => [
|
|
'active' => Timeline::query()
|
|
->where('status', TimelineStatus::Active)
|
|
->count(),
|
|
'archived' => Timeline::query()
|
|
->where('status', TimelineStatus::Archived)
|
|
->count(),
|
|
'updates_this_week' => TimelineUpdate::query()
|
|
->where('created_at', '>=', now()->subWeek())
|
|
->count(),
|
|
]);
|
|
}
|
|
|
|
private function getPostMetrics(): array
|
|
{
|
|
return Cache::remember('admin.metrics.posts', 300, fn () => [
|
|
'total_published' => Post::query()
|
|
->where('status', PostStatus::Published)
|
|
->count(),
|
|
'this_month' => Post::query()
|
|
->where('status', PostStatus::Published)
|
|
->whereMonth('published_at', now()->month)
|
|
->whereYear('published_at', now()->year)
|
|
->count(),
|
|
]);
|
|
}
|
|
}; ?>
|
|
|
|
<div>
|
|
<div class="mb-6">
|
|
<flux:heading size="xl">{{ __('admin_metrics.title') }}</flux:heading>
|
|
<flux:text class="mt-1 text-zinc-500 dark:text-zinc-400">{{ __('admin_metrics.subtitle') }}</flux:text>
|
|
</div>
|
|
|
|
<div class="grid grid-cols-1 gap-6 md:grid-cols-2 xl:grid-cols-4">
|
|
{{-- User Metrics Card --}}
|
|
<div class="rounded-lg border border-zinc-200 bg-white p-6 dark:border-zinc-700 dark:bg-zinc-800">
|
|
<div class="mb-4 flex items-center gap-3">
|
|
<div class="flex h-10 w-10 items-center justify-center rounded-lg bg-[#0A1F44] text-white dark:bg-[#D4AF37] dark:text-zinc-900">
|
|
<flux:icon name="users" class="h-5 w-5" />
|
|
</div>
|
|
<flux:heading size="lg">{{ __('admin_metrics.clients') }}</flux:heading>
|
|
</div>
|
|
<div class="space-y-3">
|
|
<div class="flex items-center justify-between">
|
|
<flux:text class="text-zinc-600 dark:text-zinc-400">{{ __('admin_metrics.total_active') }}</flux:text>
|
|
<span class="text-xl font-semibold text-zinc-900 dark:text-zinc-100">{{ $userMetrics['total_active'] }}</span>
|
|
</div>
|
|
<div class="flex items-center justify-between">
|
|
<flux:text class="text-zinc-600 dark:text-zinc-400">{{ __('admin_metrics.individual') }}</flux:text>
|
|
<span class="font-medium text-zinc-700 dark:text-zinc-300">{{ $userMetrics['individual'] }}</span>
|
|
</div>
|
|
<div class="flex items-center justify-between">
|
|
<flux:text class="text-zinc-600 dark:text-zinc-400">{{ __('admin_metrics.company') }}</flux:text>
|
|
<span class="font-medium text-zinc-700 dark:text-zinc-300">{{ $userMetrics['company'] }}</span>
|
|
</div>
|
|
<div class="border-t border-zinc-200 pt-3 dark:border-zinc-700">
|
|
<div class="flex items-center justify-between">
|
|
<flux:text class="text-zinc-600 dark:text-zinc-400">{{ __('admin_metrics.deactivated') }}</flux:text>
|
|
<span class="font-medium text-zinc-500 dark:text-zinc-400">{{ $userMetrics['deactivated'] }}</span>
|
|
</div>
|
|
</div>
|
|
<div class="flex items-center justify-between">
|
|
<flux:text class="text-zinc-600 dark:text-zinc-400">{{ __('admin_metrics.new_this_month') }}</flux:text>
|
|
<flux:badge color="lime" size="sm">{{ $userMetrics['new_this_month'] }}</flux:badge>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{{-- Booking Metrics Card --}}
|
|
<div class="rounded-lg border border-zinc-200 bg-white p-6 dark:border-zinc-700 dark:bg-zinc-800">
|
|
<div class="mb-4 flex items-center gap-3">
|
|
<div class="flex h-10 w-10 items-center justify-center rounded-lg bg-[#0A1F44] text-white dark:bg-[#D4AF37] dark:text-zinc-900">
|
|
<flux:icon name="calendar" class="h-5 w-5" />
|
|
</div>
|
|
<flux:heading size="lg">{{ __('admin_metrics.consultations') }}</flux:heading>
|
|
</div>
|
|
<div class="space-y-3">
|
|
<div class="flex items-center justify-between">
|
|
<flux:text class="text-zinc-600 dark:text-zinc-400">{{ __('admin_metrics.pending_requests') }}</flux:text>
|
|
<flux:badge color="amber" size="sm">{{ $bookingMetrics['pending'] }}</flux:badge>
|
|
</div>
|
|
<div class="flex items-center justify-between">
|
|
<flux:text class="text-zinc-600 dark:text-zinc-400">{{ __('admin_metrics.today') }}</flux:text>
|
|
<span class="font-medium text-zinc-700 dark:text-zinc-300">{{ $bookingMetrics['today'] }}</span>
|
|
</div>
|
|
<div class="flex items-center justify-between">
|
|
<flux:text class="text-zinc-600 dark:text-zinc-400">{{ __('admin_metrics.this_week') }}</flux:text>
|
|
<span class="font-medium text-zinc-700 dark:text-zinc-300">{{ $bookingMetrics['this_week'] }}</span>
|
|
</div>
|
|
<div class="flex items-center justify-between">
|
|
<flux:text class="text-zinc-600 dark:text-zinc-400">{{ __('admin_metrics.this_month') }}</flux:text>
|
|
<span class="font-medium text-zinc-700 dark:text-zinc-300">{{ $bookingMetrics['this_month'] }}</span>
|
|
</div>
|
|
<div class="border-t border-zinc-200 pt-3 dark:border-zinc-700">
|
|
<div class="flex items-center justify-between">
|
|
<flux:text class="text-zinc-600 dark:text-zinc-400">{{ __('admin_metrics.free') }}</flux:text>
|
|
<span class="font-medium text-zinc-700 dark:text-zinc-300">{{ $bookingMetrics['free'] }}</span>
|
|
</div>
|
|
<div class="mt-2 flex items-center justify-between">
|
|
<flux:text class="text-zinc-600 dark:text-zinc-400">{{ __('admin_metrics.paid') }}</flux:text>
|
|
<span class="font-medium text-zinc-700 dark:text-zinc-300">{{ $bookingMetrics['paid'] }}</span>
|
|
</div>
|
|
</div>
|
|
<div class="flex items-center justify-between">
|
|
<flux:text class="text-zinc-600 dark:text-zinc-400">{{ __('admin_metrics.no_show_rate') }}</flux:text>
|
|
<span class="font-medium text-zinc-700 dark:text-zinc-300">{{ $bookingMetrics['no_show_rate'] }}%</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{{-- Timeline Metrics Card --}}
|
|
<div class="rounded-lg border border-zinc-200 bg-white p-6 dark:border-zinc-700 dark:bg-zinc-800">
|
|
<div class="mb-4 flex items-center gap-3">
|
|
<div class="flex h-10 w-10 items-center justify-center rounded-lg bg-[#0A1F44] text-white dark:bg-[#D4AF37] dark:text-zinc-900">
|
|
<flux:icon name="clipboard-document-list" class="h-5 w-5" />
|
|
</div>
|
|
<flux:heading size="lg">{{ __('admin_metrics.timelines') }}</flux:heading>
|
|
</div>
|
|
<div class="space-y-3">
|
|
<div class="flex items-center justify-between">
|
|
<flux:text class="text-zinc-600 dark:text-zinc-400">{{ __('admin_metrics.active_cases') }}</flux:text>
|
|
<span class="text-xl font-semibold text-zinc-900 dark:text-zinc-100">{{ $timelineMetrics['active'] }}</span>
|
|
</div>
|
|
<div class="flex items-center justify-between">
|
|
<flux:text class="text-zinc-600 dark:text-zinc-400">{{ __('admin_metrics.archived') }}</flux:text>
|
|
<span class="font-medium text-zinc-500 dark:text-zinc-400">{{ $timelineMetrics['archived'] }}</span>
|
|
</div>
|
|
<div class="border-t border-zinc-200 pt-3 dark:border-zinc-700">
|
|
<div class="flex items-center justify-between">
|
|
<flux:text class="text-zinc-600 dark:text-zinc-400">{{ __('admin_metrics.updates_this_week') }}</flux:text>
|
|
<flux:badge color="lime" size="sm">{{ $timelineMetrics['updates_this_week'] }}</flux:badge>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{{-- Posts Metrics Card --}}
|
|
<div class="rounded-lg border border-zinc-200 bg-white p-6 dark:border-zinc-700 dark:bg-zinc-800">
|
|
<div class="mb-4 flex items-center gap-3">
|
|
<div class="flex h-10 w-10 items-center justify-center rounded-lg bg-[#0A1F44] text-white dark:bg-[#D4AF37] dark:text-zinc-900">
|
|
<flux:icon name="document-text" class="h-5 w-5" />
|
|
</div>
|
|
<flux:heading size="lg">{{ __('admin_metrics.posts') }}</flux:heading>
|
|
</div>
|
|
<div class="space-y-3">
|
|
<div class="flex items-center justify-between">
|
|
<flux:text class="text-zinc-600 dark:text-zinc-400">{{ __('admin_metrics.total_published') }}</flux:text>
|
|
<span class="text-xl font-semibold text-zinc-900 dark:text-zinc-100">{{ $postMetrics['total_published'] }}</span>
|
|
</div>
|
|
<div class="border-t border-zinc-200 pt-3 dark:border-zinc-700">
|
|
<div class="flex items-center justify-between">
|
|
<flux:text class="text-zinc-600 dark:text-zinc-400">{{ __('admin_metrics.published_this_month') }}</flux:text>
|
|
<flux:badge color="lime" size="sm">{{ $postMetrics['this_month'] }}</flux:badge>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|