feat: improve visits filter UX and update currency label
This commit is contained in:
@@ -58,7 +58,6 @@ public function form(Schema $schema): Schema
|
||||
->label(__('visits.fields.visit_time'))
|
||||
->native(false)
|
||||
->seconds(false)
|
||||
->minutesStep(15)
|
||||
->afterStateHydrated(function ($component, $state) {
|
||||
if (blank($state)) {
|
||||
$component->state(now()->format('Y-m-d H:i:s'));
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
namespace App\Filament\Resources;
|
||||
|
||||
use App\Filament\Resources\VisitResource\Pages;
|
||||
use App\Filament\Widgets\VisitStatsWidget;
|
||||
use App\Models\Doctor;
|
||||
use App\Models\Patient;
|
||||
use App\Models\PaymentType;
|
||||
@@ -29,6 +30,7 @@
|
||||
use Filament\Schemas\Components\Section;
|
||||
use Filament\Schemas\Schema;
|
||||
use Filament\Tables\Columns\TextColumn;
|
||||
use Filament\Tables\Enums\FiltersLayout;
|
||||
use Filament\Tables\Filters\Filter;
|
||||
use Filament\Tables\Filters\SelectFilter;
|
||||
use Filament\Tables\Table;
|
||||
@@ -42,6 +44,13 @@ class VisitResource extends Resource
|
||||
|
||||
protected static ?string $recordTitleAttribute = 'visit_date';
|
||||
|
||||
public static function getWidgets(): array
|
||||
{
|
||||
return [
|
||||
VisitStatsWidget::class,
|
||||
];
|
||||
}
|
||||
|
||||
public static function getGloballySearchableAttributes(): array
|
||||
{
|
||||
return ['visit_date', 'patientRecord.first_name', 'patientRecord.last_name', 'treatment_description'];
|
||||
@@ -311,9 +320,11 @@ public static function table(Table $table): Table
|
||||
->label(__('visits.filters.to_date'))
|
||||
->jalali(),
|
||||
])
|
||||
->columns(2)
|
||||
->columnSpan(2)
|
||||
->query(fn ($query, array $data) => $query
|
||||
->when($data['from_date'], fn ($q) => $q->whereDate('visit_date', '>=', $data['from_date']))
|
||||
->when($data['to_date'], fn ($q) => $q->whereDate('visit_date', '<=', $data['to_date']))
|
||||
->when($data['from_date'] ?? null, fn ($q) => $q->whereDate('visit_date', '>=', $data['from_date']))
|
||||
->when($data['to_date'] ?? null, fn ($q) => $q->whereDate('visit_date', '<=', $data['to_date']))
|
||||
)
|
||||
->indicateUsing(function (array $data): array {
|
||||
$indicators = [];
|
||||
@@ -344,6 +355,8 @@ public static function table(Table $table): Table
|
||||
fn ($p) => [$p->id => $p->payment_type]
|
||||
)->toArray()),
|
||||
])
|
||||
->filtersLayout(FiltersLayout::AboveContent)
|
||||
->filtersFormColumns(5)
|
||||
->recordActions([
|
||||
EditAction::make(),
|
||||
DeleteAction::make(),
|
||||
|
||||
@@ -19,4 +19,40 @@ protected function getHeaderActions(): array
|
||||
->icon('heroicon-o-plus'),
|
||||
];
|
||||
}
|
||||
|
||||
protected function getHeaderWidgets(): array
|
||||
{
|
||||
return VisitResource::getWidgets();
|
||||
}
|
||||
|
||||
public function applyTableFilters(): void
|
||||
{
|
||||
parent::applyTableFilters();
|
||||
$this->dispatchStatsUpdate();
|
||||
}
|
||||
|
||||
public function updatedTableFilters(): void
|
||||
{
|
||||
parent::updatedTableFilters();
|
||||
$this->dispatchStatsUpdate();
|
||||
}
|
||||
|
||||
public function resetTableFiltersForm(): void
|
||||
{
|
||||
parent::resetTableFiltersForm();
|
||||
$this->dispatchStatsUpdate();
|
||||
}
|
||||
|
||||
protected function dispatchStatsUpdate(): void
|
||||
{
|
||||
$filters = $this->tableFilters ?? [];
|
||||
|
||||
$this->dispatch('visit-stats-updated',
|
||||
fromDate: ($filters['visit_date_range']['from_date'] ?? null) ?: null,
|
||||
toDate: ($filters['visit_date_range']['to_date'] ?? null) ?: null,
|
||||
doctor: isset($filters['doctor']['value']) && $filters['doctor']['value'] !== '' ? (int) $filters['doctor']['value'] : null,
|
||||
treatment: isset($filters['treatment']['value']) && $filters['treatment']['value'] !== '' ? (int) $filters['treatment']['value'] : null,
|
||||
paymentType: isset($filters['payment_type']['value']) && $filters['payment_type']['value'] !== '' ? (int) $filters['payment_type']['value'] : null,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Widgets;
|
||||
|
||||
use App\Models\Visit;
|
||||
use Filament\Widgets\StatsOverviewWidget as BaseWidget;
|
||||
use Filament\Widgets\StatsOverviewWidget\Stat;
|
||||
use Livewire\Attributes\On;
|
||||
|
||||
class VisitStatsWidget extends BaseWidget
|
||||
{
|
||||
public ?string $fromDate = null;
|
||||
public ?string $toDate = null;
|
||||
public ?int $doctor = null;
|
||||
public ?int $treatment = null;
|
||||
public ?int $paymentType = null;
|
||||
|
||||
public function mount(): void
|
||||
{
|
||||
$this->fromDate = now()->toDateString();
|
||||
$this->toDate = now()->toDateString();
|
||||
}
|
||||
|
||||
#[On('visit-stats-updated')]
|
||||
public function updateStats(
|
||||
?string $fromDate,
|
||||
?string $toDate,
|
||||
?int $doctor,
|
||||
?int $treatment,
|
||||
?int $paymentType,
|
||||
): void {
|
||||
if (! $fromDate && ! $toDate) {
|
||||
$this->fromDate = now()->toDateString();
|
||||
$this->toDate = null;
|
||||
} else {
|
||||
$this->fromDate = $fromDate;
|
||||
$this->toDate = $toDate;
|
||||
}
|
||||
$this->doctor = $doctor;
|
||||
$this->treatment = $treatment;
|
||||
$this->paymentType = $paymentType;
|
||||
}
|
||||
|
||||
protected function buildQuery()
|
||||
{
|
||||
$query = Visit::query();
|
||||
|
||||
if ($this->fromDate) {
|
||||
$query->whereDate('visit_date', '>=', $this->fromDate);
|
||||
}
|
||||
|
||||
if ($this->toDate) {
|
||||
$query->whereDate('visit_date', '<=', $this->toDate);
|
||||
}
|
||||
|
||||
if ($this->doctor) {
|
||||
$query->where('doctor', $this->doctor);
|
||||
}
|
||||
|
||||
if ($this->treatment) {
|
||||
$query->where('treatment', $this->treatment);
|
||||
}
|
||||
|
||||
if ($this->paymentType) {
|
||||
$query->where('payment_type', $this->paymentType);
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
protected function getStats(): array
|
||||
{
|
||||
$query = $this->buildQuery();
|
||||
|
||||
$totalPaid = (int) (clone $query)->sum('paid_amount');
|
||||
$totalCost = (int) (clone $query)->sum('treatment_cost');
|
||||
$totalRemained = $totalCost - $totalPaid;
|
||||
|
||||
$currency = __('visits.fields.currency');
|
||||
|
||||
return [
|
||||
Stat::make(__('visits.fields.paid_amount'), number_format($totalPaid) . ' ' . $currency)
|
||||
->color('success')
|
||||
->icon('heroicon-o-banknotes'),
|
||||
|
||||
Stat::make(__('visits.fields.treatment_cost'), number_format($totalCost) . ' ' . $currency)
|
||||
->color('info')
|
||||
->icon('heroicon-o-clipboard-document-list'),
|
||||
|
||||
Stat::make(__('visits.fields.remained_amount'), number_format($totalRemained) . ' ' . $currency)
|
||||
->color($totalRemained > 0 ? 'danger' : 'success')
|
||||
->icon('heroicon-o-arrow-trending-down'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -16,10 +16,12 @@
|
||||
use App\Observers\PatientIllnessObserver;
|
||||
use App\Observers\PatientObserver;
|
||||
use App\Observers\SyncObserver;
|
||||
use App\Filament\Widgets\VisitStatsWidget;
|
||||
use Filament\Actions\DeleteAction;
|
||||
use Filament\Actions\DeleteBulkAction;
|
||||
use Filament\Support\Enums\Alignment;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Livewire\Livewire;
|
||||
|
||||
class AppServiceProvider extends ServiceProvider
|
||||
{
|
||||
@@ -27,6 +29,7 @@ public function register(): void {}
|
||||
|
||||
public function boot(): void
|
||||
{
|
||||
Livewire::component('app.filament.widgets.visit-stats-widget', VisitStatsWidget::class);
|
||||
$observer = new SyncObserver();
|
||||
|
||||
Patient::observe($observer);
|
||||
|
||||
Reference in New Issue
Block a user