simedkom/app/Filament/Pages/AdminVerification.php
Yoga Pangestu 6fe5e81717 refactor: add type hinting to closures and fix logic errors in Filament codebase
- Added comprehensive parameter and return type hinting to closures across various Filament resources, actions, and relation managers to improve type safety and static analysis.
- Fixed logic in ReportRelationManager where report count was incorrectly compared, preventing the 'Buat Laporan' button from showing correctly.
- Corrected namespaces for RepeatableEntry and TextEntry in ViewCooperation (changed from Filament\Schemas to Filament\Infolists).
- Fixed component rendering in JournalistResource form by ensuring sections are correctly returned in the mapping.
- Added @var docblocks to resolve lint errors related to auth()->user() and verified query builder return types.
- Standardized file retrieval logic in PartnerResource and improved overall code reliability."
2026-01-12 15:17:21 +07:00

270 lines
9.4 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\Models\VerificationRequest;
use App\Services\VerificationService;
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\Notifications\Notification;
use Filament\Pages\Page;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use UnitEnum;
class AdminVerification extends Page implements HasActions, HasForms
{
use HasPageShield, InteractsWithActions, InteractsWithForms;
protected static string|BackedEnum|null $navigationIcon = 'heroicon-o-user-group';
protected static ?string $title = 'Verifikasi Rekanan';
protected static string|UnitEnum|null $navigationGroup = 'Kelola';
protected static ?string $navigationLabel = 'Verifikasi Rekanan';
protected static ?int $navigationSort = 2;
protected static ?string $slug = 'manage/admin-verification';
protected string $view = 'filament.pages.admin-verification';
public string $filterStatus = 'all';
protected VerificationService $verificationService;
public function boot(): void
{
$this->verificationService = app(VerificationService::class);
}
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 $this->verificationService->getCompanyName($request);
}
/**
* 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
{
return $this->verificationService->getVerificationSummary($request);
}
/**
* 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 Lengkap')
->modalDescription('Apakah Anda yakin ingin menyetujui verifikasi ini? Status akan diterapkan pada Perusahaan, Media, dan semua Jurnalis sekaligus.')
->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('User akan diminta untuk memperbaiki data sesuai catatan yang diberikan.')
->schema([
Textarea::make('note')
->label('Catatan Revisi')
->required()
->placeholder('Jelaskan bagian yang perlu diperbaiki (Perusahaan, Media, atau Jurnalis)...')
->helperText('Catatan ini akan ditampilkan kepada user.')
->autocomplete(false)
->autofocus(),
])
->action(function (array $arguments, array $data): void {
$this->processReview($arguments['id'], DecisionAdmin::NEED_REVISION, $data['note']);
});
}
/**
* 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 Lengkap')
->modalDescription('Pengajuan akan ditolak secara permanen. Status REJECTED akan diterapkan pada Perusahaan, Media, dan semua Jurnalis. User tidak dapat mengajukan ulang untuk data ini.')
->modalSubmitActionLabel('Ya, Tolak')
->schema([
Textarea::make('note')
->label('Alasan Penolakan')
->required()
->placeholder('Jelaskan alasan pengajuan ditolak...')
->helperText('Alasan ini akan ditampilkan kepada user.')
->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) {
Notification::make()
->title('Error')
->body('Data verifikasi tidak ditemukan.')
->danger()
->send();
return;
}
try {
$this->verificationService->processReview($verificationRequest, $decision, $note);
$actionLabel = match ($decision) {
DecisionAdmin::APPROVED => 'disetujui',
DecisionAdmin::NEED_REVISION => 'diminta revisi',
DecisionAdmin::REJECTED => 'ditolak',
};
$color = match ($decision) {
DecisionAdmin::APPROVED => 'success',
DecisionAdmin::NEED_REVISION => 'info',
DecisionAdmin::REJECTED => 'danger',
};
Notification::make()
->title('Berhasil')
->body("Verifikasi lengkap telah {$actionLabel}. Status telah diterapkan pada Perusahaan, Media, dan semua Jurnalis.")
->color($color)
->send();
} catch (\Exception $e) {
Notification::make()
->title('Error')
->body('Terjadi kesalahan saat memproses verifikasi: '.$e->getMessage())
->danger()
->send();
}
}
}