refactor: Remove AdminVerification page and restructure verification actions into separate classes for improved organization and maintainability.
This commit is contained in:
parent
938680e2cc
commit
c600ee131b
@ -1,340 +0,0 @@
|
||||
<?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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Pages\AdminVerification\Actions;
|
||||
|
||||
use App\Enums\DecisionAdmin;
|
||||
use App\Filament\Pages\AdminVerification\Actions\Concerns\CanProcessReview;
|
||||
use Filament\Actions\Action;
|
||||
|
||||
class ApproveAction extends Action
|
||||
{
|
||||
use CanProcessReview;
|
||||
|
||||
public static function getDefaultName(): ?string
|
||||
{
|
||||
return 'approve';
|
||||
}
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->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): void {
|
||||
$this->processReview($arguments['id'], DecisionAdmin::APPROVED, null);
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,113 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Pages\AdminVerification\Actions\Concerns;
|
||||
|
||||
use App\Enums\DecisionAdmin;
|
||||
use App\Enums\VerificationStatus;
|
||||
use App\Filament\Pages\Verification;
|
||||
use App\Filament\Support\CheerfulNotification;
|
||||
use App\Models\VerificationRequest;
|
||||
use App\Models\VerificationReview;
|
||||
use App\Notifications\BroadcastNotification;
|
||||
use Filament\Actions\Action;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
trait CanProcessReview
|
||||
{
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Pages\AdminVerification\Actions;
|
||||
|
||||
use App\Enums\DecisionAdmin;
|
||||
use App\Filament\Pages\AdminVerification\Actions\Concerns\CanProcessReview;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Forms\Components\Textarea;
|
||||
|
||||
class RejectAction extends Action
|
||||
{
|
||||
use CanProcessReview;
|
||||
|
||||
public static function getDefaultName(): ?string
|
||||
{
|
||||
return 'reject';
|
||||
}
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->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']);
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Pages\AdminVerification\Actions;
|
||||
|
||||
use App\Enums\DecisionAdmin;
|
||||
use App\Filament\Pages\AdminVerification\Actions\Concerns\CanProcessReview;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Forms\Components\Textarea;
|
||||
use Filament\Support\Enums\Width;
|
||||
|
||||
class RevisionAction extends Action
|
||||
{
|
||||
use CanProcessReview;
|
||||
|
||||
public static function getDefaultName(): ?string
|
||||
{
|
||||
return 'revision';
|
||||
}
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->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);
|
||||
}
|
||||
}
|
||||
239
app/Filament/Pages/AdminVerification/Index.php
Normal file
239
app/Filament/Pages/AdminVerification/Index.php
Normal file
@ -0,0 +1,239 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Pages\AdminVerification;
|
||||
|
||||
use App\Enums\VerificationStatus;
|
||||
use App\Filament\Pages\AdminVerification\Actions\ApproveAction;
|
||||
use App\Filament\Pages\AdminVerification\Actions\RejectAction;
|
||||
use App\Filament\Pages\AdminVerification\Actions\RevisionAction;
|
||||
use App\Filament\Resources\Manage\Partners\Pages\ViewPartner;
|
||||
use App\Models\VerificationRequest;
|
||||
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\Contracts\Pagination\LengthAwarePaginator;
|
||||
use Livewire\Attributes\Computed;
|
||||
use UnitEnum;
|
||||
|
||||
class Index extends Page implements HasActions, HasForms
|
||||
{
|
||||
use HasPageShield, InteractsWithActions, InteractsWithForms;
|
||||
|
||||
public string $filterStatus = '1';
|
||||
|
||||
public int $rowsToDisplay = 12;
|
||||
|
||||
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 canAccess(array $parameters = []): bool
|
||||
{
|
||||
return auth()->user()->can('View:AdminVerification');
|
||||
}
|
||||
|
||||
public static function getNavigationBadge(): ?string
|
||||
{
|
||||
return VerificationRequest::query()->pending()->count();
|
||||
}
|
||||
|
||||
#[Computed]
|
||||
public function verifications(): 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($this->rowsToDisplay)
|
||||
->through(function (VerificationRequest $verification) {
|
||||
$latestReview = $verification->reviews->first();
|
||||
$status = $verification->status;
|
||||
$company = $verification->company;
|
||||
$media = $company?->partnerMedia;
|
||||
$journalists = $media?->journalists ?? collect();
|
||||
|
||||
// Prepare Review classes
|
||||
$reviewColor = $latestReview?->decision?->getColor() ?? 'gray';
|
||||
$reviewClasses = match ($reviewColor) {
|
||||
'success' => 'bg-success-50 dark:bg-success-500/10 border-success-200 dark:border-success-500/20',
|
||||
'warning' => 'bg-warning-50 dark:bg-warning-500/10 border-warning-200 dark:border-warning-500/20',
|
||||
'danger' => 'bg-danger-50 dark:bg-danger-500/10 border-danger-200 dark:border-danger-500/20',
|
||||
'info' => 'bg-info-50 dark:bg-info-500/10 border-info-200 dark:border-info-500/20',
|
||||
default => 'bg-gray-50 dark:bg-gray-500/10 border-gray-200 dark:border-gray-500/20',
|
||||
};
|
||||
|
||||
$reviewTitleClasses = match ($reviewColor) {
|
||||
'success' => 'text-success-700 dark:text-success-400',
|
||||
'warning' => 'text-warning-700 dark:text-warning-400',
|
||||
'danger' => 'text-danger-700 dark:text-danger-400',
|
||||
'info' => 'text-info-700 dark:text-info-400',
|
||||
default => 'text-gray-700 dark:text-gray-400',
|
||||
};
|
||||
|
||||
return [
|
||||
'id' => $verification->id,
|
||||
'company' => [
|
||||
'name' => $company?->name ?? 'Data tidak ditemukan',
|
||||
'detailUrl' => ($company && $company->user_id) ? ViewPartner::getUrl(['record' => $company->user_id]) : null,
|
||||
],
|
||||
'submittedBy' => $verification->submittedBy?->name ?? 'Unknown',
|
||||
|
||||
'status' => [
|
||||
'value' => $status,
|
||||
'label' => $status->getLabel(),
|
||||
'color' => $status->getColor(),
|
||||
'isPending' => $status === VerificationStatus::PENDING,
|
||||
'isRevision' => $status === VerificationStatus::NEED_REVISION,
|
||||
'isApproved' => $status === VerificationStatus::APPROVED,
|
||||
'isRejected' => $status === VerificationStatus::REJECTED,
|
||||
],
|
||||
|
||||
'timestamps' => [
|
||||
'submittedAt' => $verification->created_at->translatedFormat('l, d M Y H:i'),
|
||||
'submittedAtHuman' => $verification->created_at->diffForHumans(),
|
||||
'updatedAt' => $verification->updated_at->translatedFormat('l, d M Y H:i'),
|
||||
'updatedAtHuman' => $verification->updated_at->diffForHumans(),
|
||||
],
|
||||
|
||||
'summary' => [
|
||||
'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,
|
||||
],
|
||||
],
|
||||
|
||||
'reviews' => [
|
||||
'count' => $verification->reviews->count(),
|
||||
'latest' => [
|
||||
'note' => $latestReview?->note,
|
||||
'reviewer' => $latestReview?->reviewer?->name ?? 'Admin',
|
||||
'createdAtHuman' => $latestReview?->created_at?->diffForHumans(),
|
||||
'classes' => $reviewClasses,
|
||||
'titleClasses' => $reviewTitleClasses,
|
||||
],
|
||||
'history' => $verification->reviews->map(fn ($review) => [
|
||||
'decisionLabel' => $review->decision->getLabel(),
|
||||
'decisionColor' => $review->decision->getColor(),
|
||||
'reviewerName' => $review->reviewer?->name ?? 'Admin',
|
||||
'note' => $review->note,
|
||||
'createdAt' => $review->created_at->translatedFormat('l, d M Y H:i'),
|
||||
]),
|
||||
],
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
public function loadMore(): void
|
||||
{
|
||||
$this->rowsToDisplay += 12;
|
||||
}
|
||||
|
||||
public function setFilter(string $status): void
|
||||
{
|
||||
$this->filterStatus = $status;
|
||||
$this->rowsToDisplay = 12;
|
||||
}
|
||||
|
||||
#[Computed]
|
||||
public function getFilters(): array
|
||||
{
|
||||
return [
|
||||
[
|
||||
'id' => 'all',
|
||||
'label' => 'Semua',
|
||||
'icon' => null,
|
||||
'color' => 'primary',
|
||||
'count' => VerificationRequest::count(),
|
||||
],
|
||||
[
|
||||
'id' => (string) VerificationStatus::PENDING->value,
|
||||
'label' => 'Menunggu',
|
||||
'icon' => 'heroicon-s-clock',
|
||||
'color' => 'warning',
|
||||
'count' => VerificationRequest::pending()->count(),
|
||||
],
|
||||
[
|
||||
'id' => (string) VerificationStatus::NEED_REVISION->value,
|
||||
'label' => 'Revisi',
|
||||
'icon' => 'heroicon-s-pencil-square',
|
||||
'color' => 'info',
|
||||
'count' => VerificationRequest::needRevision()->count(),
|
||||
],
|
||||
[
|
||||
'id' => (string) VerificationStatus::APPROVED->value,
|
||||
'label' => 'Disetujui',
|
||||
'icon' => 'heroicon-s-check-circle',
|
||||
'color' => 'success',
|
||||
'count' => VerificationRequest::approved()->count(),
|
||||
],
|
||||
[
|
||||
'id' => (string) VerificationStatus::REJECTED->value,
|
||||
'label' => 'Ditolak',
|
||||
'icon' => 'heroicon-s-x-circle',
|
||||
'color' => 'danger',
|
||||
'count' => VerificationRequest::rejected()->count(),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Approve verification action
|
||||
*/
|
||||
public function approveAction(): Action
|
||||
{
|
||||
return ApproveAction::make();
|
||||
}
|
||||
|
||||
/**
|
||||
* Revision verification action
|
||||
*/
|
||||
public function revisionAction(): Action
|
||||
{
|
||||
return RevisionAction::make();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reject verification action
|
||||
*/
|
||||
public function rejectAction(): Action
|
||||
{
|
||||
return RejectAction::make();
|
||||
}
|
||||
}
|
||||
@ -1,123 +1,43 @@
|
||||
<x-filament-panels::page>
|
||||
@php
|
||||
$counts = $this->getStatusCounts();
|
||||
@endphp
|
||||
|
||||
<div class="mb-6">
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<button wire:click="setFilter('all')" @class([
|
||||
<div class="flex flex-wrap gap-2">
|
||||
@foreach ($this->getFilters as $filter)
|
||||
<button wire:click="setFilter('{{ $filter['id'] }}')" @class([
|
||||
'inline-flex items-center gap-2 px-4 py-2 text-sm font-medium rounded-lg transition-all duration-200',
|
||||
'bg-primary-500 text-white shadow-md shadow-primary-500/20' =>
|
||||
$filterStatus === 'all',
|
||||
"bg-{$filter['color']}-500 text-white shadow-md shadow-{$filter['color']}-500/20" =>
|
||||
$filterStatus === $filter['id'],
|
||||
'bg-gray-100 dark:bg-gray-800 text-gray-700 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-700' =>
|
||||
$filterStatus !== 'all',
|
||||
$filterStatus !== $filter['id'],
|
||||
])>
|
||||
<span>Semua</span>
|
||||
@if ($filter['icon'])
|
||||
<x-dynamic-component :component="$filter['icon']" class="w-4 h-4" />
|
||||
@endif
|
||||
<span>{{ $filter['label'] }}</span>
|
||||
<span @class([
|
||||
'px-2 py-0.5 text-xs rounded-full',
|
||||
'bg-white/20' => $filterStatus === 'all',
|
||||
'bg-gray-200 dark:bg-gray-700' => $filterStatus !== 'all',
|
||||
])>{{ $counts['all'] }}</span>
|
||||
'bg-white/20' => $filterStatus === $filter['id'],
|
||||
"bg-{$filter['color']}-100 text-{$filter['color']}-700 dark:bg-{$filter['color']}-500/20 dark:text-{$filter['color']}-400" =>
|
||||
$filterStatus !== $filter['id'],
|
||||
])>{{ $filter['count'] ?? 0 }}</span>
|
||||
</button>
|
||||
|
||||
<button wire:click="setFilter('{{ \App\Enums\VerificationStatus::PENDING->value }}')"
|
||||
@class([
|
||||
'inline-flex items-center gap-2 px-4 py-2 text-sm font-medium rounded-lg transition-all duration-200',
|
||||
'bg-warning-500 text-white shadow-md shadow-warning-500/20' =>
|
||||
$filterStatus == \App\Enums\VerificationStatus::PENDING->value,
|
||||
'bg-gray-100 dark:bg-gray-800 text-gray-700 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-700' =>
|
||||
$filterStatus != \App\Enums\VerificationStatus::PENDING->value,
|
||||
])>
|
||||
<x-heroicon-s-clock class="w-4 h-4" />
|
||||
<span>Menunggu</span>
|
||||
<span @class([
|
||||
'px-2 py-0.5 text-xs rounded-full',
|
||||
'bg-white/20' =>
|
||||
$filterStatus == \App\Enums\VerificationStatus::PENDING->value,
|
||||
'bg-warning-100 text-warning-700 dark:bg-warning-500/20 dark:text-warning-400' =>
|
||||
$filterStatus != \App\Enums\VerificationStatus::PENDING->value,
|
||||
])>{{ $counts['pending'] }}</span>
|
||||
</button>
|
||||
|
||||
<button wire:click="setFilter('{{ \App\Enums\VerificationStatus::NEED_REVISION->value }}')"
|
||||
@class([
|
||||
'inline-flex items-center gap-2 px-4 py-2 text-sm font-medium rounded-lg transition-all duration-200',
|
||||
'bg-info-500 text-white shadow-md shadow-info-500/20' =>
|
||||
$filterStatus == \App\Enums\VerificationStatus::NEED_REVISION->value,
|
||||
'bg-gray-100 dark:bg-gray-800 text-gray-700 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-700' =>
|
||||
$filterStatus != \App\Enums\VerificationStatus::NEED_REVISION->value,
|
||||
])>
|
||||
<x-heroicon-s-pencil-square class="w-4 h-4" />
|
||||
<span>Revisi</span>
|
||||
<span @class([
|
||||
'px-2 py-0.5 text-xs rounded-full',
|
||||
'bg-white/20' =>
|
||||
$filterStatus == \App\Enums\VerificationStatus::NEED_REVISION->value,
|
||||
'bg-info-100 text-info-700 dark:bg-info-500/20 dark:text-info-400' =>
|
||||
$filterStatus != \App\Enums\VerificationStatus::NEED_REVISION->value,
|
||||
])>{{ $counts['revision'] }}</span>
|
||||
</button>
|
||||
|
||||
<button wire:click="setFilter('{{ \App\Enums\VerificationStatus::APPROVED->value }}')"
|
||||
@class([
|
||||
'inline-flex items-center gap-2 px-4 py-2 text-sm font-medium rounded-lg transition-all duration-200',
|
||||
'bg-success-500 text-white shadow-md shadow-success-500/20' =>
|
||||
$filterStatus == \App\Enums\VerificationStatus::APPROVED->value,
|
||||
'bg-gray-100 dark:bg-gray-800 text-gray-700 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-700' =>
|
||||
$filterStatus != \App\Enums\VerificationStatus::APPROVED->value,
|
||||
])>
|
||||
<x-heroicon-s-check-circle class="w-4 h-4" />
|
||||
<span>Disetujui</span>
|
||||
<span @class([
|
||||
'px-2 py-0.5 text-xs rounded-full',
|
||||
'bg-white/20' =>
|
||||
$filterStatus == \App\Enums\VerificationStatus::APPROVED->value,
|
||||
'bg-success-100 text-success-700 dark:bg-success-500/20 dark:text-success-400' =>
|
||||
$filterStatus != \App\Enums\VerificationStatus::APPROVED->value,
|
||||
])>{{ $counts['approved'] }}</span>
|
||||
</button>
|
||||
|
||||
<button wire:click="setFilter('{{ \App\Enums\VerificationStatus::REJECTED->value }}')"
|
||||
@class([
|
||||
'inline-flex items-center gap-2 px-4 py-2 text-sm font-medium rounded-lg transition-all duration-200',
|
||||
'bg-danger-500 text-white shadow-md shadow-danger-500/20' =>
|
||||
$filterStatus == \App\Enums\VerificationStatus::REJECTED->value,
|
||||
'bg-gray-100 dark:bg-gray-800 text-gray-700 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-700' =>
|
||||
$filterStatus != \App\Enums\VerificationStatus::REJECTED->value,
|
||||
])>
|
||||
<x-heroicon-s-x-circle class="w-4 h-4" />
|
||||
<span>Ditolak</span>
|
||||
<span @class([
|
||||
'px-2 py-0.5 text-xs rounded-full',
|
||||
'bg-white/20' =>
|
||||
$filterStatus == \App\Enums\VerificationStatus::REJECTED->value,
|
||||
'bg-danger-100 text-danger-700 dark:bg-danger-500/20 dark:text-danger-400' =>
|
||||
$filterStatus != \App\Enums\VerificationStatus::REJECTED->value,
|
||||
])>{{ $counts['rejected'] }}</span>
|
||||
</button>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
{{-- Verification Cards Grid --}}
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||
@forelse ($this->getVerifications() as $verification)
|
||||
@php
|
||||
$card = $this->getCardData($verification);
|
||||
@endphp
|
||||
|
||||
@forelse ($this->verifications as $verification)
|
||||
<x-filament::section class="h-full">
|
||||
<x-slot name="heading">
|
||||
<div class="flex items-start justify-between gap-3">
|
||||
<div class="min-w-0 flex-1">
|
||||
<h3 class="font-bold text-lg text-gray-900 dark:text-white truncate"
|
||||
title="{{ $card['companyName'] }}">
|
||||
{{ $card['companyName'] }}
|
||||
title="{{ $verification['company']['name'] }}">
|
||||
{{ $verification['company']['name'] }}
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
{{-- Status Badge --}}
|
||||
<x-filament::badge :color="$card['statusColor']" size="lg">
|
||||
{{ $card['statusLabel'] }}
|
||||
<x-filament::badge :color="$verification['status']['color']" size="lg">
|
||||
{{ $verification['status']['label'] }}
|
||||
</x-filament::badge>
|
||||
</div>
|
||||
</x-slot>
|
||||
@ -135,7 +55,7 @@ class="w-10 h-10 rounded-full bg-primary-100 dark:bg-primary-500/20 flex items-c
|
||||
<p class="text-xs text-gray-500 dark:text-gray-400 uppercase font-semibold">Diajukan oleh
|
||||
</p>
|
||||
<p class="text-sm font-medium text-gray-900 dark:text-white truncate">
|
||||
{{ $card['submittedBy'] }}
|
||||
{{ $verification['submittedBy'] }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@ -150,10 +70,10 @@ class="w-10 h-10 rounded-full bg-primary-100 dark:bg-primary-500/20 flex items-c
|
||||
</p>
|
||||
</div>
|
||||
<p class="text-sm font-medium text-gray-900 dark:text-white truncate">
|
||||
{{ $card['summary']['company']['name'] }}
|
||||
{{ $verification['summary']['company']['name'] }}
|
||||
</p>
|
||||
<x-filament::badge :color="$card['summary']['company']['exists'] ? 'success' : 'warning'" size="xs" class="mt-1">
|
||||
{{ $card['summary']['company']['exists'] ? '✓ Lengkap' : '⚠ Belum diisi' }}
|
||||
<x-filament::badge :color="$verification['summary']['company']['exists'] ? 'success' : 'warning'" size="xs" class="mt-1">
|
||||
{{ $verification['summary']['company']['exists'] ? '✓ Lengkap' : '⚠ Belum diisi' }}
|
||||
</x-filament::badge>
|
||||
</div>
|
||||
|
||||
@ -164,10 +84,10 @@ class="w-10 h-10 rounded-full bg-primary-100 dark:bg-primary-500/20 flex items-c
|
||||
<p class="text-xs font-semibold text-gray-600 dark:text-gray-400 uppercase">Media</p>
|
||||
</div>
|
||||
<p class="text-sm font-medium text-gray-900 dark:text-white truncate">
|
||||
{{ $card['summary']['media']['name'] }}
|
||||
{{ $verification['summary']['media']['name'] }}
|
||||
</p>
|
||||
<x-filament::badge :color="$card['summary']['media']['exists'] ? 'success' : 'warning'" size="xs" class="mt-1">
|
||||
{{ $card['summary']['media']['exists'] ? '✓ Lengkap' : '⚠ Belum diisi' }}
|
||||
<x-filament::badge :color="$verification['summary']['media']['exists'] ? 'success' : 'warning'" size="xs" class="mt-1">
|
||||
{{ $verification['summary']['media']['exists'] ? '✓ Lengkap' : '⚠ Belum diisi' }}
|
||||
</x-filament::badge>
|
||||
</div>
|
||||
|
||||
@ -178,10 +98,10 @@ class="w-10 h-10 rounded-full bg-primary-100 dark:bg-primary-500/20 flex items-c
|
||||
<p class="text-xs font-semibold text-gray-600 dark:text-gray-400 uppercase">Jurnalis</p>
|
||||
</div>
|
||||
<p class="text-sm font-medium text-gray-900 dark:text-white">
|
||||
{{ $card['summary']['journalists']['count'] }} jurnalis
|
||||
{{ $verification['summary']['journalists']['count'] }} jurnalis
|
||||
</p>
|
||||
<x-filament::badge :color="$card['summary']['journalists']['exists'] ? 'success' : 'warning'" size="xs" class="mt-1">
|
||||
{{ $card['summary']['journalists']['exists'] ? '✓ Lengkap' : '⚠ Belum diisi' }}
|
||||
<x-filament::badge :color="$verification['summary']['journalists']['exists'] ? 'success' : 'warning'" size="xs" class="mt-1">
|
||||
{{ $verification['summary']['journalists']['exists'] ? '✓ Lengkap' : '⚠ Belum diisi' }}
|
||||
</x-filament::badge>
|
||||
</div>
|
||||
</div>
|
||||
@ -191,10 +111,10 @@ class="w-10 h-10 rounded-full bg-primary-100 dark:bg-primary-500/20 flex items-c
|
||||
<div>
|
||||
<p class="text-xs text-gray-500 dark:text-gray-400 uppercase font-semibold">Diajukan</p>
|
||||
<p class="text-gray-700 dark:text-gray-300">
|
||||
{{ $verification->created_at->translatedFormat('l, d M Y H:i') }}
|
||||
{{ $verification['timestamps']['submittedAt'] }}
|
||||
</p>
|
||||
<p class="text-xs text-gray-500">
|
||||
{{ $verification->created_at->diffForHumans() }}
|
||||
{{ $verification['timestamps']['submittedAtHuman'] }}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
@ -202,51 +122,33 @@ class="w-10 h-10 rounded-full bg-primary-100 dark:bg-primary-500/20 flex items-c
|
||||
Terakhir
|
||||
</p>
|
||||
<p class="text-gray-700 dark:text-gray-300">
|
||||
{{ $verification->updated_at->translatedFormat('l, d M Y H:i') }}
|
||||
{{ $verification['timestamps']['updatedAt'] }}
|
||||
</p>
|
||||
<p class="text-xs text-gray-500">
|
||||
{{ $verification->updated_at->diffForHumans() }}
|
||||
{{ $verification['timestamps']['updatedAtHuman'] }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Latest Review Note --}}
|
||||
@if ($card['latestReviewNote'])
|
||||
@php
|
||||
$reviewClasses = match ($card['latestReviewDecisionColor']) {
|
||||
'success'
|
||||
=> 'bg-success-50 dark:bg-success-500/10 border-success-200 dark:border-success-500/20',
|
||||
'warning'
|
||||
=> 'bg-warning-50 dark:bg-warning-500/10 border-warning-200 dark:border-warning-500/20',
|
||||
'danger'
|
||||
=> 'bg-danger-50 dark:bg-danger-500/10 border-danger-200 dark:border-danger-500/20',
|
||||
'info' => 'bg-info-50 dark:bg-info-500/10 border-info-200 dark:border-info-500/20',
|
||||
default => 'bg-gray-50 dark:bg-gray-500/10 border-gray-200 dark:border-gray-500/20',
|
||||
};
|
||||
$reviewTitleClasses = match ($card['latestReviewDecisionColor']) {
|
||||
'success' => 'text-success-700 dark:text-success-400',
|
||||
'warning' => 'text-warning-700 dark:text-warning-400',
|
||||
'danger' => 'text-danger-700 dark:text-danger-400',
|
||||
'info' => 'text-info-700 dark:text-info-400',
|
||||
default => 'text-gray-700 dark:text-gray-400',
|
||||
};
|
||||
@endphp
|
||||
<div class="p-3 rounded-lg border {{ $reviewClasses }}">
|
||||
<p class="text-xs font-semibold uppercase mb-1 {{ $reviewTitleClasses }}">
|
||||
@if ($verification['reviews']['latest']['note'])
|
||||
<div class="p-3 rounded-lg border {{ $verification['reviews']['latest']['classes'] }}">
|
||||
<p
|
||||
class="text-xs font-semibold uppercase mb-1 {{ $verification['reviews']['latest']['titleClasses'] }}">
|
||||
Catatan Terakhir
|
||||
</p>
|
||||
<p class="text-sm text-gray-700 dark:text-gray-300 line-clamp-2">
|
||||
{{ $card['latestReviewNote'] }}
|
||||
{{ $verification['reviews']['latest']['note'] }}
|
||||
</p>
|
||||
<p class="text-xs text-gray-500 mt-1">
|
||||
oleh {{ $card['latestReviewReviewer'] }} •
|
||||
{{ $card['latestReviewCreatedAt']->diffForHumans() }}
|
||||
oleh {{ $verification['reviews']['latest']['reviewer'] }} •
|
||||
{{ $verification['reviews']['latest']['createdAtHuman'] }}
|
||||
</p>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- Review History Accordion --}}
|
||||
@if ($card['reviewsCount'] > 0)
|
||||
@if ($verification['reviews']['count'] > 0)
|
||||
<div x-data="{ open: false }"
|
||||
class="border border-gray-200 dark:border-gray-700 rounded-xl overflow-hidden">
|
||||
<button @click="open = !open"
|
||||
@ -254,7 +156,7 @@ class="w-full flex items-center justify-between p-3 bg-gray-50 dark:bg-gray-800
|
||||
<div
|
||||
class="flex items-center gap-2 text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||
<x-heroicon-o-clock class="w-4 h-4" />
|
||||
Riwayat Review ({{ $card['reviewsCount'] }})
|
||||
Riwayat Review ({{ $verification['reviews']['count'] }})
|
||||
</div>
|
||||
<x-heroicon-o-chevron-down
|
||||
class="w-4 h-4 text-gray-500 transition-transform duration-200"
|
||||
@ -263,26 +165,26 @@ class="w-4 h-4 text-gray-500 transition-transform duration-200"
|
||||
|
||||
<div x-show="open" x-collapse class="border-t border-gray-200 dark:border-gray-700">
|
||||
<div class="divide-y divide-gray-100 dark:divide-gray-700 max-h-60 overflow-y-auto">
|
||||
@foreach ($verification->reviews as $review)
|
||||
@foreach ($verification['reviews']['history'] as $review)
|
||||
<div class="p-3">
|
||||
<div class="flex items-start justify-between gap-3">
|
||||
<div class="flex-1 min-w-0">
|
||||
<div class="flex items-center gap-2 mb-1">
|
||||
<x-filament::badge :color="$review->decision->getColor()" size="sm">
|
||||
{{ $review->decision->getLabel() }}
|
||||
<x-filament::badge :color="$review['decisionColor']" size="sm">
|
||||
{{ $review['decisionLabel'] }}
|
||||
</x-filament::badge>
|
||||
<span class="text-xs text-gray-500 dark:text-gray-400">
|
||||
oleh {{ $review->reviewer?->name ?? 'Admin' }}
|
||||
oleh {{ $review['reviewerName'] }}
|
||||
</span>
|
||||
</div>
|
||||
@if ($review->note)
|
||||
@if ($review['note'])
|
||||
<p class="text-sm text-gray-600 dark:text-gray-400 mt-1">
|
||||
{{ $review->note }}
|
||||
{{ $review['note'] }}
|
||||
</p>
|
||||
@endif
|
||||
</div>
|
||||
<div class="shrink-0 text-xs text-gray-400">
|
||||
{{ $review->created_at->translatedFormat('l, d M Y H:i') }}
|
||||
{{ $review['createdAt'] }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -296,31 +198,31 @@ class="w-4 h-4 text-gray-500 transition-transform duration-200"
|
||||
<x-slot name="footer">
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
{{-- View Detail Button --}}
|
||||
@if ($card['detailUrl'])
|
||||
@if ($verification['company']['detailUrl'])
|
||||
<x-filament::button color="gray" size="sm" icon="heroicon-m-eye" tag="a"
|
||||
:href="$card['detailUrl']">
|
||||
:href="$verification['company']['detailUrl']">
|
||||
Detail
|
||||
</x-filament::button>
|
||||
@endif
|
||||
|
||||
{{-- Action Buttons based on status --}}
|
||||
@if ($card['isPending'])
|
||||
{{ ($this->approveAction)(['id' => $verification->id]) }}
|
||||
{{ ($this->revisionAction)(['id' => $verification->id]) }}
|
||||
{{ ($this->rejectAction)(['id' => $verification->id]) }}
|
||||
@elseif($card['isRevision'])
|
||||
@if ($verification['status']['isPending'])
|
||||
{{ ($this->approveAction)(['id' => $verification['id']]) }}
|
||||
{{ ($this->revisionAction)(['id' => $verification['id']]) }}
|
||||
{{ ($this->rejectAction)(['id' => $verification['id']]) }}
|
||||
@elseif($verification['status']['isRevision'])
|
||||
<span
|
||||
class="flex items-center gap-1 text-xs text-warning-600 dark:text-warning-400 font-medium italic">
|
||||
<x-heroicon-s-clock class="w-3 h-3 animate-pulse" />
|
||||
Menunggu revisi dari pengguna...
|
||||
</span>
|
||||
@elseif($card['isApproved'])
|
||||
@elseif($verification['status']['isApproved'])
|
||||
<span
|
||||
class="flex items-center gap-1 text-xs text-success-600 dark:text-success-400 font-medium">
|
||||
<x-heroicon-s-check-circle class="w-3 h-3" />
|
||||
Verifikasi selesai
|
||||
</span>
|
||||
@elseif($card['isRejected'])
|
||||
@elseif($verification['status']['isRejected'])
|
||||
<span
|
||||
class="flex items-center gap-1 text-xs text-danger-600 dark:text-danger-400 font-medium">
|
||||
<x-heroicon-s-x-circle class="w-3 h-3" />
|
||||
@ -339,10 +241,10 @@ class="flex items-center gap-1 text-xs text-danger-600 dark:text-danger-400 font
|
||||
@endforelse
|
||||
</div>
|
||||
|
||||
{{-- Pagination --}}
|
||||
@if ($this->getVerifications()->hasPages())
|
||||
<div class="mt-6">
|
||||
{{ $this->getVerifications()->links() }}
|
||||
{{-- Infinite Scroll Sentinel --}}
|
||||
@if ($this->verifications->hasMorePages())
|
||||
<div x-intersect="$wire.loadMore()" class="py-6 flex justify-center">
|
||||
<x-filament::loading-indicator class="w-8 h-8 text-primary-500" />
|
||||
</div>
|
||||
@endif
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user