152 lines
6.9 KiB
PHP
152 lines
6.9 KiB
PHP
<?php
|
|
|
|
namespace App\Filament\Pages\Transactions;
|
|
|
|
use App\Services\ComplaintService;
|
|
use App\Services\CustomerService;
|
|
use BackedEnum;
|
|
use Filament\Actions\Action;
|
|
use Filament\Actions\CreateAction;
|
|
use Filament\Forms\Components\DatePicker;
|
|
use Filament\Forms\Components\Select;
|
|
use Filament\Forms\Components\Textarea;
|
|
use Filament\Notifications\Notification;
|
|
use Filament\Pages\Page;
|
|
use Filament\Schemas\Components\EmbeddedTable;
|
|
use Filament\Schemas\Schema;
|
|
use Filament\Tables\Columns\TextColumn;
|
|
use Filament\Tables\Concerns\InteractsWithTable;
|
|
use Filament\Tables\Contracts\HasTable;
|
|
use Filament\Tables\Filters\Filter;
|
|
use Filament\Tables\Table;
|
|
use Illuminate\Auth\Access\AuthorizationException;
|
|
use Illuminate\Contracts\Support\Htmlable;
|
|
use Illuminate\Support\Arr;
|
|
use UnitEnum;
|
|
|
|
class Complaints extends Page implements HasTable
|
|
{
|
|
use InteractsWithTable;
|
|
|
|
protected static string|BackedEnum|null $navigationIcon = 'heroicon-o-chat-bubble-left-ellipsis';
|
|
|
|
protected static string|UnitEnum|null $navigationGroup = 'Transaksi';
|
|
|
|
protected static ?int $navigationSort = 1;
|
|
|
|
protected static ?string $navigationLabel = 'Komplain';
|
|
|
|
protected static ?string $slug = 'transactions/complaints';
|
|
|
|
protected string $view = 'filament.pages.blank';
|
|
|
|
public function getTitle(): string|Htmlable
|
|
{
|
|
return 'Komplain';
|
|
}
|
|
|
|
public function table(Table $table): Table
|
|
{
|
|
/** @var ComplaintService $service */
|
|
$service = app(ComplaintService::class);
|
|
|
|
return $table
|
|
->records(function (?string $search, ?string $sortColumn, ?string $sortDirection, int|string $page, int|string $recordsPerPage, ?array $filters) use ($service) {
|
|
$status = Arr::get($filters, 'status.value') ?? Arr::get($filters, 'status');
|
|
|
|
return $service->paginate(
|
|
search: $search,
|
|
sortColumn: $sortColumn,
|
|
sortDirection: $sortDirection,
|
|
page: (int) $page,
|
|
perPage: (int) $recordsPerPage,
|
|
filters: array_filter(['status' => is_array($status) ? ($status['value'] ?? null) : $status]),
|
|
);
|
|
})
|
|
->columns([
|
|
TextColumn::make('complaint_number')->label('No. Komplain')->badge()->sortable(),
|
|
TextColumn::make('customers.name')->label('Pelanggan'),
|
|
TextColumn::make('orders.order_number')->label('Order')->placeholder('—'),
|
|
TextColumn::make('complaint_date')->label('Tgl')->date('d M Y')->sortable(),
|
|
TextColumn::make('category')
|
|
->label('Kategori')
|
|
->badge()
|
|
->color('info')
|
|
->formatStateUsing(fn (string $state): string => ComplaintService::CATEGORIES[$state] ?? $state),
|
|
TextColumn::make('priority')
|
|
->label('Prioritas')
|
|
->badge()
|
|
->color(fn (string $state): string => match ($state) {
|
|
'high' => 'danger',
|
|
'medium' => 'warning',
|
|
default => 'gray',
|
|
})
|
|
->formatStateUsing(fn (string $state): string => ComplaintService::PRIORITIES[$state] ?? $state)
|
|
->sortable(),
|
|
TextColumn::make('description')->label('Deskripsi')->limit(40),
|
|
TextColumn::make('status')
|
|
->label('Status')
|
|
->badge()
|
|
->color(fn (string $state): string => $state === ComplaintService::STATUS_OPEN ? 'warning' : 'success')
|
|
->formatStateUsing(fn (string $state): string => $state === ComplaintService::STATUS_OPEN ? 'Terbuka' : 'Selesai')
|
|
->sortable(),
|
|
TextColumn::make('resolution')->label('Resolusi')->limit(30)->placeholder('—'),
|
|
])
|
|
->filters([
|
|
Filter::make('status')
|
|
->schema([
|
|
Select::make('value')->label('Status')->options([
|
|
ComplaintService::STATUS_OPEN => 'Terbuka',
|
|
ComplaintService::STATUS_RESOLVED => 'Selesai',
|
|
]),
|
|
]),
|
|
])
|
|
->recordActions([
|
|
Action::make('tutup')
|
|
->label('Tutup')
|
|
->color('success')
|
|
->icon('heroicon-o-check-badge')
|
|
->visible(fn (array $record): bool => $service->canResolve($record, auth()->user()))
|
|
->schema(fn (Schema $schema): Schema => $schema->components([
|
|
Textarea::make('resolution')->label('Resolusi / Tindak Lanjut')->required(),
|
|
]))
|
|
->action(function (array $data, array $record) use ($service): void {
|
|
try {
|
|
$service->resolve($record['id'], $data['resolution'], auth()->user());
|
|
} catch (AuthorizationException) {
|
|
Notification::make()->title('Akses ditolak')->body('Hanya pembuat komplain atau admin yang dapat menutup.')->danger()->send();
|
|
|
|
return;
|
|
}
|
|
|
|
Notification::make()->title('Komplain ditutup.')->success()->send();
|
|
}),
|
|
])
|
|
->headerActions([
|
|
CreateAction::make()
|
|
->label('Catat Komplain')
|
|
->schema(fn (Schema $schema): Schema => $schema->components([
|
|
Select::make('customer_id')->label('Pelanggan')->options(fn (): array => app(CustomerService::class)->optionsForSelect())->required()->searchable(),
|
|
Select::make('order_id')->label('Order Terkait (opsional)')->options(fn (): array => $service->orderOptions())->searchable(),
|
|
DatePicker::make('complaint_date')->label('Tanggal')->default(now()->toDateString())->required(),
|
|
Select::make('category')->label('Kategori')->options(ComplaintService::CATEGORIES)->default('product')->required(),
|
|
Select::make('priority')->label('Prioritas')->options(ComplaintService::PRIORITIES)->default('medium')->required(),
|
|
Textarea::make('description')->label('Deskripsi Masalah')->required()->columnSpanFull(),
|
|
]))
|
|
->using(function (array $data): void {
|
|
app(ComplaintService::class)->createComplaint($data, auth()->user());
|
|
Notification::make()->title('Komplain dicatat.')->success()->send();
|
|
}),
|
|
])
|
|
->paginated([10, 25, 50]);
|
|
}
|
|
|
|
public function content(Schema $schema): Schema
|
|
{
|
|
return $schema
|
|
->components([
|
|
EmbeddedTable::make(),
|
|
]);
|
|
}
|
|
}
|