simedkom/app/Filament/Pages/AdminVerification.php

341 lines
12 KiB
PHP

<?php
namespace App\Filament\Pages;
use App\Enums\DecisionAdmin;
use App\Enums\VerificationStatus;
use App\Filament\Resources\Manage\Partners\Pages\ViewPartner;
use App\Filament\Support\CheerfulNotification;
use App\Models\User;
use App\Models\VerificationRequest;
use App\Models\VerificationReview;
use App\Notifications\BroadcastNotification;
use BackedEnum;
use BezhanSalleh\FilamentShield\Traits\HasPageShield;
use Filament\Actions\Action;
use Filament\Actions\Concerns\InteractsWithActions;
use Filament\Actions\Contracts\HasActions;
use Filament\Forms\Components\Textarea;
use Filament\Forms\Concerns\InteractsWithForms;
use Filament\Forms\Contracts\HasForms;
use Filament\Pages\Page;
use Filament\Support\Enums\Width;
use Filament\Support\Icons\Heroicon;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use UnitEnum;
class AdminVerification extends Page implements HasActions, HasForms
{
use HasPageShield, InteractsWithActions, InteractsWithForms;
public string $filterStatus = 'all';
protected static string|UnitEnum|null $navigationGroup = 'Kelola';
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedUserGroup;
protected static ?string $navigationLabel = 'Verifikasi Rekanan';
protected static ?int $navigationSort = 6;
protected static ?string $slug = 'manage/admin-verification';
protected static ?string $title = 'Verifikasi Rekanan';
protected string $view = 'filament.pages.admin-verification';
public static function getNavigationBadge(): ?string
{
return VerificationRequest::query()->pending()->count();
}
public function getVerifications(): LengthAwarePaginator
{
$query = VerificationRequest::with([
'company.user',
'company.partnerMedia',
'company.partnerMedia.journalists',
'submittedBy',
'reviews.reviewer',
])
->latest('updated_at');
// Apply status filter
if ($this->filterStatus !== 'all') {
$query->where('status', $this->filterStatus);
}
return $query->paginate(12);
}
public function setFilter(string $status): void
{
$this->filterStatus = $status;
}
public function getStatusCounts(): array
{
return [
'all' => VerificationRequest::count(),
'pending' => VerificationRequest::pending()->count(),
'revision' => VerificationRequest::needRevision()->count(),
'approved' => VerificationRequest::approved()->count(),
'rejected' => VerificationRequest::rejected()->count(),
];
}
/**
* Get company name from verification request
*/
public function getCompanyName(VerificationRequest $request): string
{
return $request->company?->name ?? 'Data tidak ditemukan';
}
/**
* Get detail URL for the company
*/
public function getDetailUrl(VerificationRequest $request): ?string
{
if (! $request->company || ! $request->company->user_id) {
return null;
}
return ViewPartner::getUrl(['record' => $request->company->user_id]);
}
/**
* Get verification summary data
*/
public function getVerificationSummary(VerificationRequest $request): array
{
$company = $request->company;
$media = $company?->partnerMedia;
$journalists = $media?->journalists ?? collect();
return [
'company' => [
'name' => $company?->name ?? 'Belum diisi',
'email' => $company?->email ?? '-',
'exists' => $company !== null,
],
'media' => [
'name' => $media?->name ?? 'Belum diisi',
'type' => $media?->type?->getLabel() ?? '-',
'exists' => $media !== null,
],
'journalists' => [
'count' => $journalists->count(),
'names' => $journalists->pluck('name')->take(3)->toArray(),
'exists' => $journalists->count() > 0,
],
];
}
/**
* Get card data for verification request
*/
public function getCardData(VerificationRequest $verification): array
{
$latestReview = $verification->reviews->first();
$summary = $this->getVerificationSummary($verification);
$status = $verification->status;
return [
'companyName' => $this->getCompanyName($verification),
'detailUrl' => $this->getDetailUrl($verification),
'status' => $status,
'statusLabel' => $status->getLabel(),
'statusColor' => $status->getColor(),
'summary' => $summary,
'latestReview' => $latestReview,
'latestReviewNote' => $latestReview?->note,
'latestReviewDecision' => $latestReview?->decision,
'latestReviewDecisionLabel' => $latestReview?->decision?->getLabel(),
'latestReviewDecisionColor' => $latestReview?->decision?->getColor(),
'latestReviewReviewer' => $latestReview?->reviewer?->name ?? 'Admin',
'latestReviewCreatedAt' => $latestReview?->created_at,
'submittedBy' => $verification->submittedBy?->name ?? 'Unknown',
'reviewsCount' => $verification->reviews->count(),
'canApprove' => $status === VerificationStatus::PENDING,
'isPending' => $status === VerificationStatus::PENDING,
'isRevision' => $status === VerificationStatus::NEED_REVISION,
'isApproved' => $status === VerificationStatus::APPROVED,
'isRejected' => $status === VerificationStatus::REJECTED,
];
}
/**
* Approve verification action
*/
public function approveAction(): Action
{
return Action::make('approve')
->label('Setujui')
->color('success')
->icon('heroicon-o-check')
->size('sm')
->requiresConfirmation()
->modalHeading('Setujui Verifikasi')
->modalDescription('Apakah Anda yakin ingin menyetujui verifikasi ini? Pastikan data sudah benar dan lengkap.')
->modalSubmitActionLabel('Ya, Setujui')
->action(function (array $arguments, array $data): void {
$this->processReview($arguments['id'], DecisionAdmin::APPROVED, $data['note'] ?? null);
});
}
/**
* Revision verification action
*/
public function revisionAction(): Action
{
return Action::make('revision')
->label('Minta Revisi')
->color('warning')
->icon('heroicon-o-pencil-square')
->size('sm')
->modalHeading('Minta Revisi')
->modalDescription('Pengguna akan diminta untuk memperbaiki data sesuai catatan yang diberikan.')
->schema([
Textarea::make('note')
->label('Catatan Revisi')
->required()
->placeholder('Jelaskan bagian yang perlu diperbaiki...')
->autocomplete(false)
->autofocus(),
])
->action(function (array $arguments, array $data): void {
$this->processReview($arguments['id'], DecisionAdmin::NEED_REVISION, $data['note']);
})
->modalWidth(Width::Large);
}
/**
* Reject verification action
*/
public function rejectAction(): Action
{
return Action::make('reject')
->label('Tolak')
->color('danger')
->icon('heroicon-o-x-mark')
->size('sm')
->requiresConfirmation()
->modalHeading('Tolak Verifikasi')
->modalDescription('Pengajuan akan ditolak secara permanen. Pengguna tidak dapat mengajukan ulang untuk data ini.')
->modalSubmitActionLabel('Ya, Tolak')
->schema([
Textarea::make('note')
->label('Alasan Penolakan')
->required()
->placeholder('Jelaskan alasan pengajuan ditolak...')
->autocomplete(false)
->autofocus(),
])
->action(function (array $arguments, array $data): void {
$this->processReview($arguments['id'], DecisionAdmin::REJECTED, $data['note']);
});
}
/**
* Process review decision
*/
protected function processReview(int $requestId, DecisionAdmin $decision, ?string $note): void
{
$verificationRequest = VerificationRequest::with(['company.partnerMedia.journalists'])->find($requestId);
if (! $verificationRequest) {
CheerfulNotification::danger(
'Belum ketemu nih 🙌',
'Data verifikasi belum kami temukan. Coba sebentar lagi, ya.',
)
->send();
return;
}
try {
DB::beginTransaction();
// Create review record
VerificationReview::create([
'verification_request_id' => $verificationRequest->id,
'reviewer_id' => auth()->id(),
'decision' => $decision,
'note' => $note,
]);
// Update verification request status
$newStatus = match ($decision) {
DecisionAdmin::APPROVED => VerificationStatus::APPROVED,
DecisionAdmin::NEED_REVISION => VerificationStatus::NEED_REVISION,
DecisionAdmin::REJECTED => VerificationStatus::REJECTED,
};
$verificationRequest->update([
'status' => $newStatus,
]);
DB::commit();
$actionLabel = match ($decision) {
DecisionAdmin::APPROVED => 'disetujui',
DecisionAdmin::NEED_REVISION => 'diminta revisi',
DecisionAdmin::REJECTED => 'ditolak',
};
CheerfulNotification::success(
'Berhasil Diproses! ✨',
"Mantap! Verifikasi telah {$actionLabel} dan notifikasi sudah dikirim ke pengguna. Kerja bagus, Admin! 💪"
)
->send();
// Notify User
$partnerUser = $verificationRequest->company?->user;
if ($partnerUser) {
$statusTitle = match ($decision) {
DecisionAdmin::APPROVED => 'Horee! Verifikasi Disetujui 🎉',
DecisionAdmin::NEED_REVISION => 'Yah, Ada Yang Perlu Direvisi nih 📝',
DecisionAdmin::REJECTED => 'Maaf, Verifikasi Ditolak 😔',
};
$body = match ($decision) {
DecisionAdmin::APPROVED => 'Yeay! Pengajuan verifikasi perusahaan Anda telah disetujui. Sekarang Anda sudah resmi terverifikasi dan bekerja sama dengan kami! 🎊',
DecisionAdmin::NEED_REVISION => 'Ayo sedikit lagi! Admin meminta beberapa perbaikan pada pengajuan Anda agar bisa segera disetujui. 💪',
DecisionAdmin::REJECTED => 'Yah, maaf banget... pengajuan verifikasi perusahaan Anda belum bisa kami terima saat ini. 💔',
};
if ($note) {
$body .= "\n\nCatatan Admin: {$note}";
}
$partnerUser->notify(new BroadcastNotification([
'title' => $statusTitle,
'body' => $body,
'action' => [
Action::make('view')
->label('Lihat')
->url(Verification::getUrl()),
],
]));
}
} catch (\Throwable $e) {
DB::rollBack();
Log::error('Verification processing failed', [
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
'request_id' => $requestId,
'decision' => $decision->value,
]);
CheerfulNotification::danger(
'Terjadi Kesalahan ⚠️',
'Proses verifikasi gagal karena kesalahan sistem. Tim kami sudah mencatat masalah ini. Silakan coba lagi nanti.',
)
->send();
}
}
}