simedkom/app/Filament/Pages/Verification.php

433 lines
15 KiB
PHP

<?php
namespace App\Filament\Pages;
use App\Enums\VerificationStatus;
use App\Filament\Pages\Company as PagesCompany;
use App\Filament\Resources\Manage\Journalists\Pages\ManageJournalists;
use App\Filament\Resources\Manage\Partners\PartnerResource;
use App\Filament\Support\CheerfulNotification;
use App\Models\Company;
use App\Models\User;
use App\Models\VerificationRequest;
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\Concerns\InteractsWithForms;
use Filament\Forms\Contracts\HasForms;
use Filament\Pages\Page;
use Filament\Support\Icons\Heroicon;
use Illuminate\Support\Collection;
use UnitEnum;
class Verification extends Page implements HasActions, HasForms
{
use HasPageShield, InteractsWithActions, InteractsWithForms;
public ?Company $company = null;
protected static string|UnitEnum|null $navigationGroup = 'Kelola';
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedCheckBadge;
protected static ?string $navigationLabel = 'Verifikasi';
protected static ?int $navigationSort = 5;
protected static ?string $slug = 'manage/verification';
protected static ?string $title = 'Verifikasi';
public ?VerificationRequest $verificationRequest = null;
protected string $view = 'filament.pages.verification';
public function mount(): void
{
$user = auth()->user();
$this->company = $user->company;
// Load unified verification request
if ($this->company) {
$this->verificationRequest = $this->company->verificationRequest()
->with(['reviews.reviewer'])
->latest()
->first();
}
}
/**
* Get unified verification progress
*/
public function getVerificationProgress(): array
{
$isComplete = $this->isDataComplete();
$missingData = $this->getMissingData();
$status = $this->verificationRequest?->status;
$progress = [
'currentStep' => $this->resolveStep(),
'isDataComplete' => $isComplete,
'missingData' => $missingData,
'canSubmit' => $this->canSubmit(),
'verification' => $this->verificationRequest,
'status' => [
'label' => $status?->getLabel() ?? 'Belum Mengajukan',
'color' => $status?->getColor() ?? 'gray',
],
'isApproved' => $status === VerificationStatus::APPROVED,
'isRejected' => $status === VerificationStatus::REJECTED,
'isPending' => $status === VerificationStatus::PENDING,
'needsRevision' => $status === VerificationStatus::NEED_REVISION,
'latestNotes' => $this->getLatestReviewNotes(),
'reviewHistory' => $this->getReviewHistory(),
'company' => $this->company,
'partnerMedia' => $this->company?->partnerMedia,
'journalists' => $this->company?->partnerMedia?->journalists ?? collect(),
'steps' => $this->getSteps($isComplete),
];
// Add additional data for view
$progress['missingDataButtons'] = $this->getMissingDataButtons();
$progress['alert'] = $this->getAlertData();
$progress['lastSubmittedAt'] = $this->verificationRequest?->updated_at?->translatedFormat('l, d M Y H:i');
return $progress;
}
/**
* Submit unified verification action
*/
public function submitVerificationAction(): Action
{
$progress = $this->getVerificationProgress();
return Action::make('submitVerification')
->label(fn (): string => $this->verificationRequest && $this->verificationRequest->status === VerificationStatus::NEED_REVISION
? 'Kirim Ulang'
: 'Ajukan Verifikasi')
->icon('heroicon-o-paper-airplane')
->color('primary')
->requiresConfirmation()
->modalHeading(fn () => CheerfulNotification::getByKey('verification.submit.title'))
->modalDescription(function () use ($progress): string {
if (! $progress['isDataComplete']) {
return CheerfulNotification::getByKey('verification.submit.description.incomplete', ['missing' => implode(', ', $progress['missingData'])]);
}
if ($this->verificationRequest && $this->verificationRequest->status === VerificationStatus::NEED_REVISION) {
return CheerfulNotification::getByKey('verification.submit.description.revision');
}
return CheerfulNotification::getByKey('verification.submit.description.default');
})
->modalSubmitActionLabel('Ya, Ajukan')
->visible(fn (): bool => $progress['canSubmit'])
->disabled(fn (): bool => ! $progress['isDataComplete'])
->action(function () use ($progress): void {
if (! $progress['isDataComplete']) {
CheerfulNotification::danger(
CheerfulNotification::getByKey('verification.incomplete.title'),
CheerfulNotification::getByKey('verification.incomplete.body', ['missing' => implode(', ', $progress['missingData'])])
)->send();
return;
}
$isRevision = $this->verificationRequest && $this->verificationRequest->status === VerificationStatus::NEED_REVISION;
$this->verificationRequest = $this->submitVerification();
CheerfulNotification::success(
CheerfulNotification::getByKey('verification.success.title'),
CheerfulNotification::getByKey('verification.success.body')
)->send();
User::superAdmin()
->get()
->each(function ($admin) use ($isRevision): void {
$user = auth()->user();
$admin->notify(new BroadcastNotification([
'title' => $isRevision
? CheerfulNotification::getByKey('verification.admin_notify.title.revision')
: CheerfulNotification::getByKey('verification.admin_notify.title.new'),
'body' => $isRevision
? CheerfulNotification::getByKey('verification.admin_notify.body.revision', ['name' => $user->name, 'company' => $user->company->name])
: CheerfulNotification::getByKey('verification.admin_notify.body.new', ['name' => $user->name, 'company' => $user->company->name]),
'action' => [
Action::make('view')
->label('Lihat')
->url(PartnerResource::getUrl('view', ['record' => $user])),
],
]));
});
});
}
/**
* Check if all required data is complete for verification
*/
protected function isDataComplete(): bool
{
return $this->company !== null
&& $this->company->partnerMedia !== null
&& $this->company->partnerMedia?->journalists !== null
&& $this->company->partnerMedia->journalists->count() > 0;
}
/**
* Get missing data items for verification
*/
protected function getMissingData(): array
{
$missing = [];
if (! $this->company) {
$missing[] = 'Data Perusahaan';
}
if ($this->company && ! $this->company->partnerMedia) {
$missing[] = 'Data Media';
}
if ($this->company && $this->company->partnerMedia && ($this->company->partnerMedia?->journalists === null || $this->company->partnerMedia?->journalists?->count() === 0)) {
$missing[] = 'Data Jurnalis (minimal 1)';
}
return $missing;
}
/**
* Check if user can submit verification
*/
protected function canSubmit(): bool
{
if (! $this->isDataComplete()) {
return false;
}
// No verification yet - can submit
if (! $this->verificationRequest) {
return true;
}
// Can resubmit only if status is NEED_REVISION
return $this->verificationRequest->status === VerificationStatus::NEED_REVISION;
}
/**
* Determine the current step based on status
*/
protected function resolveStep(): int
{
if (! $this->isDataComplete()) {
return 1; // Data belum lengkap
}
if (! $this->verificationRequest) {
return 1; // Belum submit
}
return match ($this->verificationRequest->status) {
VerificationStatus::PENDING,
VerificationStatus::NEED_REVISION => 2, // In review or needs revision
VerificationStatus::APPROVED,
VerificationStatus::REJECTED => 3, // Final decision
default => 1, // Draft / not submitted yet
};
}
/**
* Get the latest review notes
*/
protected function getLatestReviewNotes(): ?string
{
if (! $this->verificationRequest) {
return null;
}
$latestReview = $this->verificationRequest->reviews()
->where(fn ($q) => $q->needRevision()->orWhere(fn ($q) => $q->rejected()))
->latest()
->first();
return $latestReview?->note;
}
/**
* Get all reviews history
*/
protected function getReviewHistory(): Collection
{
if (! $this->verificationRequest) {
return collect();
}
return $this->verificationRequest->reviews()
->with('reviewer')
->latest()
->get();
}
/**
* Submit unified verification
*/
protected function submitVerification(): VerificationRequest
{
if ($this->verificationRequest && $this->verificationRequest->status === VerificationStatus::NEED_REVISION) {
// Resubmit - update existing request
$this->verificationRequest->update([
'status' => VerificationStatus::PENDING,
]);
return $this->verificationRequest->fresh(['reviews.reviewer']);
}
// New submission
return VerificationRequest::create([
'company_id' => $this->company->id,
'submitted_by' => auth()->id(),
'status' => VerificationStatus::PENDING,
])->load(['reviews.reviewer']);
}
/**
* Get alert data for verification status
*/
protected function getAlertData(): ?array
{
if (! $this->verificationRequest) {
return null;
}
$status = $this->verificationRequest->status;
$latestNotes = $this->getLatestReviewNotes();
if ($status === VerificationStatus::NEED_REVISION && $latestNotes) {
return [
'type' => 'revision',
'icon' => 'exclamation-triangle',
'title' => 'Catatan Revisi dari Admin',
'message' => $latestNotes,
'color' => 'warning',
];
}
if ($status === VerificationStatus::REJECTED && $latestNotes) {
return [
'type' => 'rejected',
'icon' => 'x-circle',
'title' => 'Pengajuan Ditolak',
'message' => $latestNotes,
'color' => 'danger',
];
}
if ($status === VerificationStatus::APPROVED) {
return [
'type' => 'approved',
'icon' => 'check-circle',
'title' => 'Verifikasi Diterima',
'message' => CheerfulNotification::getByKey('verification.alert.approved.body'),
'color' => 'success',
];
}
if ($status === VerificationStatus::PENDING) {
return [
'type' => 'pending',
'icon' => 'clock',
'title' => CheerfulNotification::getByKey('verification.alert.pending.title'),
'message' => CheerfulNotification::getByKey('verification.alert.pending.body'),
'color' => 'info',
'animate' => true,
];
}
return null;
}
/**
* Get missing data buttons configuration
*/
protected function getMissingDataButtons(): array
{
$buttons = [];
if (! $this->company) {
$buttons[] = [
'label' => 'Lengkapi Data Perusahaan',
'url' => PagesCompany::getUrl(),
'icon' => 'building-office-2',
'color' => 'warning',
];
}
if ($this->company && ! $this->company->partnerMedia) {
$buttons[] = [
'label' => 'Lengkapi Data Media',
'url' => Media::getUrl(),
'icon' => 'newspaper',
'color' => 'warning',
];
}
if ($this->company && $this->company->partnerMedia && ($this->company->partnerMedia->journalists === null || $this->company->partnerMedia->journalists->count() === 0)) {
$buttons[] = [
'label' => 'Lengkapi Data Jurnalis',
'url' => ManageJournalists::getUrl(),
'icon' => 'user-circle',
'color' => 'warning',
];
}
return $buttons;
}
/**
* Get steps array for stepper
*/
protected function getSteps(bool $isComplete): array
{
$status = $this->verificationRequest?->status;
return [
[
'title' => 'Pengajuan',
'description' => $isComplete
? 'Data sudah lengkap, silakan ajukan verifikasi'
: 'Silakan lengkapi data',
],
[
'title' => 'Verifikasi',
'description' => match ($status) {
VerificationStatus::NEED_REVISION => 'Perlu perbaikan data',
VerificationStatus::PENDING => 'Sedang diverifikasi admin',
default => 'Menunggu pengajuan',
},
],
[
'title' => 'Selesai',
'description' => match ($status) {
VerificationStatus::APPROVED => 'Verifikasi disetujui',
VerificationStatus::REJECTED => 'Verifikasi ditolak',
default => 'Menunggu keputusan',
},
],
];
}
public function getViewData(): array
{
return [
'verificationProgress' => $this->getVerificationProgress(),
];
}
}