341 lines
12 KiB
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();
|
|
}
|
|
}
|
|
}
|