diff --git a/app/Filament/Pages/AdminVerification/Actions/ApproveAction.php b/app/Filament/Pages/AdminVerification/Actions/ApproveAction.php index 88d653c..aae1ce4 100644 --- a/app/Filament/Pages/AdminVerification/Actions/ApproveAction.php +++ b/app/Filament/Pages/AdminVerification/Actions/ApproveAction.php @@ -3,13 +3,13 @@ namespace App\Filament\Pages\AdminVerification\Actions; use App\Enums\DecisionAdmin; -use App\Filament\Pages\AdminVerification\Actions\Concerns\CanProcessReview; +use App\Filament\Pages\AdminVerification\Concerns\CanProcessVerificationReview; use App\Filament\Support\CheerfulNotification; use Filament\Actions\Action; class ApproveAction extends Action { - use CanProcessReview; + use CanProcessVerificationReview; public static function getDefaultName(): ?string { @@ -21,9 +21,11 @@ protected function setUp(): void parent::setUp(); $this->label('Setujui') + ->authorize(fn (): bool => auth()->user()?->can('Approve:AdminVerification')) ->color('success') ->icon('heroicon-o-check') ->size('sm') + ->link() ->requiresConfirmation() ->modalHeading(fn () => CheerfulNotification::getByKey('actions.approve.title')) ->modalDescription(fn () => CheerfulNotification::getByKey('actions.approve.description')) diff --git a/app/Filament/Pages/AdminVerification/Actions/RejectAction.php b/app/Filament/Pages/AdminVerification/Actions/RejectAction.php index 1ad3bb3..598d309 100644 --- a/app/Filament/Pages/AdminVerification/Actions/RejectAction.php +++ b/app/Filament/Pages/AdminVerification/Actions/RejectAction.php @@ -3,14 +3,14 @@ namespace App\Filament\Pages\AdminVerification\Actions; use App\Enums\DecisionAdmin; -use App\Filament\Pages\AdminVerification\Actions\Concerns\CanProcessReview; +use App\Filament\Pages\AdminVerification\Concerns\CanProcessVerificationReview; use App\Filament\Support\CheerfulNotification; use Filament\Actions\Action; use Filament\Forms\Components\Textarea; class RejectAction extends Action { - use CanProcessReview; + use CanProcessVerificationReview; public static function getDefaultName(): ?string { @@ -22,9 +22,11 @@ protected function setUp(): void parent::setUp(); $this->label('Tolak') + ->authorize(fn (): bool => auth()->user()?->can('Reject:AdminVerification')) ->color('danger') ->icon('heroicon-o-x-mark') ->size('sm') + ->link() ->requiresConfirmation() ->modalHeading(fn () => CheerfulNotification::getByKey('actions.reject.title')) ->modalDescription(fn () => CheerfulNotification::getByKey('actions.reject.description')) diff --git a/app/Filament/Pages/AdminVerification/Actions/RevisionAction.php b/app/Filament/Pages/AdminVerification/Actions/RevisionAction.php index 8c6c3ea..e212d4f 100644 --- a/app/Filament/Pages/AdminVerification/Actions/RevisionAction.php +++ b/app/Filament/Pages/AdminVerification/Actions/RevisionAction.php @@ -3,7 +3,7 @@ namespace App\Filament\Pages\AdminVerification\Actions; use App\Enums\DecisionAdmin; -use App\Filament\Pages\AdminVerification\Actions\Concerns\CanProcessReview; +use App\Filament\Pages\AdminVerification\Concerns\CanProcessVerificationReview; use App\Filament\Support\CheerfulNotification; use Filament\Actions\Action; use Filament\Forms\Components\Textarea; @@ -11,7 +11,7 @@ class RevisionAction extends Action { - use CanProcessReview; + use CanProcessVerificationReview; public static function getDefaultName(): ?string { @@ -23,9 +23,11 @@ protected function setUp(): void parent::setUp(); $this->label('Minta Revisi') + ->authorize(fn (): bool => auth()->user()?->can('Revision:AdminVerification')) ->color('warning') ->icon('heroicon-o-pencil-square') ->size('sm') + ->link() ->modalHeading(fn () => CheerfulNotification::getByKey('actions.revision.title')) ->modalDescription(fn () => CheerfulNotification::getByKey('actions.revision.description')) ->schema([ diff --git a/app/Filament/Pages/AdminVerification/Actions/ViewAction.php b/app/Filament/Pages/AdminVerification/Actions/ViewAction.php new file mode 100644 index 0000000..2c2a119 --- /dev/null +++ b/app/Filament/Pages/AdminVerification/Actions/ViewAction.php @@ -0,0 +1,29 @@ +label('Lihat') + ->color('success') + ->icon('heroicon-o-eye') + ->size('sm') + ->link() + ->url(fn (array $arguments) => ViewPartner::getUrl(['record' => $arguments['id']])); + } +} diff --git a/app/Filament/Pages/AdminVerification/Actions/Concerns/CanProcessReview.php b/app/Filament/Pages/AdminVerification/Concerns/CanProcessVerificationReview.php similarity index 71% rename from app/Filament/Pages/AdminVerification/Actions/Concerns/CanProcessReview.php rename to app/Filament/Pages/AdminVerification/Concerns/CanProcessVerificationReview.php index 73eac09..414c30f 100644 --- a/app/Filament/Pages/AdminVerification/Actions/Concerns/CanProcessReview.php +++ b/app/Filament/Pages/AdminVerification/Concerns/CanProcessVerificationReview.php @@ -1,6 +1,6 @@ find($requestId); - - if (! $verificationRequest) { - CheerfulNotification::danger( - CheerfulNotification::getByKey('verification.admin.process.not_found.title'), - CheerfulNotification::getByKey('verification.admin.process.not_found.body') - ) - ->send(); - - return; - } + $verificationRequest = VerificationRequest::with(['company.partnerMedia.journalists'])->findOrFail($requestId); try { - DB::beginTransaction(); + DB::transaction(function () use ($verificationRequest, $decision, $note) { + // Create review record + VerificationReview::create([ + 'verification_request_id' => $verificationRequest->id, + 'reviewer_id' => auth()->id(), + 'decision' => $decision, + 'note' => $note, + ]); - // 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, + }; - // 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(); + $verificationRequest->update([ + 'status' => $newStatus, + ]); + }); $actionLabel = match ($decision) { DecisionAdmin::APPROVED => 'disetujui', @@ -95,7 +83,6 @@ protected function processReview(int $requestId, DecisionAdmin $decision, ?strin ])); } } catch (\Throwable $e) { - DB::rollBack(); Log::error('Verification processing failed', [ 'error' => $e->getMessage(), 'trace' => $e->getTraceAsString(), diff --git a/app/Filament/Pages/AdminVerification/Concerns/HasVerificationActions.php b/app/Filament/Pages/AdminVerification/Concerns/HasVerificationActions.php new file mode 100644 index 0000000..49c155f --- /dev/null +++ b/app/Filament/Pages/AdminVerification/Concerns/HasVerificationActions.php @@ -0,0 +1,32 @@ +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' => 'heroicon-s-squares-2x2', + 'color' => 'primary', + 'count' => VerificationRequest::count(), + ], + [ + 'id' => (string) VerificationStatus::PENDING->value, + 'label' => 'Menunggu', + 'icon' => 'heroicon-s-clock', + 'color' => 'warning', + 'count' => VerificationRequest::query()->pending()->count(), + ], + [ + 'id' => (string) VerificationStatus::NEED_REVISION->value, + 'label' => 'Revisi', + 'icon' => 'heroicon-s-pencil-square', + 'color' => 'info', + 'count' => VerificationRequest::query()->needRevision()->count(), + ], + [ + 'id' => (string) VerificationStatus::APPROVED->value, + 'label' => 'Disetujui', + 'icon' => 'heroicon-s-check-circle', + 'color' => 'success', + 'count' => VerificationRequest::query()->approved()->count(), + ], + [ + 'id' => (string) VerificationStatus::REJECTED->value, + 'label' => 'Ditolak', + 'icon' => 'heroicon-s-x-circle', + 'color' => 'danger', + 'count' => VerificationRequest::query()->rejected()->count(), + ], + ]; + } +} diff --git a/app/Filament/Pages/AdminVerification/Concerns/HasVerificationNotifications.php b/app/Filament/Pages/AdminVerification/Concerns/HasVerificationNotifications.php new file mode 100644 index 0000000..4886cf2 --- /dev/null +++ b/app/Filament/Pages/AdminVerification/Concerns/HasVerificationNotifications.php @@ -0,0 +1,18 @@ +user()->can('View:AdminVerification'); } @@ -157,6 +152,7 @@ public function verifications(): LengthAwarePaginator 'createdAt' => $review->created_at->translatedFormat('l, d M Y H:i'), ]), ], + 'messages' => [ 'detail' => CheerfulNotification::getByKey('verification.admin.messages.detail'), 'revision' => CheerfulNotification::getByKey('verification.admin.messages.revision'), @@ -166,91 +162,4 @@ public function verifications(): LengthAwarePaginator ]; }); } - - 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' => 'heroicon-s-squares-2x2', - '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(); - } - - public function getEmptyStateHeading(): string - { - return CheerfulNotification::getByKey('verification.admin.empty_state.heading'); - } - - public function getEmptyStateDescription(): string - { - return CheerfulNotification::getByKey('verification.admin.empty_state.description'); - } } diff --git a/database/seeders/ShieldSeeder.php b/database/seeders/ShieldSeeder.php index 0a74afa..91b17bc 100644 --- a/database/seeders/ShieldSeeder.php +++ b/database/seeders/ShieldSeeder.php @@ -21,6 +21,9 @@ public function run(): void "View:Analytic", "View:AdminVerification", + "Approve:AdminVerification", + "Reject:AdminVerification", + "Revision:AdminVerification", "ViewAny:Announcement", "View:Announcement", @@ -281,6 +284,9 @@ public function run(): void "View:Analytic", "View:AdminVerification", + "Approve:AdminVerification", + "Reject:AdminVerification", + "Revision:AdminVerification", "ViewAny:Announcement", "View:Announcement", diff --git a/lang/id/notif.php b/lang/id/notif.php index 6a0c5c1..20367f2 100644 --- a/lang/id/notif.php +++ b/lang/id/notif.php @@ -265,16 +265,6 @@ ], 'admin' => [ 'process' => [ - 'not_found' => [ - 'title' => [ - 'cheerful' => 'Belum ketemu nih 🙌', - 'formal' => 'Data Tidak Ditemukan', - ], - 'body' => [ - 'cheerful' => 'Data verifikasi belum kami temukan. Coba sebentar lagi, ya.', - 'formal' => 'Permintaan verifikasi tidak ditemukan dalam sistem kami.', - ], - ], 'success' => [ 'title' => [ 'cheerful' => 'Berhasil Diproses! ✨', diff --git a/resources/views/filament/pages/admin-verification.blade.php b/resources/views/filament/pages/admin-verification.blade.php index aef6ff1..75ecc09 100644 --- a/resources/views/filament/pages/admin-verification.blade.php +++ b/resources/views/filament/pages/admin-verification.blade.php @@ -1,24 +1,31 @@
@foreach ($this->getFilters as $filter) - + @endforeach
- {{-- Verification Cards Grid --}} -
- @forelse ($this->verifications as $verification) - - -
-
-

- {{ $verification['company']['name'] }} -

-
- - {{-- Status Badge --}} - - {{ $verification['status']['label'] }} - -
-
- -
- {{-- Submitter Info --}} -
-
-
- + + {{-- Verification Cards Grid --}} +
+ @forelse ($this->verifications as $verification) + + +
+
+

+ {{ $verification['company']['name'] }} +

-
-
-

Diajukan oleh -

-

- {{ $verification['submittedBy'] }} -

-
-
- {{-- Data Summary --}} -
- {{-- Company Summary --}} -
-
- -

Perusahaan + {{-- Status Badge --}} + + {{ $verification['status']['label'] }} + +

+ + +
+ {{-- Submitter Info --}} +
+
+
+ +
+
+
+

Diajukan + oleh +

+

+ {{ $verification['submittedBy'] }}

-

- {{ $verification['summary']['company']['name'] }} -

- - {{ $verification['summary']['company']['exists'] ? '✓ Lengkap' : '⚠ Belum diisi' }} -
- {{-- Media Summary --}} -
-
- -

Media

-
-

- {{ $verification['summary']['media']['name'] }} -

- - {{ $verification['summary']['media']['exists'] ? '✓ Lengkap' : '⚠ Belum diisi' }} - -
- - {{-- Journalists Summary --}} -
-
- -

Jurnalis

-
-

- {{ $verification['summary']['journalists']['count'] }} jurnalis -

- - {{ $verification['summary']['journalists']['exists'] ? '✓ Lengkap' : '⚠ Belum diisi' }} - -
-
- - {{-- Timeline Info --}} -
-
-

Diajukan

-

- {{ $verification['timestamps']['submittedAt'] }} -

-

- {{ $verification['timestamps']['submittedAtHuman'] }} -

-
-
-

Pembaruan - Terakhir -

-

- {{ $verification['timestamps']['updatedAt'] }} -

-

- {{ $verification['timestamps']['updatedAtHuman'] }} -

-
-
- - {{-- Latest Review Note --}} - @if ($verification['reviews']['latest']['note']) -
-

- Catatan Terakhir -

-

- {{ $verification['reviews']['latest']['note'] }} -

-

- oleh {{ $verification['reviews']['latest']['reviewer'] }} • - {{ $verification['reviews']['latest']['createdAtHuman'] }} -

-
- @endif - - {{-- Review History Accordion --}} - @if ($verification['reviews']['count'] > 0) -
- +

+ {{ $verification['summary']['company']['name'] }} +

+ + {{ $verification['summary']['company']['exists'] ? '✓ Lengkap' : '⚠ Belum diisi' }} + +
-
-
- @foreach ($verification['reviews']['history'] as $review) -
-
-
-
- - {{ $review['decisionLabel'] }} - - - oleh {{ $review['reviewerName'] }} - + {{-- Media Summary --}} +
+
+ +

Media +

+
+

+ {{ $verification['summary']['media']['name'] }} +

+ + {{ $verification['summary']['media']['exists'] ? '✓ Lengkap' : '⚠ Belum diisi' }} + +
+ + {{-- Journalists Summary --}} +
+
+ +

Jurnalis +

+
+

+ {{ $verification['summary']['journalists']['count'] }} jurnalis +

+ + {{ $verification['summary']['journalists']['exists'] ? '✓ Lengkap' : '⚠ Belum diisi' }} + +
+
+ + {{-- Timeline Info --}} +
+
+

Diajukan

+

+ {{ $verification['timestamps']['submittedAt'] }} +

+

+ {{ $verification['timestamps']['submittedAtHuman'] }} +

+
+
+

Pembaruan + Terakhir +

+

+ {{ $verification['timestamps']['updatedAt'] }} +

+

+ {{ $verification['timestamps']['updatedAtHuman'] }} +

+
+
+ + {{-- Latest Review Note --}} + @if ($verification['reviews']['latest']['note']) +
+

+ Catatan Terakhir +

+

+ {{ $verification['reviews']['latest']['note'] }} +

+

+ oleh {{ $verification['reviews']['latest']['reviewer'] }} • + {{ $verification['reviews']['latest']['createdAtHuman'] }} +

+
+ @endif + + {{-- Review History Accordion --}} + @if ($verification['reviews']['count'] > 0) +
+ + +
+
+ @foreach ($verification['reviews']['history'] as $review) +
+
+
+
+ + {{ $review['decisionLabel'] }} + + + oleh {{ $review['reviewerName'] }} + +
+ @if ($review['note']) +

+ {{ $review['note'] }} +

+ @endif +
+
+ {{ $review['createdAt'] }}
- @if ($review['note']) -

- {{ $review['note'] }} -

- @endif -
-
- {{ $review['createdAt'] }}
-
- @endforeach + @endforeach +
-
- @endif -
- - -
- {{-- View Detail Button --}} - @if ($verification['company']['detailUrl']) - - {{ $verification['messages']['detail'] }} - - @endif - - {{-- Action Buttons based on status --}} - @if ($verification['status']['isPending']) - {{ ($this->approveAction)(['id' => $verification['id']]) }} - {{ ($this->revisionAction)(['id' => $verification['id']]) }} - {{ ($this->rejectAction)(['id' => $verification['id']]) }} - @elseif($verification['status']['isRevision']) - - - {{ $verification['messages']['revision'] }} - - @elseif($verification['status']['isApproved']) - - - {{ $verification['messages']['approved'] }} - - @elseif($verification['status']['isRejected']) - - - {{ $verification['messages']['rejected'] }} - @endif
-
- - @empty -
- - -
- @endforelse -
- {{-- Infinite Scroll Sentinel --}} - @if ($this->verifications->hasMorePages()) -
- + +
+ {{-- View Detail Button --}} + @can('View:AdminVerification') + @if ($verification['company']['detailUrl']) + {{ ($this->viewAction)(['id' => $verification['id']]) }} + @endif + @endcan + + {{-- Action Buttons based on status --}} + @if ($verification['status']['isPending']) + @canAny(['Approve:AdminVerification', 'Revision:AdminVerification', + 'Reject:AdminVerification']) + @can('Approve:AdminVerification') + {{ ($this->approveAction)(['id' => $verification['id']]) }} + @endcan + + @can('Revision:AdminVerification') + {{ ($this->revisionAction)(['id' => $verification['id']]) }} + @endcan + + @can('Reject:AdminVerification') + {{ ($this->rejectAction)(['id' => $verification['id']]) }} + @endcan + @endcanAny + @elseif($verification['status']['isRevision']) + + + {{ $verification['messages']['revision'] }} + + @elseif($verification['status']['isApproved']) + + + {{ $verification['messages']['approved'] }} + + @elseif($verification['status']['isRejected']) + + + {{ $verification['messages']['rejected'] }} + + @endif +
+
+ + @empty +
+ + +
+ @endforelse
- @endif - + {{-- Infinite Scroll Sentinel --}} + @if ($this->verifications->hasMorePages()) +
+ +
+ @endif + + +