diff --git a/app/Filament/Pages/AdminVerification.php b/app/Filament/Pages/AdminVerification.php index c8007e5..4389a37 100644 --- a/app/Filament/Pages/AdminVerification.php +++ b/app/Filament/Pages/AdminVerification.php @@ -4,10 +4,9 @@ use App\Enums\DecisionAdmin; use App\Enums\VerificationStatus; -use App\Filament\Resources\Manage\Journalists\Pages\ManageJournalists; use App\Filament\Resources\Manage\Partners\Pages\ViewPartner; use App\Models\VerificationRequest; -use App\Models\VerificationReview; +use App\Services\VerificationService; use BackedEnum; use BezhanSalleh\FilamentShield\Traits\HasPageShield; use Filament\Actions\Action; @@ -19,7 +18,6 @@ use Filament\Notifications\Notification; use Filament\Pages\Page; use Illuminate\Contracts\Pagination\LengthAwarePaginator; -use Illuminate\Support\Facades\Auth; use UnitEnum; class AdminVerification extends Page implements HasActions, HasForms @@ -40,6 +38,13 @@ class AdminVerification extends Page implements HasActions, HasForms 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(); @@ -48,7 +53,9 @@ public static function getNavigationBadge(): ?string public function getVerifications(): LengthAwarePaginator { $query = VerificationRequest::with([ - 'verifiable', + 'company.user', + 'company.partnerMedia', + 'company.partnerMedia.journalists', 'submittedBy', 'reviews.reviewer', ]) @@ -79,52 +86,64 @@ public function getStatusCounts(): array } /** - * Get the entity type label + * Get company name from verification request */ - public function getEntityTypeLabel(VerificationRequest $request): string + public function getCompanyName(VerificationRequest $request): string { - $type = class_basename($request->verifiable_type); - - return match ($type) { - 'Company' => 'Perusahaan', - 'PartnerMedia' => 'Media', - 'Journalist' => 'Jurnalis', - default => $type, - }; + return $this->verificationService->getCompanyName($request); } /** - * Get the entity name - */ - public function getEntityName(VerificationRequest $request): string - { - $entity = $request->verifiable; - - if (! $entity) { - return 'Data tidak ditemukan'; - } - - return $entity->name ?? 'Tanpa Nama'; - } - - /** - * Get detail URL based on entity type + * Get detail URL for the company */ public function getDetailUrl(VerificationRequest $request): ?string { - $entity = $request->verifiable; - $type = class_basename($request->verifiable_type); - - if (! $entity) { + if (! $request->company || ! $request->company->user_id) { return null; } - return match ($type) { - 'Company' => ViewPartner::getUrl(['record' => $entity->user_id]), - 'PartnerMedia' => ViewPartner::getUrl(['record' => $entity->company?->user_id]), - 'Journalist' => ManageJournalists::getUrl(), - default => 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, + ]; } /** @@ -138,14 +157,9 @@ public function approveAction(): Action ->icon('heroicon-o-check') ->size('sm') ->requiresConfirmation() - ->modalHeading('Setujui Verifikasi') - ->modalDescription('Apakah Anda yakin ingin menyetujui verifikasi ini?') + ->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') - ->schema([ - Textarea::make('note') - ->label('Catatan (Opsional)') - ->placeholder('Tambahkan catatan jika diperlukan...'), - ]) ->action(function (array $arguments, array $data) { $this->processReview($arguments['id'], DecisionAdmin::APPROVED, $data['note'] ?? null); }); @@ -163,11 +177,11 @@ public function revisionAction(): Action ->size('sm') ->modalHeading('Minta Revisi') ->modalDescription('User akan diminta untuk memperbaiki data sesuai catatan yang diberikan.') - ->form([ + ->schema([ Textarea::make('note') ->label('Catatan Revisi') ->required() - ->placeholder('Jelaskan bagian yang perlu diperbaiki...') + ->placeholder('Jelaskan bagian yang perlu diperbaiki (Perusahaan, Media, atau Jurnalis)...') ->helperText('Catatan ini akan ditampilkan kepada user.'), ]) ->action(function (array $arguments, array $data) { @@ -186,10 +200,10 @@ public function rejectAction(): Action ->icon('heroicon-o-x-mark') ->size('sm') ->requiresConfirmation() - ->modalHeading('Tolak Verifikasi') - ->modalDescription('Pengajuan akan ditolak secara permanen. User tidak dapat mengajukan ulang untuk data ini.') + ->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') - ->form([ + ->schema([ Textarea::make('note') ->label('Alasan Penolakan') ->required() @@ -206,7 +220,7 @@ public function rejectAction(): Action */ protected function processReview(int $requestId, DecisionAdmin $decision, ?string $note): void { - $verificationRequest = VerificationRequest::find($requestId); + $verificationRequest = VerificationRequest::with(['company.partnerMedia.journalists'])->find($requestId); if (! $verificationRequest) { Notification::make() @@ -218,47 +232,32 @@ protected function processReview(int $requestId, DecisionAdmin $decision, ?strin return; } - // Create review record - VerificationReview::create([ - 'verification_request_id' => $verificationRequest->id, - 'reviewer_id' => Auth::id(), - 'decision' => $decision, - 'note' => $note, - ]); + try { + $this->verificationService->processReview($verificationRequest, $decision, $note); - // Update verification request status - $newStatus = match ($decision) { - DecisionAdmin::APPROVED => VerificationStatus::APPROVED, - DecisionAdmin::NEED_REVISION => VerificationStatus::NEED_REVISION, - DecisionAdmin::REJECTED => VerificationStatus::REJECTED, - }; + $actionLabel = match ($decision) { + DecisionAdmin::APPROVED => 'disetujui', + DecisionAdmin::NEED_REVISION => 'diminta revisi', + DecisionAdmin::REJECTED => 'ditolak', + }; - $verificationRequest->update([ - 'status' => $newStatus, - ]); + $color = match ($decision) { + DecisionAdmin::APPROVED => 'success', + DecisionAdmin::NEED_REVISION => 'info', + DecisionAdmin::REJECTED => 'danger', + }; - // Update the related entity status if needed - $entity = $verificationRequest->verifiable; - if ($entity && method_exists($entity, 'update')) { - $entity->update(['status' => $newStatus]); + 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(); } - - $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 telah {$actionLabel}.") - ->color($color) - ->send(); } } diff --git a/app/Filament/Pages/CustomCompany.php b/app/Filament/Pages/CustomCompany.php index 17ac7d9..a2e23d1 100644 --- a/app/Filament/Pages/CustomCompany.php +++ b/app/Filament/Pages/CustomCompany.php @@ -3,6 +3,7 @@ namespace App\Filament\Pages; use App\Models\Company; +use App\Models\VerificationRequest; use Asmit\FilamentUpload\Forms\Components\AdvancedFileUpload; use BackedEnum; use BezhanSalleh\FilamentShield\Traits\HasPageShield; @@ -37,6 +38,8 @@ class CustomCompany extends Page public ?Company $company = null; + public ?VerificationRequest $verificationRequest = null; + public array $data = []; public function mount(): void @@ -47,6 +50,10 @@ public function mount(): void return; } + $this->verificationRequest = $company->verificationRequest() + ->latest() + ->first(); + $this->company = $company; $this->form->fill($company->toArray()); diff --git a/app/Filament/Pages/CustomMedia.php b/app/Filament/Pages/CustomMedia.php index ada7b57..b746b5a 100644 --- a/app/Filament/Pages/CustomMedia.php +++ b/app/Filament/Pages/CustomMedia.php @@ -6,6 +6,7 @@ use App\Enums\MediaType; use App\Models\Company; use App\Models\PartnerMedia; +use App\Models\VerificationRequest; use Asmit\FilamentUpload\Forms\Components\AdvancedFileUpload; use BackedEnum; use BezhanSalleh\FilamentShield\Traits\HasPageShield; @@ -45,6 +46,8 @@ class CustomMedia extends Page public ?PartnerMedia $media = null; + public ?VerificationRequest $verificationRequest = null; + public bool $restricted = false; public array $data = []; @@ -65,6 +68,10 @@ public function mount(): void return; } + $this->verificationRequest = $this->company->verificationRequest() + ->latest() + ->first(); + $this->media = $media; $this->form->fill($media->toArray()); diff --git a/app/Filament/Pages/CustomVerification.php b/app/Filament/Pages/CustomVerification.php index 88d84d4..dc1d0f7 100644 --- a/app/Filament/Pages/CustomVerification.php +++ b/app/Filament/Pages/CustomVerification.php @@ -2,13 +2,10 @@ namespace App\Filament\Pages; -use App\Enums\DecisionAdmin; use App\Enums\VerificationStatus; -use App\Filament\Resources\Manage\Journalists\Pages\ManageJournalists; use App\Models\Company; -use App\Models\Journalist; -use App\Models\PartnerMedia; use App\Models\VerificationRequest; +use App\Services\VerificationService; use BackedEnum; use BezhanSalleh\FilamentShield\Traits\HasPageShield; use Filament\Actions\Action; @@ -18,7 +15,6 @@ use Filament\Forms\Contracts\HasForms; use Filament\Notifications\Notification; use Filament\Pages\Page; -use Illuminate\Support\Collection; use UnitEnum; class CustomVerification extends Page implements HasActions, HasForms @@ -39,39 +35,22 @@ class CustomVerification extends Page implements HasActions, HasForms public ?Company $company = null; - public ?PartnerMedia $partnerMedia = null; + public ?VerificationRequest $verificationRequest = null; - public ?Journalist $journalist = null; + protected VerificationService $verificationService; - public ?VerificationRequest $companyVerification = null; - - public ?VerificationRequest $mediaVerification = null; - - public ?VerificationRequest $journalistVerification = null; + public function boot(): void + { + $this->verificationService = app(VerificationService::class); + } public function mount(): void { $this->company = auth()->user()->company; - $this->partnerMedia = auth()->user()->company?->partnerMedia; - $this->journalist = auth()->user()->company?->partnerMedia?->journalists?->first(); - // Load verification requests with reviews + // Load unified verification request if ($this->company) { - $this->companyVerification = $this->company->verificationRequest() - ->with(['reviews.reviewer']) - ->latest() - ->first(); - } - - if ($this->partnerMedia) { - $this->mediaVerification = $this->partnerMedia->verificationRequest() - ->with(['reviews.reviewer']) - ->latest() - ->first(); - } - - if ($this->journalist) { - $this->journalistVerification = $this->journalist->verificationRequest() + $this->verificationRequest = $this->company->verificationRequest() ->with(['reviews.reviewer']) ->latest() ->first(); @@ -79,345 +58,79 @@ public function mount(): void } /** - * Determine the current step based on status + * Get unified verification progress */ - protected function resolveStep(?VerificationStatus $status, bool $hasData = false): int + public function getVerificationProgress(): array { - if (! $hasData) { - return 1; // No data yet - } + $progress = $this->verificationService->getVerificationProgress( + $this->company, + $this->verificationRequest + ); - return match ($status) { - VerificationStatus::PENDING, - VerificationStatus::NEED_REVISION => 2, // In review or needs revision + // Add additional data for view + $progress['missingDataButtons'] = $this->verificationService->getMissingDataButtons($this->company); + $progress['alert'] = $this->verificationService->getAlertData($this->verificationRequest); + $progress['lastSubmittedAt'] = $this->verificationRequest?->updated_at?->format('d M Y, H:i'); - VerificationStatus::APPROVED, - VerificationStatus::REJECTED => 3, // Final decision - - default => 1, // Draft / not submitted yet - }; + return $progress; } /** - * Check if user can submit verification for entity + * Submit unified verification action */ - protected function canSubmit($entity, ?VerificationRequest $verification): bool + public function submitVerificationAction(): Action { - if (! $entity) { - return false; - } + $progress = $this->getVerificationProgress(); - // No verification yet - can submit - if (! $verification) { - return true; - } - - // Can resubmit only if status is NEED_REVISION - return $verification->status === VerificationStatus::NEED_REVISION; - } - - /** - * Check if entity is approved - */ - protected function isApproved(?VerificationRequest $verification): bool - { - return $verification?->status === VerificationStatus::APPROVED; - } - - /** - * Check if entity is rejected - */ - protected function isRejected(?VerificationRequest $verification): bool - { - return $verification?->status === VerificationStatus::REJECTED; - } - - /** - * Check if entity is pending review - */ - protected function isPending(?VerificationRequest $verification): bool - { - return $verification?->status === VerificationStatus::PENDING; - } - - /** - * Check if entity needs revision - */ - protected function needsRevision(?VerificationRequest $verification): bool - { - return $verification?->status === VerificationStatus::NEED_REVISION; - } - - /** - * Get the latest review notes for an entity - */ - protected function getLatestReviewNotes(?VerificationRequest $verification): ?string - { - if (! $verification) { - return null; - } - - $latestReview = $verification->reviews() - ->whereIn('decision', [DecisionAdmin::NEED_REVISION, DecisionAdmin::REJECTED]) - ->latest() - ->first(); - - return $latestReview?->note; - } - - /** - * Get all reviews history for an entity - */ - protected function getReviewHistory(?VerificationRequest $verification): Collection - { - if (! $verification) { - return collect(); - } - - return $verification->reviews() - ->with('reviewer') - ->latest() - ->get(); - } - - public function companyProgress(): array - { - $hasData = $this->company !== null; - $currentStep = $this->resolveStep($this->companyVerification?->status, $hasData); - - return [ - 'title' => 'Perusahaan', - 'description' => 'Progress verifikasi perusahaan', - 'icon' => 'heroicon-o-building-office-2', - 'currentStep' => $currentStep, - 'entity' => $this->company, - 'verification' => $this->companyVerification, - 'canSubmit' => $this->canSubmit($this->company, $this->companyVerification), - 'isApproved' => $this->isApproved($this->companyVerification), - 'isRejected' => $this->isRejected($this->companyVerification), - 'isPending' => $this->isPending($this->companyVerification), - 'needsRevision' => $this->needsRevision($this->companyVerification), - 'latestNotes' => $this->getLatestReviewNotes($this->companyVerification), - 'reviewHistory' => $this->getReviewHistory($this->companyVerification), - 'editUrl' => CustomCompany::getUrl(), - 'steps' => [ - [ - 'title' => 'Pengajuan', - 'description' => $hasData ? 'Data perusahaan sudah diisi' : 'Lengkapi data perusahaan', - ], - [ - 'title' => 'Verifikasi', - 'description' => match ($this->companyVerification?->status) { - VerificationStatus::NEED_REVISION => 'Perlu perbaikan data', - VerificationStatus::PENDING => 'Sedang diverifikasi admin', - default => 'Menunggu pengajuan', - }, - ], - [ - 'title' => 'Selesai', - 'description' => match ($this->companyVerification?->status) { - VerificationStatus::APPROVED => 'Perusahaan disetujui', - VerificationStatus::REJECTED => 'Perusahaan ditolak', - default => 'Menunggu keputusan', - }, - ], - ], - ]; - } - - public function mediaProgress(): array - { - $hasData = $this->partnerMedia !== null; - $currentStep = $this->resolveStep($this->mediaVerification?->status, $hasData); - - return [ - 'title' => 'Media', - 'description' => 'Progress verifikasi media', - 'currentStep' => $currentStep, - 'entity' => $this->partnerMedia, - 'verification' => $this->mediaVerification, - 'canSubmit' => $this->canSubmit($this->partnerMedia, $this->mediaVerification), - 'isApproved' => $this->isApproved($this->mediaVerification), - 'isRejected' => $this->isRejected($this->mediaVerification), - 'isPending' => $this->isPending($this->mediaVerification), - 'needsRevision' => $this->needsRevision($this->mediaVerification), - 'latestNotes' => $this->getLatestReviewNotes($this->mediaVerification), - 'reviewHistory' => $this->getReviewHistory($this->mediaVerification), - 'editUrl' => CustomMedia::getUrl(), - 'steps' => [ - [ - 'title' => 'Pengajuan', - 'description' => $hasData ? 'Data media sudah diisi' : 'Lengkapi data media', - ], - [ - 'title' => 'Verifikasi', - 'description' => match ($this->mediaVerification?->status) { - VerificationStatus::NEED_REVISION => 'Perlu perbaikan data', - VerificationStatus::PENDING => 'Sedang diverifikasi admin', - default => 'Menunggu pengajuan', - }, - ], - [ - 'title' => 'Selesai', - 'description' => match ($this->mediaVerification?->status) { - VerificationStatus::APPROVED => 'Media disetujui', - VerificationStatus::REJECTED => 'Media ditolak', - default => 'Menunggu keputusan', - }, - ], - ], - ]; - } - - public function journalistProgress(): array - { - $hasData = $this->journalist !== null; - $currentStep = $this->resolveStep($this->journalistVerification?->status, $hasData); - - return [ - 'title' => 'Jurnalis', - 'description' => 'Progress verifikasi jurnalis', - 'currentStep' => $currentStep, - 'entity' => $this->journalist, - 'verification' => $this->journalistVerification, - 'canSubmit' => $this->canSubmit($this->journalist, $this->journalistVerification), - 'isApproved' => $this->isApproved($this->journalistVerification), - 'isRejected' => $this->isRejected($this->journalistVerification), - 'isPending' => $this->isPending($this->journalistVerification), - 'needsRevision' => $this->needsRevision($this->journalistVerification), - 'latestNotes' => $this->getLatestReviewNotes($this->journalistVerification), - 'reviewHistory' => $this->getReviewHistory($this->journalistVerification), - 'editUrl' => ManageJournalists::getUrl(), - 'steps' => [ - [ - 'title' => 'Pengajuan', - 'description' => $hasData ? 'Data jurnalis sudah diisi' : 'Lengkapi data jurnalis', - ], - [ - 'title' => 'Verifikasi', - 'description' => match ($this->journalistVerification?->status) { - VerificationStatus::NEED_REVISION => 'Perlu perbaikan data', - VerificationStatus::PENDING => 'Sedang diverifikasi admin', - default => 'Menunggu pengajuan', - }, - ], - [ - 'title' => 'Selesai', - 'description' => match ($this->journalistVerification?->status) { - VerificationStatus::APPROVED => 'Jurnalis disetujui', - VerificationStatus::REJECTED => 'Jurnalis ditolak', - default => 'Menunggu keputusan', - }, - ], - ], - ]; - } - - /** - * Submit company verification action - */ - public function submitCompanyAction(): Action - { - return Action::make('submitCompany') - ->label(fn () => $this->needsRevision($this->companyVerification) ? 'Kirim Ulang' : 'Ajukan Verifikasi') + return Action::make('submitVerification') + ->label(fn () => $this->verificationRequest && $this->verificationRequest->status === VerificationStatus::NEED_REVISION + ? 'Kirim Ulang' + : 'Ajukan Verifikasi') ->icon('heroicon-o-paper-airplane') ->color('primary') ->requiresConfirmation() - ->modalHeading('Ajukan Verifikasi Perusahaan') - ->modalDescription('Apakah Anda yakin ingin mengajukan data perusahaan untuk diverifikasi? Pastikan semua data sudah benar.') + ->modalHeading('Ajukan Verifikasi Lengkap') + ->modalDescription(function () use ($progress) { + if (! $progress['isDataComplete']) { + return 'Data belum lengkap. Silakan lengkapi terlebih dahulu: '.implode(', ', $progress['missingData']); + } + + return 'Apakah Anda yakin ingin mengajukan verifikasi untuk Perusahaan, Media, dan Jurnalis? Pastikan semua data sudah benar dan lengkap.'; + }) ->modalSubmitActionLabel('Ya, Ajukan') - ->visible(fn () => $this->canSubmit($this->company, $this->companyVerification)) + ->visible(fn () => $progress['canSubmit']) + ->disabled(fn () => ! $progress['isDataComplete']) ->action(function () { - $this->submitVerification('company', $this->company, $this->companyVerification); + $this->submitVerification(); }); } /** - * Submit media verification action + * Handle unified verification submission */ - public function submitMediaAction(): Action + protected function submitVerification(): void { - return Action::make('submitMedia') - ->label(fn () => $this->needsRevision($this->mediaVerification) ? 'Kirim Ulang' : 'Ajukan Verifikasi') - ->icon('heroicon-o-paper-airplane') - ->color('primary') - ->requiresConfirmation() - ->modalHeading('Ajukan Verifikasi Media') - ->modalDescription('Apakah Anda yakin ingin mengajukan data media untuk diverifikasi? Pastikan semua data sudah benar.') - ->modalSubmitActionLabel('Ya, Ajukan') - ->visible(fn () => $this->canSubmit($this->partnerMedia, $this->mediaVerification)) - ->action(function () { - $this->submitVerification('media', $this->partnerMedia, $this->mediaVerification); - }); - } + $progress = $this->getVerificationProgress(); - /** - * Submit journalist verification action - */ - public function submitJournalistAction(): Action - { - return Action::make('submitJournalist') - ->label(fn () => $this->needsRevision($this->journalistVerification) ? 'Kirim Ulang' : 'Ajukan Verifikasi') - ->icon('heroicon-o-paper-airplane') - ->color('primary') - ->requiresConfirmation() - ->modalHeading('Ajukan Verifikasi Jurnalis') - ->modalDescription('Apakah Anda yakin ingin mengajukan data jurnalis untuk diverifikasi? Pastikan semua data sudah benar.') - ->modalSubmitActionLabel('Ya, Ajukan') - ->visible(fn () => $this->canSubmit($this->journalist, $this->journalistVerification)) - ->action(function () { - $this->submitVerification('journalist', $this->journalist, $this->journalistVerification); - }); - } - - /** - * Handle verification submission - */ - protected function submitVerification(string $type, $entity, ?VerificationRequest $existingRequest): void - { - if (! $entity) { + // Validate data completeness + if (! $progress['isDataComplete']) { Notification::make() - ->title('Error') - ->body('Data tidak ditemukan. Silakan lengkapi data terlebih dahulu.') + ->title('Data Belum Lengkap') + ->body('Silakan lengkapi data terlebih dahulu: '.implode(', ', $progress['missingData'])) ->danger() ->send(); return; } - if ($existingRequest && $existingRequest->status === VerificationStatus::NEED_REVISION) { - // Resubmit - update existing request - $existingRequest->update([ - 'status' => VerificationStatus::PENDING, - ]); - - $verification = $existingRequest; - } else { - // New submission - $verification = VerificationRequest::create([ - 'verifiable_type' => get_class($entity), - 'verifiable_id' => $entity->id, - 'submitted_by' => auth()->id(), - 'status' => VerificationStatus::PENDING, - ]); - } - - // Update the local property - match ($type) { - 'company' => $this->companyVerification = $verification->fresh(['reviews.reviewer']), - 'media' => $this->mediaVerification = $verification->fresh(['reviews.reviewer']), - 'journalist' => $this->journalistVerification = $verification->fresh(['reviews.reviewer']), - }; - - $entityName = match ($type) { - 'company' => 'Perusahaan', - 'media' => 'Media', - 'journalist' => 'Jurnalis', - }; + $this->verificationRequest = $this->verificationService->submitVerification( + $this->company, + $this->verificationRequest + ); Notification::make() ->title('Berhasil!') - ->body("Data {$entityName} berhasil diajukan untuk diverifikasi.") + ->body('Verifikasi lengkap (Perusahaan, Media, dan Jurnalis) berhasil diajukan untuk diverifikasi.') ->success() ->send(); } @@ -425,9 +138,7 @@ protected function submitVerification(string $type, $entity, ?VerificationReques public function getViewData(): array { return [ - 'companyProgress' => $this->companyProgress(), - 'mediaProgress' => $this->mediaProgress(), - 'journalistProgress' => $this->journalistProgress(), + 'verificationProgress' => $this->getVerificationProgress(), ]; } } diff --git a/app/Filament/Resources/Manage/Journalists/JournalistResource.php b/app/Filament/Resources/Manage/Journalists/JournalistResource.php index 30b9d12..7d47798 100644 --- a/app/Filament/Resources/Manage/Journalists/JournalistResource.php +++ b/app/Filament/Resources/Manage/Journalists/JournalistResource.php @@ -238,9 +238,53 @@ public static function table(Table $table): Table } return $record; + }) + ->visible(function () { + $user = auth()->user(); + + if (! $user->hasRole('Perusahaan')) { + return true; + } + + if (! $user->company?->partnerMedia) { + return false; + } + + $verificationRequest = $user->company + ->verificationRequest() + ->latest() + ->first(); + + if ($verificationRequest && $verificationRequest->status !== \App\Enums\VerificationStatus::NEED_REVISION) { + return false; + } + + return true; }), - DeleteAction::make(), + DeleteAction::make() + ->visible(function () { + $user = auth()->user(); + + if (! $user->hasRole('Perusahaan')) { + return true; + } + + if (! $user->company?->partnerMedia) { + return false; + } + + $verificationRequest = $user->company + ->verificationRequest() + ->latest() + ->first(); + + if ($verificationRequest && $verificationRequest->status !== \App\Enums\VerificationStatus::NEED_REVISION) { + return false; + } + + return true; + }), ForceDeleteAction::make(), diff --git a/app/Filament/Resources/Manage/Journalists/Pages/ManageJournalists.php b/app/Filament/Resources/Manage/Journalists/Pages/ManageJournalists.php index b54b342..3b3fa5e 100644 --- a/app/Filament/Resources/Manage/Journalists/Pages/ManageJournalists.php +++ b/app/Filament/Resources/Manage/Journalists/Pages/ManageJournalists.php @@ -21,6 +21,14 @@ protected function getHeaderActions(): array return []; } + $verificationRequest = auth()->user()->company?->verificationRequest() + ->latest() + ->first(); + + if ($verificationRequest && $verificationRequest->status !== \App\Enums\VerificationStatus::NEED_REVISION) { + return []; + } + return [ CreateAction::make() ->label('Tambah') diff --git a/app/Filament/Resources/Master/Categories/CategoryResource.php b/app/Filament/Resources/Master/Categories/CategoryResource.php index 7e7d7e9..7d262d4 100644 --- a/app/Filament/Resources/Master/Categories/CategoryResource.php +++ b/app/Filament/Resources/Master/Categories/CategoryResource.php @@ -34,7 +34,7 @@ class CategoryResource extends Resource protected static string|BackedEnum|null $navigationIcon = Heroicon::ListBullet; - protected static ?string $navigationLabel = 'Kategori Berita'; + protected static ?string $navigationLabel = 'Kategori'; protected static ?string $recordTitleAttribute = 'name'; @@ -92,7 +92,7 @@ public static function table(Table $table): Table ]) ->toolbarActions([ BulkActionGroup::make([ - ...DefaultBulkActions::make('kategori berita'), + ...DefaultBulkActions::make('kategori'), ]), ]) ->emptyStateIcon(Heroicon::ListBullet) diff --git a/app/Filament/Resources/Master/Categories/Pages/ManageCategories.php b/app/Filament/Resources/Master/Categories/Pages/ManageCategories.php index f77a67d..fd1b80d 100644 --- a/app/Filament/Resources/Master/Categories/Pages/ManageCategories.php +++ b/app/Filament/Resources/Master/Categories/Pages/ManageCategories.php @@ -9,7 +9,7 @@ class ManageCategories extends ManageRecords { - protected static ?string $title = 'Kategori Berita'; + protected static ?string $title = 'Kategori'; protected static string $resource = CategoryResource::class; @@ -18,11 +18,11 @@ protected function getHeaderActions(): array return [ CreateAction::make() ->label('Tambah') - ->modalHeading('Tambah Kategori Berita') + ->modalHeading('Tambah Kategori') ->modalSubmitActionLabel('Simpan') ->modalCancelActionLabel('Batal') ->extraModalFooterActions(fn (CreateAction $action): array => [ - $action->makeModalSubmitAction('createAnother', arguments: ['another' => true]) + $action->makeModalSubmitAction('createAknother', arguments: ['another' => true]) ->label('Simpan dan Tambah Lagi'), ]) ->modalWidth(Width::Large), diff --git a/app/Models/Company.php b/app/Models/Company.php index 9d87b77..12874e0 100644 --- a/app/Models/Company.php +++ b/app/Models/Company.php @@ -8,7 +8,6 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasManyThrough; use Illuminate\Database\Eloquent\Relations\HasOne; -use Illuminate\Database\Eloquent\Relations\MorphOne; use Illuminate\Database\Eloquent\SoftDeletes; use Spatie\MediaLibrary\HasMedia; use Spatie\MediaLibrary\InteractsWithMedia; @@ -41,8 +40,8 @@ public function journalists(): HasManyThrough return $this->hasManyThrough(Journalist::class, PartnerMedia::class); } - public function verificationRequest(): MorphOne + public function verificationRequest(): HasOne { - return $this->morphOne(VerificationRequest::class, 'verifiable'); + return $this->hasOne(VerificationRequest::class); } } diff --git a/app/Models/Journalist.php b/app/Models/Journalist.php index 36ab847..b119332 100644 --- a/app/Models/Journalist.php +++ b/app/Models/Journalist.php @@ -6,7 +6,6 @@ use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; -use Illuminate\Database\Eloquent\Relations\MorphOne; use Illuminate\Database\Eloquent\SoftDeletes; use Spatie\MediaLibrary\HasMedia; use Spatie\MediaLibrary\InteractsWithMedia; @@ -28,9 +27,4 @@ public function partnerMedia(): BelongsTo { return $this->belongsTo(PartnerMedia::class); } - - public function verificationRequest(): MorphOne - { - return $this->morphOne(VerificationRequest::class, 'verifiable'); - } } diff --git a/app/Models/PartnerMedia.php b/app/Models/PartnerMedia.php index 4e98115..482449b 100644 --- a/app/Models/PartnerMedia.php +++ b/app/Models/PartnerMedia.php @@ -10,7 +10,6 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Relations\HasMany; -use Illuminate\Database\Eloquent\Relations\MorphOne; use Illuminate\Database\Eloquent\SoftDeletes; use Spatie\MediaLibrary\HasMedia; use Spatie\MediaLibrary\InteractsWithMedia; @@ -68,9 +67,4 @@ public function cooperationMedias(): HasMany { return $this->hasMany(CooperationMedia::class); } - - public function verificationRequest(): MorphOne - { - return $this->morphOne(VerificationRequest::class, 'verifiable'); - } } diff --git a/app/Models/VerificationRequest.php b/app/Models/VerificationRequest.php index 7a09789..cc6ce79 100644 --- a/app/Models/VerificationRequest.php +++ b/app/Models/VerificationRequest.php @@ -8,7 +8,6 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; -use Illuminate\Database\Eloquent\Relations\MorphTo; use Illuminate\Database\Eloquent\SoftDeletes; class VerificationRequest extends Model @@ -30,9 +29,9 @@ protected function pending(Builder $query): void $query->where('status', VerificationStatus::PENDING); } - public function verifiable(): MorphTo + public function company(): BelongsTo { - return $this->morphTo(); + return $this->belongsTo(Company::class); } public function submittedBy(): BelongsTo diff --git a/app/Services/VerificationService.php b/app/Services/VerificationService.php new file mode 100644 index 0000000..785e633 --- /dev/null +++ b/app/Services/VerificationService.php @@ -0,0 +1,391 @@ +partnerMedia !== null + && $company->partnerMedia?->journalists !== null + && $company->partnerMedia->journalists->count() > 0; + } + + /** + * Get missing data items for verification + */ + public function getMissingData(?Company $company): array + { + $missing = []; + + if (! $company) { + $missing[] = 'Data Perusahaan'; + } + + if ($company && ! $company->partnerMedia) { + $missing[] = 'Data Media'; + } + + if ($company && $company->partnerMedia && ($company->partnerMedia?->journalists === null || $company->partnerMedia?->journalists?->count() === 0)) { + $missing[] = 'Data Jurnalis (minimal 1)'; + } + + return $missing; + } + + /** + * Check if user can submit verification + */ + public function canSubmit(?Company $company, ?VerificationRequest $verificationRequest): bool + { + if (! $this->isDataComplete($company)) { + return false; + } + + // No verification yet - can submit + if (! $verificationRequest) { + return true; + } + + // Can resubmit only if status is NEED_REVISION + return $verificationRequest->status === VerificationStatus::NEED_REVISION; + } + + /** + * Determine the current step based on status + */ + public function resolveStep(?Company $company, ?VerificationRequest $verificationRequest): int + { + if (! $this->isDataComplete($company)) { + return 1; // Data belum lengkap + } + + if (! $verificationRequest) { + return 1; // Belum submit + } + + return match ($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 + */ + public function getLatestReviewNotes(?VerificationRequest $verificationRequest): ?string + { + if (! $verificationRequest) { + return null; + } + + $latestReview = $verificationRequest->reviews() + ->whereIn('decision', [DecisionAdmin::NEED_REVISION, DecisionAdmin::REJECTED]) + ->latest() + ->first(); + + return $latestReview?->note; + } + + /** + * Get all reviews history + */ + public function getReviewHistory(?VerificationRequest $verificationRequest): Collection + { + if (! $verificationRequest) { + return collect(); + } + + return $verificationRequest->reviews() + ->with('reviewer') + ->latest() + ->get(); + } + + /** + * Get unified verification progress data + */ + public function getVerificationProgress(?Company $company, ?VerificationRequest $verificationRequest): array + { + $currentStep = $this->resolveStep($company, $verificationRequest); + $isComplete = $this->isDataComplete($company); + $missingData = $this->getMissingData($company); + + $status = $verificationRequest?->status; + + return [ + 'currentStep' => $currentStep, + 'isDataComplete' => $isComplete, + 'missingData' => $missingData, + 'canSubmit' => $this->canSubmit($company, $verificationRequest), + 'verification' => $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($verificationRequest), + 'reviewHistory' => $this->getReviewHistory($verificationRequest), + 'company' => $company, + 'partnerMedia' => $company?->partnerMedia, + 'journalists' => $company?->partnerMedia?->journalists ?? collect(), + 'steps' => $this->getSteps($verificationRequest, $isComplete, $missingData), + ]; + } + + /** + * Get steps array for stepper + */ + protected function getSteps(?VerificationRequest $verificationRequest, bool $isComplete, array $missingData): array + { + $status = $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', + }, + ], + ]; + } + + /** + * Submit unified verification + */ + public function submitVerification(Company $company, ?VerificationRequest $existingRequest): VerificationRequest + { + if ($existingRequest && $existingRequest->status === VerificationStatus::NEED_REVISION) { + // Resubmit - update existing request + $existingRequest->update([ + 'status' => VerificationStatus::PENDING, + ]); + + return $existingRequest->fresh(['reviews.reviewer']); + } + + // New submission + return VerificationRequest::create([ + 'company_id' => $company->id, + 'submitted_by' => Auth::id(), + 'status' => VerificationStatus::PENDING, + ])->load(['reviews.reviewer']); + } + + /** + * Process review decision - unified for Company, Media, and Journalists + */ + public function processReview(VerificationRequest $verificationRequest, DecisionAdmin $decision, ?string $note): void + { + $verificationRequest->load(['company.partnerMedia.journalists']); + + DB::beginTransaction(); + + try { + // 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, + ]); + + // Update Company status + $company = $verificationRequest->company; + if ($company) { + $company->update(['status' => $newStatus]); + + // Update Media status + $media = $company->partnerMedia; + if ($media) { + $media->update(['status' => $newStatus]); + + // Update all Journalists status + $media->journalists()->update(['status' => $newStatus]); + } + } + + DB::commit(); + } catch (\Exception $e) { + DB::rollBack(); + throw $e; + } + } + + /** + * Get company name from verification request + */ + public function getCompanyName(VerificationRequest $request): string + { + return $request->company?->name ?? 'Data tidak ditemukan'; + } + + /** + * Get verification summary data for admin + */ + 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 alert data for verification status + */ + public function getAlertData(?VerificationRequest $verificationRequest): ?array + { + if (! $verificationRequest) { + return null; + } + + $status = $verificationRequest->status; + $latestNotes = $this->getLatestReviewNotes($verificationRequest); + + 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' => 'Verifikasi lengkap Anda telah diterima dan disetujui oleh admin. Seluruh data (Perusahaan, Media, dan Jurnalis) telah terverifikasi.', + 'color' => 'success', + ]; + } + + if ($status === VerificationStatus::PENDING) { + return [ + 'type' => 'pending', + 'icon' => 'clock', + 'title' => 'Menunggu Verifikasi', + 'message' => 'Data Anda sedang dalam proses verifikasi oleh admin. Mohon tunggu.', + 'color' => 'info', + 'animate' => true, + ]; + } + + return null; + } + + /** + * Get missing data buttons configuration + */ + public function getMissingDataButtons(?Company $company): array + { + $buttons = []; + + if (! $company) { + $buttons[] = [ + 'label' => 'Lengkapi Data Perusahaan', + 'url' => \App\Filament\Pages\CustomCompany::getUrl(), + 'icon' => 'building-office-2', + 'color' => 'warning', + ]; + } + + if ($company && ! $company->partnerMedia) { + $buttons[] = [ + 'label' => 'Lengkapi Data Media', + 'url' => \App\Filament\Pages\CustomMedia::getUrl(), + 'icon' => 'newspaper', + 'color' => 'warning', + ]; + } + + if ($company && $company->partnerMedia && ($company->partnerMedia->journalists === null || $company->partnerMedia->journalists->count() === 0)) { + $buttons[] = [ + 'label' => 'Lengkapi Data Jurnalis', + 'url' => \App\Filament\Resources\Manage\Journalists\Pages\ManageJournalists::getUrl(), + 'icon' => 'user-circle', + 'color' => 'warning', + ]; + } + + return $buttons; + } +} diff --git a/database/migrations/2025_12_22_130245_create_verification_requests_table.php b/database/migrations/2025_12_22_130245_create_verification_requests_table.php index 2817c75..45087db 100644 --- a/database/migrations/2025_12_22_130245_create_verification_requests_table.php +++ b/database/migrations/2025_12_22_130245_create_verification_requests_table.php @@ -1,6 +1,7 @@ id(); - $table->morphs('verifiable'); + $table->foreignIdFor(Company::class); $table->foreignIdFor(User::class, 'submitted_by'); $table->enum('status', VerificationStatus::cases())->default(VerificationStatus::PENDING)->comment(VerificationStatus::comment()); $table->timestamp('created_at')->useCurrent(); diff --git a/resources/views/filament/pages/admin-verification.blade.php b/resources/views/filament/pages/admin-verification.blade.php index ddd7696..77275d2 100644 --- a/resources/views/filament/pages/admin-verification.blade.php +++ b/resources/views/filament/pages/admin-verification.blade.php @@ -1,5 +1,4 @@ - {{-- Status Filter Tabs --}} @php $counts = $this->getStatusCounts(); @endphp @@ -103,39 +102,25 @@
@forelse ($this->getVerifications() as $verification) @php - $entityType = $this->getEntityTypeLabel($verification); - $entityName = $this->getEntityName($verification); - $detailUrl = $this->getDetailUrl($verification); - $latestReview = $verification->reviews->first(); + $card = $this->getCardData($verification); @endphp
-
- {{-- Entity Type Badge --}} - @php - $typeColor = match ($entityType) { - 'Perusahaan' => 'primary', - 'Media' => 'info', - 'Jurnalis' => 'success', - default => 'gray', - }; - @endphp - - {{ $entityType }} - -

- {{ $entityName }} + title="{{ $card['companyName'] }}"> + {{ $card['companyName'] }}

+

+ Perusahaan, Media & Jurnalis +

{{-- Status Badge --}} - - {{ $verification->status->getLabel() }} + + {{ $card['statusLabel'] }}
@@ -153,11 +138,57 @@ class="w-10 h-10 rounded-full bg-primary-100 dark:bg-primary-500/20 flex items-c

Diajukan oleh

- {{ $verification->submittedBy?->name ?? 'Unknown' }} + {{ $card['submittedBy'] }}

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

Perusahaan +

+
+

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

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

Media

+
+

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

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

Jurnalis

+
+

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

+ + {{ $card['summary']['journalists']['exists'] ? '✓ Lengkap' : '⚠ Belum diisi' }} + +
+
+ {{-- Timeline Info --}}
@@ -182,38 +213,42 @@ class="w-10 h-10 rounded-full bg-primary-100 dark:bg-primary-500/20 flex items-c
{{-- Latest Review Note --}} - @if ($latestReview && $latestReview->note) -
-

+ '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 +

+

Catatan Terakhir

- {{ $latestReview->note }} + {{ $card['latestReviewNote'] }}

- oleh {{ $latestReview->reviewer?->name ?? 'Admin' }} • - {{ $latestReview->created_at->diffForHumans() }} + oleh {{ $card['latestReviewReviewer'] }} • + {{ $card['latestReviewCreatedAt']->diffForHumans() }}

@endif {{-- Review History Accordion --}} - @if ($verification->reviews->count() > 0) + @if ($card['reviewsCount'] > 0)
{{-- View Detail Button --}} - @if ($detailUrl) + @if ($card['detailUrl']) + :href="$card['detailUrl']"> Detail @endif {{-- Action Buttons based on status --}} - @if ($verification->status === \App\Enums\VerificationStatus::PENDING) + @if ($card['isPending']) {{ ($this->approveAction)(['id' => $verification->id]) }} {{ ($this->revisionAction)(['id' => $verification->id]) }} {{ ($this->rejectAction)(['id' => $verification->id]) }} - @elseif($verification->status === \App\Enums\VerificationStatus::NEED_REVISION) + @elseif($card['isRevision']) Menunggu revisi dari user... - @elseif($verification->status === \App\Enums\VerificationStatus::APPROVED) + @elseif($card['isApproved']) Verifikasi selesai - @elseif($verification->status === \App\Enums\VerificationStatus::REJECTED) + @elseif($card['isRejected']) diff --git a/resources/views/filament/pages/company.blade.php b/resources/views/filament/pages/company.blade.php index e5fc471..fed56ed 100644 --- a/resources/views/filament/pages/company.blade.php +++ b/resources/views/filament/pages/company.blade.php @@ -2,8 +2,12 @@
{{ $this->form }} -
- Simpan -
+ @if ( + !$verificationRequest || + ($verificationRequest && $verificationRequest->status === App\Enums\VerificationStatus::NEED_REVISION)) +
+ Simpan +
+ @endif
diff --git a/resources/views/filament/pages/custom-media.blade.php b/resources/views/filament/pages/custom-media.blade.php index 1b0fb68..64521ba 100644 --- a/resources/views/filament/pages/custom-media.blade.php +++ b/resources/views/filament/pages/custom-media.blade.php @@ -32,11 +32,13 @@ class="dark:text-gray-400">
{{ $this->form }} -
- - Simpan - -
+ @if ( + !$verificationRequest || + ($verificationRequest && $verificationRequest->status === App\Enums\VerificationStatus::NEED_REVISION)) +
+ Simpan +
+ @endif
@endif diff --git a/resources/views/filament/pages/verification.blade.php b/resources/views/filament/pages/verification.blade.php index 4801db1..f4fe8f5 100644 --- a/resources/views/filament/pages/verification.blade.php +++ b/resources/views/filament/pages/verification.blade.php @@ -1,13 +1,206 @@ + @php + $progress = $this->getVerificationProgress(); + $alert = $progress['alert']; + $hasJournalists = $progress['journalists'] && $progress['journalists']->count() > 0; + @endphp +
- {{-- Company Verification Section --}} - + {{-- Unified Verification Card --}} + + +
+
+
+ +
+
+

+ Verifikasi Data +

+

+ Lengkapi data perusahaan, media, dan jurnalis Anda untuk mengajukan verifikasi kepada + Administrator. +

+
+
- {{-- Media Verification Section --}} - + {{-- Status Badge --}} + + {{ $progress['status']['label'] }} + +
+
- {{-- Journalist Verification Section --}} - +
+ {{-- Stepper Progress --}} +
+ +
+ + {{-- Data Summary --}} +
+ {{-- Company Summary --}} +
+
+ +

Perusahaan

+
+

{{ $progress['company']?->name }}

+ + {{ $progress['company'] ? '✓ Lengkap' : 'Belum Lengkap' }} + +
+ + {{-- Media Summary --}} +
+
+ +

Media

+
+

{{ $progress['partnerMedia']?->name }} +

+ + {{ $progress['partnerMedia'] ? '✓ Lengkap' : 'Belum Lengkap' }} + +
+ + {{-- Journalists Summary --}} +
+
+ +

Jurnalis

+
+

+ {{ $progress['journalists']?->count() }} jurnalis terdaftar +

+ + {{ $hasJournalists ? '✓ Lengkap' : 'Belum Lengkap' }} + +
+
+ + {{-- Alert Messages --}} + @if ($alert) + @php + $alertClasses = match ($alert['color']) { + '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', + }; + $alertIconClasses = match ($alert['color']) { + 'success' => 'text-success-500', + 'warning' => 'text-warning-500', + 'danger' => 'text-danger-500', + 'info' => 'text-info-500', + default => 'text-gray-500', + }; + $alertTitleClasses = match ($alert['color']) { + 'success' => 'text-success-800 dark:text-success-200', + 'warning' => 'text-warning-800 dark:text-warning-200', + 'danger' => 'text-danger-800 dark:text-danger-200', + 'info' => 'text-info-800 dark:text-info-200', + default => 'text-gray-800 dark:text-gray-200', + }; + $alertMessageClasses = match ($alert['color']) { + 'success' => 'text-success-700 dark:text-success-300', + 'warning' => 'text-warning-700 dark:text-warning-300', + 'danger' => 'text-danger-700 dark:text-danger-300', + 'info' => 'text-info-700 dark:text-info-300', + default => 'text-gray-700 dark:text-gray-300', + }; + $iconName = 'heroicon-s-' . $alert['icon']; + $iconClasses = + $alertIconClasses . (isset($alert['animate']) && $alert['animate'] ? ' animate-pulse' : ''); + @endphp +
+
+
+ +
+
+

+ {{ $alert['title'] }} +

+

+ {{ $alert['message'] }} +

+
+
+
+ @endif + + {{-- Review History Accordion --}} + @if ($progress['reviewHistory']->isNotEmpty()) +
+ + +
+
+ @foreach ($progress['reviewHistory'] as $review) +
+
+
+
+ + {{ $review->decision->getLabel() }} + + + oleh {{ $review->reviewer?->name ?? 'Admin' }} + +
+ @if ($review->note) +

+ {{ $review->note }} +

+ @endif +
+
+ {{ $review->created_at->format('d M Y, H:i') }} +
+
+
+ @endforeach +
+
+
+ @endif +
+ + {{-- Footer with Actions --}} + @if ($progress['canSubmit']) + +
+
+ @if ($progress['lastSubmittedAt']) + Terakhir diajukan: {{ $progress['lastSubmittedAt'] }} + @endif +
+ +
+ {{ $this->submitVerificationAction }} +
+
+
+ @endif +