245 lines
12 KiB
PHP
245 lines
12 KiB
PHP
<?php
|
|
|
|
use App\Models\TimelineUpdate;
|
|
use Livewire\Volt\Component;
|
|
|
|
new class extends Component {
|
|
public function with(): array
|
|
{
|
|
$user = auth()->user();
|
|
|
|
return [
|
|
'upcomingConsultation' => $user->consultations()
|
|
->approved()
|
|
->where('booking_date', '>=', today())
|
|
->orderBy('booking_date')
|
|
->orderBy('booking_time')
|
|
->first(),
|
|
'activeTimelinesCount' => $user->timelines()->active()->count(),
|
|
'latestTimeline' => $user->timelines()->active()->latest()->first(),
|
|
'recentUpdates' => TimelineUpdate::whereHas('timeline', fn ($q) => $q->where('user_id', $user->id))
|
|
->latest()
|
|
->take(3)
|
|
->with('timeline')
|
|
->get(),
|
|
'pendingBookingsCount' => $user->consultations()->pending()->count(),
|
|
'canBookToday' => ! $user->consultations()
|
|
->whereDate('booking_date', today())
|
|
->whereIn('status', ['pending', 'approved'])
|
|
->exists(),
|
|
];
|
|
}
|
|
}; ?>
|
|
|
|
<div class="space-y-6 p-4 sm:p-6">
|
|
{{-- Welcome Section --}}
|
|
<div class="rounded-lg border border-zinc-200 bg-[#0A1F44] p-4 sm:p-6 text-white">
|
|
<flux:heading size="xl" class="text-white text-lg sm:text-xl lg:text-2xl">
|
|
{{ __('client.dashboard.welcome', ['name' => auth()->user()->full_name]) }}
|
|
</flux:heading>
|
|
<flux:text class="mt-1 text-zinc-300 text-sm sm:text-base">
|
|
{{ now()->locale(app()->getLocale())->translatedFormat(app()->getLocale() === 'ar' ? 'l، j F Y' : 'l, F j, Y') }}
|
|
</flux:text>
|
|
</div>
|
|
|
|
{{-- Widgets Grid --}}
|
|
<div class="grid gap-4 sm:gap-6 grid-cols-1 sm:grid-cols-2">
|
|
{{-- Upcoming Consultation Widget --}}
|
|
<div class="rounded-lg border border-zinc-200 bg-white p-4 sm:p-6 shadow-sm ">
|
|
<flux:heading size="lg" class="mb-4 text-base sm:text-lg">
|
|
{{ __('client.dashboard.upcoming_consultation') }}
|
|
</flux:heading>
|
|
|
|
@if ($upcomingConsultation)
|
|
<div class="space-y-3">
|
|
<div class="flex items-center gap-2">
|
|
<flux:icon name="calendar" class="h-5 w-5 text-zinc-500" />
|
|
<flux:text>
|
|
@if (app()->getLocale() === 'ar')
|
|
{{ $upcomingConsultation->booking_date->format('d/m/Y') }}
|
|
@else
|
|
{{ $upcomingConsultation->booking_date->format('m/d/Y') }}
|
|
@endif
|
|
</flux:text>
|
|
</div>
|
|
<div class="flex items-center gap-2">
|
|
<flux:icon name="clock" class="h-5 w-5 text-zinc-500" />
|
|
<flux:text>
|
|
{{ \Carbon\Carbon::parse($upcomingConsultation->booking_time)->format('g:i A') }}
|
|
</flux:text>
|
|
</div>
|
|
<div class="flex flex-wrap gap-2">
|
|
@if ($upcomingConsultation->consultation_type->value === 'free')
|
|
<flux:badge color="green">{{ $upcomingConsultation->consultation_type->label() }}</flux:badge>
|
|
@else
|
|
<flux:badge color="yellow">{{ $upcomingConsultation->consultation_type->label() }}</flux:badge>
|
|
@endif
|
|
<flux:badge color="sky">{{ $upcomingConsultation->status->label() }}</flux:badge>
|
|
</div>
|
|
<div class="pt-2">
|
|
<flux:button
|
|
variant="ghost"
|
|
size="sm"
|
|
:href="route('client.consultations.index')"
|
|
wire:navigate
|
|
>
|
|
{{ __('client.dashboard.view_details') }}
|
|
</flux:button>
|
|
</div>
|
|
</div>
|
|
@else
|
|
<div class="text-center py-4">
|
|
<flux:icon name="calendar-days" class="mx-auto h-12 w-12 text-zinc-300" />
|
|
<flux:text class="mt-2 text-zinc-500">{{ __('client.dashboard.no_upcoming') }}</flux:text>
|
|
<div class="mt-4">
|
|
<flux:button
|
|
variant="primary"
|
|
size="sm"
|
|
:href="route('client.consultations.book')"
|
|
wire:navigate
|
|
>
|
|
{{ __('client.dashboard.book_first') }}
|
|
</flux:button>
|
|
</div>
|
|
</div>
|
|
@endif
|
|
</div>
|
|
|
|
{{-- Active Cases Widget --}}
|
|
<div class="rounded-lg border border-zinc-200 bg-white p-4 sm:p-6 shadow-sm ">
|
|
<flux:heading size="lg" class="mb-4 text-base sm:text-lg">
|
|
{{ __('client.dashboard.active_cases') }}
|
|
</flux:heading>
|
|
|
|
@if ($activeTimelinesCount > 0)
|
|
<div class="space-y-3">
|
|
<div class="flex items-center gap-2">
|
|
<span class="text-3xl font-bold text-[#D4AF37]">{{ $activeTimelinesCount }}</span>
|
|
<flux:text class="text-zinc-500">{{ trans_choice('client.dashboard.cases_count', $activeTimelinesCount) }}</flux:text>
|
|
</div>
|
|
@if ($latestTimeline)
|
|
@php
|
|
$latestUpdate = $latestTimeline->updates()->reorder()->latest()->first();
|
|
@endphp
|
|
@if ($latestUpdate)
|
|
<div class="rounded-lg bg-zinc-50 p-3">
|
|
<flux:text size="sm" class="text-zinc-600">
|
|
{{ __('client.dashboard.latest_update') }}:
|
|
</flux:text>
|
|
<flux:text class="mt-1">
|
|
{{ Str::limit($latestUpdate->update_text, 100) }}
|
|
</flux:text>
|
|
</div>
|
|
@endif
|
|
@endif
|
|
<div class="pt-2">
|
|
<flux:button
|
|
variant="ghost"
|
|
size="sm"
|
|
:href="route('client.timelines.index')"
|
|
wire:navigate
|
|
>
|
|
{{ __('client.dashboard.view_all_cases') }}
|
|
</flux:button>
|
|
</div>
|
|
</div>
|
|
@else
|
|
<div class="text-center py-4">
|
|
<flux:icon name="folder-open" class="mx-auto h-12 w-12 text-zinc-300" />
|
|
<flux:text class="mt-2 text-zinc-500">{{ __('client.dashboard.no_cases') }}</flux:text>
|
|
</div>
|
|
@endif
|
|
</div>
|
|
|
|
{{-- Recent Updates Widget --}}
|
|
<div class="rounded-lg border border-zinc-200 bg-white p-4 sm:p-6 shadow-sm ">
|
|
<flux:heading size="lg" class="mb-4 text-base sm:text-lg">
|
|
{{ __('client.dashboard.recent_updates') }}
|
|
</flux:heading>
|
|
|
|
@if ($recentUpdates->isNotEmpty())
|
|
<div class="space-y-3">
|
|
@foreach ($recentUpdates as $update)
|
|
<div class="border-b border-zinc-100 pb-3 last:border-0 last:pb-0 ">
|
|
<div class="flex items-start justify-between gap-2">
|
|
<div class="flex-1">
|
|
<flux:text size="sm" class="font-medium">
|
|
{{ $update->timeline->case_name }}
|
|
</flux:text>
|
|
<flux:text size="sm" class="text-zinc-500">
|
|
{{ $update->created_at->locale(app()->getLocale())->diffForHumans() }}
|
|
</flux:text>
|
|
<flux:text class="mt-1">
|
|
{{ Str::limit($update->update_text, 80) }}
|
|
</flux:text>
|
|
</div>
|
|
<flux:button
|
|
variant="ghost"
|
|
size="xs"
|
|
:href="route('client.timelines.show', $update->timeline)"
|
|
wire:navigate
|
|
icon="arrow-right"
|
|
/>
|
|
</div>
|
|
</div>
|
|
@endforeach
|
|
</div>
|
|
@else
|
|
<div class="text-center py-4">
|
|
<flux:icon name="bell-slash" class="mx-auto h-12 w-12 text-zinc-300" />
|
|
<flux:text class="mt-2 text-zinc-500">{{ __('client.dashboard.no_updates') }}</flux:text>
|
|
</div>
|
|
@endif
|
|
</div>
|
|
|
|
{{-- Booking Status Widget --}}
|
|
<div class="rounded-lg border border-zinc-200 bg-white p-4 sm:p-6 shadow-sm ">
|
|
<flux:heading size="lg" class="mb-4 text-base sm:text-lg">
|
|
{{ __('client.dashboard.booking_status') }}
|
|
</flux:heading>
|
|
|
|
<div class="space-y-4">
|
|
@if ($pendingBookingsCount > 0)
|
|
<div class="flex items-center gap-2">
|
|
<flux:icon name="clock" class="h-5 w-5 text-amber-500" />
|
|
<flux:text>
|
|
{{ trans_choice('client.dashboard.pending_bookings', $pendingBookingsCount, ['count' => $pendingBookingsCount]) }}
|
|
</flux:text>
|
|
</div>
|
|
@endif
|
|
|
|
<div class="rounded-lg p-3 {{ $canBookToday ? 'bg-green-50' : 'bg-amber-50' }}">
|
|
@if ($canBookToday)
|
|
<div class="flex items-center gap-2">
|
|
<flux:icon name="check-circle" class="h-5 w-5 text-green-600" />
|
|
<flux:text class="text-green-700">
|
|
{{ __('client.dashboard.can_book') }}
|
|
</flux:text>
|
|
</div>
|
|
@else
|
|
<div class="flex items-center gap-2">
|
|
<flux:icon name="information-circle" class="h-5 w-5 text-amber-600" />
|
|
<flux:text class="text-amber-700">
|
|
{{ __('client.dashboard.cannot_book') }}
|
|
</flux:text>
|
|
</div>
|
|
@endif
|
|
</div>
|
|
|
|
<div class="pt-2">
|
|
<flux:button
|
|
variant="primary"
|
|
:href="route('client.consultations.book')"
|
|
wire:navigate
|
|
:disabled="!$canBookToday"
|
|
class="w-full justify-center"
|
|
>
|
|
{{ __('client.dashboard.book_consultation') }}
|
|
</flux:button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|