feat: Implement company-based verification status for media and journalist data change requests, and refine admin verification UI/UX.
This commit is contained in:
parent
78246123c5
commit
9005f9cbec
@ -19,6 +19,7 @@
|
||||
use Filament\Forms\Concerns\InteractsWithForms;
|
||||
use Filament\Forms\Contracts\HasForms;
|
||||
use Filament\Pages\Page;
|
||||
use Filament\Support\Enums\Width;
|
||||
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use UnitEnum;
|
||||
@ -186,13 +187,14 @@ public function revisionAction(): Action
|
||||
Textarea::make('note')
|
||||
->label('Catatan Revisi')
|
||||
->required()
|
||||
->placeholder('Jelaskan bagian yang perlu diperbaiki (Perusahaan, Media, atau Jurnalis)...')
|
||||
->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);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -292,8 +294,8 @@ protected function processReview(int $requestId, DecisionAdmin $decision, ?strin
|
||||
]);
|
||||
|
||||
CheerfulNotification::danger(
|
||||
'Belum ketemu nih 🙌',
|
||||
'Data verifikasi belum kami temukan atau terjadi kesalahan sistem. Coba sebentar lagi, ya.',
|
||||
'Terjadi Kesalahan ⚠️',
|
||||
'Proses verifikasi gagal karena kesalahan sistem. Tim kami sudah mencatat masalah ini. Silakan coba lagi nanti.',
|
||||
)
|
||||
->send();
|
||||
}
|
||||
|
||||
@ -3,7 +3,6 @@
|
||||
namespace App\Filament\Pages;
|
||||
|
||||
use App\Enums\DataChangeStatus;
|
||||
use App\Enums\VerificationStatus;
|
||||
use App\Filament\Resources\Manage\DataChanges\DataChangesResource;
|
||||
use App\Filament\Support\CheerfulNotification;
|
||||
use App\Models\Company as CompanyModel;
|
||||
@ -271,7 +270,7 @@ public function save(): void
|
||||
|
||||
public function saveAction(): Action
|
||||
{
|
||||
$needsReview = $this->company?->status === VerificationStatus::APPROVED;
|
||||
$needsReview = $this->company?->verificationRequest?->status !== null;
|
||||
|
||||
return Action::make('save')
|
||||
->label('Simpan')
|
||||
|
||||
@ -6,7 +6,6 @@
|
||||
use App\Enums\MediaClassification;
|
||||
use App\Enums\MediaType;
|
||||
use App\Enums\RoleEnum;
|
||||
use App\Enums\VerificationStatus;
|
||||
use App\Filament\Resources\Manage\DataChanges\DataChangesResource;
|
||||
use App\Filament\Support\CheerfulNotification;
|
||||
use App\Models\Company;
|
||||
@ -225,7 +224,7 @@ public function save(): void
|
||||
|
||||
public function saveAction(): Action
|
||||
{
|
||||
$needsReview = $this->media?->status === VerificationStatus::APPROVED;
|
||||
$needsReview = $this->media?->company?->verificationRequest?->status !== null;
|
||||
|
||||
return Action::make('save')
|
||||
->label('Simpan')
|
||||
@ -251,7 +250,7 @@ public function saveAction(): Action
|
||||
|
||||
public function performSave(array $data = [], bool $needsReview = false): void
|
||||
{
|
||||
if ($needsReview && $this->media !== null) {
|
||||
if ($needsReview) {
|
||||
$existingRequest = DataChangeRequest::where('entity_type', PartnerMedia::class)
|
||||
->where('entity_id', $this->media->id)
|
||||
->where('status', DataChangeStatus::PENDING)
|
||||
|
||||
@ -26,13 +26,15 @@ protected function setUp(): void
|
||||
->modalSubmitActionLabel('Simpan')
|
||||
->modalCancelActionLabel('Batal')
|
||||
->successNotification(null)
|
||||
->extraModalFooterActions(fn (CreateAction $action): array => [
|
||||
->extraModalFooterActions(fn(CreateAction $action): array => [
|
||||
$action->makeModalSubmitAction('createAnother', arguments: ['another' => true])
|
||||
->label('Simpan dan Tambah Lagi'),
|
||||
])
|
||||
->modalWidth(Width::Large)
|
||||
->using(function (array $data, string $model): Model {
|
||||
$data['partner_media_id'] = auth()->user()->company?->partnerMedia?->id;
|
||||
$user = auth()->user();
|
||||
|
||||
$data['partner_media_id'] = $user->company?->partnerMedia?->id;
|
||||
|
||||
$journalist = $model::create($data);
|
||||
|
||||
@ -68,7 +70,7 @@ protected function setUp(): void
|
||||
}
|
||||
|
||||
// If company is already approved, addition of new journalist needs permohonan/review
|
||||
if (auth()->user()->company?->status === VerificationStatus::APPROVED) {
|
||||
if ($user->company?->verificationRequest !== null) {
|
||||
$fieldLabels = Journalist::dataChangeEntryLabels();
|
||||
$newFields = [];
|
||||
|
||||
@ -101,7 +103,7 @@ protected function setUp(): void
|
||||
->each(function ($admin) use ($dataChangeRequest): void {
|
||||
$admin->notify(new BroadcastNotification([
|
||||
'title' => 'Ada Penambahan Jurnalis Baru ✨',
|
||||
'body' => 'Halooo Admin! 👋 '.auth()->user()->name.' baru saja menambahkan jurnalis baru. Yuk, cek detailnya! 🚀',
|
||||
'body' => 'Halooo Admin! 👋 ' . $user->name . ' baru saja menambahkan jurnalis baru. Yuk, cek detailnya! 🚀',
|
||||
'action' => [
|
||||
Action::make('view')
|
||||
->label('Lihat')
|
||||
|
||||
@ -23,9 +23,9 @@ protected function setUp(): void
|
||||
|
||||
$this->successNotification(null);
|
||||
|
||||
$this->modalHeading(fn () => auth()->user()->company?->status === VerificationStatus::APPROVED && auth()->user()->hasRole(RoleEnum::PERUSAHAAN->value) ? 'Ajukan Penghapusan Jurnalis' : 'Hapus Jurnalis');
|
||||
$this->modalHeading(fn() => auth()->user()->company?->verificationRequest?->status === VerificationStatus::APPROVED ? 'Ajukan Penghapusan Jurnalis' : 'Hapus Jurnalis');
|
||||
|
||||
$this->schema(fn () => auth()->user()->company?->status === VerificationStatus::APPROVED && auth()->user()->hasRole(RoleEnum::PERUSAHAAN->value) ? [
|
||||
$this->schema(fn() => auth()->user()->company?->verificationRequest?->status === VerificationStatus::APPROVED ? [
|
||||
Textarea::make('deletion_reason')
|
||||
->label('Alasan Penghapusan')
|
||||
->placeholder('Jelaskan alasan mengapa jurnalis ini dihapus...')
|
||||
@ -34,10 +34,8 @@ protected function setUp(): void
|
||||
|
||||
$this->action(function (Journalist $record, array $data): void {
|
||||
$user = auth()->user();
|
||||
$needsReview = $user->company?->status === VerificationStatus::APPROVED &&
|
||||
$user->hasRole(RoleEnum::PERUSAHAAN->value);
|
||||
|
||||
if ($needsReview) {
|
||||
if ($user->company?->verificationRequest !== null) {
|
||||
$existingRequest = DataChangeRequest::where('entity_type', Journalist::class)
|
||||
->where('entity_id', $record->id)
|
||||
->where('status', DataChangeStatus::PENDING)
|
||||
@ -71,7 +69,7 @@ protected function setUp(): void
|
||||
->each(function ($admin) use ($user, $record, $dataChangeRequest): void {
|
||||
$admin->notify(new BroadcastNotification([
|
||||
'title' => 'Ada Pengajuan Penghapusan Jurnalis ✨',
|
||||
'body' => 'Halooo Admin! 👋 '.$user->name.' baru saja mengajukan penghapusan jurnalis '.$record->name.'. Yuk, cek detailnya! 🚀',
|
||||
'body' => 'Halooo Admin! 👋 ' . $user->name . ' baru saja mengajukan penghapusan jurnalis ' . $record->name . '. Yuk, cek detailnya! 🚀',
|
||||
'action' => [
|
||||
Action::make('view')
|
||||
->label('Lihat')
|
||||
|
||||
@ -50,10 +50,9 @@ protected function setUp(): void
|
||||
return $data;
|
||||
})
|
||||
->using(function (Journalist $record, array $data): Journalist {
|
||||
$needsReview = auth()->user()->company?->status === VerificationStatus::APPROVED &&
|
||||
auth()->user()->hasRole(RoleEnum::PERUSAHAAN->value);
|
||||
$user = auth()->user();
|
||||
|
||||
if ($needsReview) {
|
||||
if ($user->company?->verificationRequest !== null) {
|
||||
$existingRequest = DataChangeRequest::where('entity_type', Journalist::class)
|
||||
->where('entity_id', $record->id)
|
||||
->where('status', DataChangeStatus::PENDING)
|
||||
@ -67,69 +66,67 @@ protected function setUp(): void
|
||||
|
||||
return $record;
|
||||
}
|
||||
}
|
||||
|
||||
$fieldLabels = Journalist::dataChangeEntryLabels();
|
||||
$editableFields = ['name', 'email', 'phone_number', 'press_card', 'ukw_certificate'];
|
||||
$fieldLabels = Journalist::dataChangeEntryLabels();
|
||||
$editableFields = ['name', 'email', 'phone_number', 'press_card', 'ukw_certificate'];
|
||||
|
||||
$changedFields = [];
|
||||
$oldDataArr = [];
|
||||
$changedFields = [];
|
||||
$oldDataArr = [];
|
||||
|
||||
$casts = $record->getCasts();
|
||||
foreach ($editableFields as $field) {
|
||||
$val = $data[$field] ?? null;
|
||||
$oldVal = $record->{$field};
|
||||
$casts = $record->getCasts();
|
||||
foreach ($editableFields as $field) {
|
||||
$val = $data[$field] ?? null;
|
||||
$oldVal = $record->{$field};
|
||||
|
||||
// If value is a raw scalar but should be an enum, resolve it for display
|
||||
if (isset($casts[$field]) && ! ($val instanceof \BackedEnum) && enum_exists($casts[$field])) {
|
||||
$enumClass = $casts[$field];
|
||||
$val = $enumClass::tryFrom($val) ?? $val;
|
||||
// If value is a raw scalar but should be an enum, resolve it for display
|
||||
if (isset($casts[$field]) && ! ($val instanceof \BackedEnum) && enum_exists($casts[$field])) {
|
||||
$enumClass = $casts[$field];
|
||||
$val = $enumClass::tryFrom($val) ?? $val;
|
||||
}
|
||||
|
||||
$displayVal = ($val instanceof HasLabel) ? $val->getLabel() : ($val instanceof \BackedEnum ? $val->value : $val);
|
||||
$displayOldVal = ($oldVal instanceof HasLabel) ? $oldVal->getLabel() : ($oldVal instanceof \BackedEnum ? $oldVal->value : $oldVal);
|
||||
|
||||
if ($displayOldVal !== $displayVal) {
|
||||
$label = $fieldLabels[$field] ?? $field;
|
||||
$changedFields[$label] = $displayVal;
|
||||
$oldDataArr[$label] = $displayOldVal;
|
||||
}
|
||||
}
|
||||
|
||||
$displayVal = ($val instanceof HasLabel) ? $val->getLabel() : ($val instanceof \BackedEnum ? $val->value : $val);
|
||||
$displayOldVal = ($oldVal instanceof HasLabel) ? $oldVal->getLabel() : ($oldVal instanceof \BackedEnum ? $oldVal->value : $oldVal);
|
||||
$documentFields = ['press_card_docs', 'ukw_certificate_docs'];
|
||||
$mediaItems = $record->getMedia('journalists');
|
||||
|
||||
if ($displayOldVal !== $displayVal) {
|
||||
$label = $fieldLabels[$field] ?? $field;
|
||||
$changedFields[$label] = $displayVal;
|
||||
$oldDataArr[$label] = $displayOldVal;
|
||||
foreach ($documentFields as $field) {
|
||||
$newDocs = array_values((array) ($data[$field] ?? []));
|
||||
sort($newDocs);
|
||||
|
||||
$docType = str($field)->replace('_docs', '')->slug('-');
|
||||
$currentMedia = $mediaItems
|
||||
->where('custom_properties.doc_type', $docType)
|
||||
->sortByDesc('created_at')
|
||||
->first();
|
||||
|
||||
$oldDocs = $currentMedia ? [$currentMedia->getPathRelativeToRoot()] : [];
|
||||
$oldDocs = array_values($oldDocs);
|
||||
sort($oldDocs);
|
||||
|
||||
if (json_encode($newDocs) !== json_encode($oldDocs)) {
|
||||
$label = $fieldLabels[$field] ?? $field;
|
||||
$changedFields[$label] = $newDocs;
|
||||
$oldDataArr[$label] = $oldDocs;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$documentFields = ['press_card_docs', 'ukw_certificate_docs'];
|
||||
$mediaItems = $record->getMedia('journalists');
|
||||
if (empty($changedFields)) {
|
||||
CheerfulNotification::info(
|
||||
'Belum ada yang berubah ✨',
|
||||
'Ubah data terlebih dulu lalu simpan kembali 💪'
|
||||
)->send();
|
||||
|
||||
foreach ($documentFields as $field) {
|
||||
$newDocs = array_values((array) ($data[$field] ?? []));
|
||||
sort($newDocs);
|
||||
|
||||
$docType = str($field)->replace('_docs', '')->slug('-');
|
||||
$currentMedia = $mediaItems
|
||||
->where('custom_properties.doc_type', $docType)
|
||||
->sortByDesc('created_at')
|
||||
->first();
|
||||
|
||||
$oldDocs = $currentMedia ? [$currentMedia->getPathRelativeToRoot()] : [];
|
||||
$oldDocs = array_values($oldDocs);
|
||||
sort($oldDocs);
|
||||
|
||||
if (json_encode($newDocs) !== json_encode($oldDocs)) {
|
||||
$label = $fieldLabels[$field] ?? $field;
|
||||
$changedFields[$label] = $newDocs;
|
||||
$oldDataArr[$label] = $oldDocs;
|
||||
return $record;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($changedFields)) {
|
||||
CheerfulNotification::info(
|
||||
'Belum ada yang berubah ✨',
|
||||
'Ubah data terlebih dulu lalu simpan kembali 💪'
|
||||
)->send();
|
||||
|
||||
return $record;
|
||||
}
|
||||
|
||||
if ($needsReview) {
|
||||
$dataChangeRequest = DataChangeRequest::create([
|
||||
'user_id' => auth()->id(),
|
||||
'entity_type' => Journalist::class,
|
||||
@ -149,7 +146,7 @@ protected function setUp(): void
|
||||
->each(function ($admin) use ($dataChangeRequest): void {
|
||||
$admin->notify(new BroadcastNotification([
|
||||
'title' => 'Ada Pengajuan Perubahan Jurnalis ✨',
|
||||
'body' => 'Halooo Admin! 👋 '.auth()->user()->name.' baru saja mengajukan perubahan data jurnalis. Yuk, cek detailnya! 🚀',
|
||||
'body' => 'Halooo Admin! 👋 ' . $user->name . ' baru saja mengajukan perubahan data jurnalis. Yuk, cek detailnya! 🚀',
|
||||
'action' => [
|
||||
Action::make('view')
|
||||
->label('Lihat')
|
||||
@ -163,7 +160,7 @@ protected function setUp(): void
|
||||
|
||||
$this->performSave($record, $data);
|
||||
|
||||
CheerfulNotification::update()->send(); // Manual success notification
|
||||
CheerfulNotification::update()->send();
|
||||
|
||||
return $record;
|
||||
})
|
||||
@ -198,7 +195,7 @@ private function performSave(Journalist $record, array $data): void
|
||||
];
|
||||
|
||||
$existingMedia = $record->getMedia('journalists');
|
||||
$existingPaths = $existingMedia->map(fn ($m) => $m->getPathRelativeToRoot())->toArray();
|
||||
$existingPaths = $existingMedia->map(fn($m) => $m->getPathRelativeToRoot())->toArray();
|
||||
|
||||
foreach ($documents as $field) {
|
||||
if (empty($data[$field])) {
|
||||
|
||||
@ -49,17 +49,6 @@ class JournalistResource extends Resource
|
||||
|
||||
protected static ?string $slug = 'manage/journalists';
|
||||
|
||||
public static function getNavigationBadge(): ?string
|
||||
{
|
||||
return static::getModel()::pending()
|
||||
->when(auth()->user()->hasRole(RoleEnum::PERUSAHAAN->value), function (Builder $query): void {
|
||||
$query->whereHas('partnerMedia.company', function (Builder $q): void {
|
||||
$q->where('user_id', auth()->id());
|
||||
});
|
||||
})
|
||||
->count() ?: null;
|
||||
}
|
||||
|
||||
public static function form(Schema $schema): Schema
|
||||
{
|
||||
$documents = [
|
||||
|
||||
@ -112,7 +112,7 @@ public static function infolist(Schema $schema): Schema
|
||||
->first();
|
||||
|
||||
if (! $media) {
|
||||
return 'Tidak ada dokumen';
|
||||
return '';
|
||||
}
|
||||
|
||||
$url = $media->getUrl();
|
||||
@ -298,9 +298,19 @@ public static function table(Table $table): Table
|
||||
->sortable()
|
||||
->badge(),
|
||||
|
||||
TextColumn::make('company.status')
|
||||
TextColumn::make('status')
|
||||
->label('Status')
|
||||
->badge(),
|
||||
->badge()
|
||||
->getStateUsing(function ($record) {
|
||||
return $record->company->verificationRequest
|
||||
? $record->company->verificationRequest->status->getLabel()
|
||||
: 'Belum Mengajukan';
|
||||
})
|
||||
->color(function ($record) {
|
||||
return $record->company->verificationRequest
|
||||
? $record->company->verificationRequest->status->getColor()
|
||||
: 'gray';
|
||||
}),
|
||||
|
||||
TextColumn::make('created_at')
|
||||
->label('Tgl Daftar')
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\VerificationStatus;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
@ -19,13 +18,6 @@ class Company extends Model implements HasMedia
|
||||
|
||||
protected $guarded = ['id'];
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'status' => VerificationStatus::class,
|
||||
];
|
||||
}
|
||||
|
||||
public function journalists(): HasManyThrough
|
||||
{
|
||||
return $this->hasManyThrough(Journalist::class, PartnerMedia::class);
|
||||
|
||||
@ -17,13 +17,6 @@ class Journalist extends Model implements HasMedia
|
||||
|
||||
protected $guarded = ['id'];
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'status' => VerificationStatus::class,
|
||||
];
|
||||
}
|
||||
|
||||
public function partnerMedia(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(PartnerMedia::class);
|
||||
|
||||
@ -5,7 +5,6 @@
|
||||
use App\Enums\ApprovalStatus;
|
||||
use App\Enums\MediaClassification;
|
||||
use App\Enums\MediaType;
|
||||
use App\Enums\VerificationStatus;
|
||||
use Illuminate\Database\Eloquent\Attributes\Scope;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
@ -29,34 +28,9 @@ protected function casts(): array
|
||||
return [
|
||||
'type' => MediaType::class,
|
||||
'classification' => MediaClassification::class,
|
||||
'status' => VerificationStatus::class,
|
||||
];
|
||||
}
|
||||
|
||||
#[Scope]
|
||||
protected function pending(Builder $query): void
|
||||
{
|
||||
$query->where('status', VerificationStatus::PENDING);
|
||||
}
|
||||
|
||||
#[Scope]
|
||||
protected function approved(Builder $query): void
|
||||
{
|
||||
$query->where('status', VerificationStatus::APPROVED);
|
||||
}
|
||||
|
||||
#[Scope]
|
||||
protected function needRevision(Builder $query): void
|
||||
{
|
||||
$query->where('status', VerificationStatus::NEED_REVISION);
|
||||
}
|
||||
|
||||
#[Scope]
|
||||
protected function rejected(Builder $query): void
|
||||
{
|
||||
$query->where('status', VerificationStatus::REJECTED);
|
||||
}
|
||||
|
||||
#[Scope]
|
||||
protected function verified(Builder $query): void
|
||||
{
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
<?php
|
||||
|
||||
use App\Enums\VerificationStatus;
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
@ -28,7 +27,6 @@ public function up(): void
|
||||
$table->string('annual_tax_return', 100);
|
||||
$table->string('domicile_certificate', 100);
|
||||
$table->string('profile', 100);
|
||||
$table->enum('status', VerificationStatus::cases())->default(VerificationStatus::PENDING)->comment(VerificationStatus::comment());
|
||||
$table->timestamp('created_at')->useCurrent();
|
||||
$table->timestamp('updated_at')->nullable()->useCurrentOnUpdate();
|
||||
$table->softDeletes();
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
|
||||
use App\Enums\MediaClassification;
|
||||
use App\Enums\MediaType;
|
||||
use App\Enums\VerificationStatus;
|
||||
use App\Models\Company;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
@ -25,7 +24,6 @@ public function up(): void
|
||||
$table->enum('classification', [MediaClassification::values()])->comment(MediaClassification::comment());
|
||||
$table->string('journalism_organization')->nullable();
|
||||
$table->string('press_council_certificate')->nullable();
|
||||
$table->enum('status', VerificationStatus::cases())->default(VerificationStatus::PENDING)->comment(VerificationStatus::comment());
|
||||
$table->timestamp('created_at')->useCurrent();
|
||||
$table->timestamp('updated_at')->nullable()->useCurrentOnUpdate();
|
||||
$table->softDeletes();
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
<?php
|
||||
|
||||
use App\Enums\VerificationStatus;
|
||||
use App\Models\PartnerMedia;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
@ -21,7 +20,6 @@ public function up(): void
|
||||
$table->string('phone_number', 20);
|
||||
$table->string('press_card', 100)->nullable();
|
||||
$table->string('ukw_certificate', 100)->nullable();
|
||||
$table->enum('status', VerificationStatus::cases())->default(VerificationStatus::PENDING)->comment(VerificationStatus::comment());
|
||||
$table->timestamp('created_at')->useCurrent();
|
||||
$table->timestamp('updated_at')->nullable()->useCurrentOnUpdate();
|
||||
$table->softDeletes();
|
||||
|
||||
Loading…
Reference in New Issue
Block a user