simedkom/app/Filament/Resources/Manage/DataChanges/DataChangesResource.php

293 lines
14 KiB
PHP

<?php
namespace App\Filament\Resources\Manage\DataChanges;
use App\Enums\DataChangeStatus;
use App\Enums\RoleEnum;
use App\Filament\Actions\DefaultBulkActions;
use App\Filament\Columns\RowIndexColumn;
use App\Filament\Resources\Manage\DataChanges\Actions\DataChangeRequest\AcceptAction;
use App\Filament\Resources\Manage\DataChanges\Actions\DataChangeRequest\RejectAction;
use App\Filament\Resources\Manage\DataChanges\Pages\ManageDataChanges;
use App\Filament\Resources\Manage\DataChanges\Pages\ViewDataChange;
use App\Models\Company;
use App\Models\DataChangeRequest;
use App\Models\Journalist;
use App\Models\PartnerMedia;
use BackedEnum;
use Filament\Actions\BulkActionGroup;
use Filament\Actions\ViewAction;
use Filament\Infolists\Components\TextEntry;
use Filament\Resources\Resource;
use Filament\Schemas\Schema;
use Filament\Support\Icons\Heroicon;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Filters\SelectFilter;
use Filament\Tables\Filters\TrashedFilter;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\Storage;
use UnitEnum;
class DataChangesResource extends Resource
{
protected static ?string $model = DataChangeRequest::class;
protected static string|UnitEnum|null $navigationGroup = 'Kelola';
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedDocumentText;
protected static ?string $navigationLabel = 'Permohonan Perubahan Data';
protected static ?int $navigationSort = 8;
protected static ?string $recordTitleAttribute = 'change_reason';
protected static ?string $slug = 'manage/data-changes';
public static function getNavigationBadge(): ?string
{
if (auth()->user()->hasRole(RoleEnum::PERUSAHAAN)) {
return null;
}
return static::getModel()::pending()->count();
}
public static function infolist(Schema $schema): Schema
{
return $schema
->schema([
TextEntry::make('comparison')
->hiddenLabel()
->html()
->state(function (DataChangeRequest $record): string {
$old = $record->old_data ?? [];
$new = $record->new_data ?? [];
if (! is_array($old) || ! is_array($new)) {
return '<em>Format data tidak valid.</em>';
}
$renderValue = function ($value) {
if (empty($value)) {
return '<em>Kosong</em>';
}
$paths = is_array($value) ? $value : [$value];
$isProbablyPath = false;
foreach ($paths as $path) {
if (is_string($path) && (str_contains($path, '/') || str_contains($path, '\\'))) {
$isProbablyPath = true;
break;
}
}
if ($isProbablyPath) {
return collect($paths)
->map(function ($path) {
if (! is_string($path)) {
return e(json_encode($path));
}
$url = Storage::disk(config('filesystems.default'))->url($path);
$extension = strtolower(pathinfo($path, PATHINFO_EXTENSION));
if (in_array($extension, ['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg'])) {
return "<a href='{$url}' target='_blank'><img src='{$url}' style='max-height:100px; border-radius:8px; margin-top:4px; border:1px solid #e5e7eb;'></a>";
}
if ($extension === 'pdf') {
return "
<div style='margin-top:4px;'>
<iframe src='{$url}' style='width:100%; height:300px; border:1px solid #e5e7eb; border-radius:8px;'></iframe>
<a href='{$url}' target='_blank' style='display:block; margin-top:4px; color:#3b82f6; text-decoration:underline; font-size:0.75rem;'>↗️ Buka PDF di Tab Baru</a>
</div>
";
}
return "<a href='{$url}' target='_blank' style='color:#3b82f6; text-decoration:underline; font-size:0.875rem;'>📎 Buka Dokumen (".strtoupper($extension).')</a>';
})
->implode('<br>');
}
return e(is_scalar($value) ? $value : json_encode($value));
};
// 1. Deletion Request
if (isset($new['__delete_request__']) && $new['__delete_request__'] === true) {
return "
<div style='background-color:#fee2e2; border:1px solid #f87171; color:#991b1b; padding:16px; border-radius:8px; margin-bottom:16px;'>
<div style='font-weight:700; font-size:1.125rem; margin-bottom:4px;'>🚮 Permohonan Penghapusan Data</div>
<p style='font-size:0.875rem;'>User mengajukan penghapusan permanen untuk data ini. Semua informasi terkait akan dihapus setelah disetujui.</p>
</div>
";
}
// 2. New Addition Request
if (empty($old)) {
$rows = collect($new)
->map(function ($value, $key) use ($renderValue) {
return sprintf(
"<div style='margin-bottom:12px; border-bottom:1px solid #f3f4f6; padding-bottom:8px'>
<div style='font-weight:600; color:#374151; margin-bottom:4px'>%s</div>
<div style='color:#16a34a; font-weight:500;'>[BARU]</div>
<div style='margin-top:4px'>%s</div>
</div>",
e($key),
$renderValue($value)
);
})
->implode('');
return "
<div style='background-color:#f0fdf4; border:1px solid #bbf7d0; color:#166534; padding:16px; border-radius:8px; margin-bottom:16px;'>
<div style='font-weight:700; font-size:1.125rem; margin-bottom:4px;'>✨ Permohonan Penambahan Data</div>
<p style='font-size:0.875rem;'>Berikut adalah data baru yang akan dibuat.</p>
</div>
{$rows}
";
}
// 3. Standard Comparison (Update)
return collect($new)
->map(function ($newValue, $key) use ($old, $renderValue) {
$oldValue = $old[$key] ?? null;
if ($oldValue === $newValue) {
return null;
}
return sprintf(
"<div style='margin-bottom:16px; border-bottom:1px solid #f3f4f6; padding-bottom:12px'>
<div style='font-weight:600; color:#374151; margin-bottom:4px'>%s</div>
<div style='display:grid; grid-template-columns: 1fr 1fr; gap:16px'>
<div>
<span style='font-size:0.75rem; font-weight:700; color:#dc2626; text-transform:uppercase'>LAMA</span>
<div style='margin-top:2px'>%s</div>
</div>
<div>
<span style='font-size:0.75rem; font-weight:700; color:#16a34a; text-transform:uppercase'>BARU</span>
<div style='margin-top:2px'>%s</div>
</div>
</div>
</div>",
e($key),
$renderValue($oldValue),
$renderValue($newValue)
);
})
->filter()
->implode('');
})
->columnSpanFull(),
])
->columns(1);
}
public static function table(Table $table): Table
{
return $table
->columns([
...RowIndexColumn::make(),
TextColumn::make('user.name')
->label('Pemohon')
->searchable()
->sortable()
->description(fn (DataChangeRequest $record): ?string => $record?->entity?->name)
->visible(! auth()->user()->hasRole(RoleEnum::PERUSAHAAN->value)),
TextColumn::make('entity_type')
->label('Jenis Data')
->searchable()
->sortable()
->description(fn (DataChangeRequest $record): ?string => match (true) {
isset($record->new_data['__delete_request__']) => '🗑️ Penghapusan',
empty($record->old_data) => '✨ Penambahan',
default => '📝 Perubahan',
})
->formatStateUsing(fn (DataChangeRequest $record): ?string => match ($record->entity_type) {
Company::class => 'Perusahaan',
PartnerMedia::class => 'Media',
Journalist::class => 'Jurnalis',
default => 'Lainnya',
})
->color(fn (DataChangeRequest $record): ?string => match ($record->entity_type) {
Company::class => 'success',
PartnerMedia::class => 'info',
Journalist::class => 'warning',
default => 'secondary',
})
->badge(),
TextColumn::make('status')
->label('Status')
->searchable()
->sortable()
->badge()
->formatStateUsing(fn (DataChangeStatus $state): string => $state->getLabel())
->color(fn (DataChangeStatus $state): string => $state->getColor())
->description(fn (DataChangeRequest $record): ?string => $record?->review ? 'Oleh: '.$record->review?->reviewer?->name : ''),
TextColumn::make('change_reason')
->label('Alasan Perubahan')
->limit(50),
TextColumn::make('review.note')
->label('Alasan Penolakan')
->limit(50),
TextColumn::make('created_at')
->label('Tgl Permohonan')
->date()
->sortable()
->dateTime('l, d F Y H:i:s')
->wrap(),
])
->filters([
SelectFilter::make('status')
->label('Status')
->options(DataChangeStatus::class)
->native(false),
TrashedFilter::make()
->native(false)
->visible(fn () => auth()->user()?->hasRole(RoleEnum::DEVELOPER->value)),
])
->recordActions([
ViewAction::make(),
AcceptAction::make('accept'),
RejectAction::make('reject'),
])
->toolbarActions([
BulkActionGroup::make([
...DefaultBulkActions::make('permohonan perubahan data'),
]),
])
->emptyStateIcon(Heroicon::OutlinedDocumentText)
->emptyStateDescription('Belum ada permohonan perubahan data.')
->defaultSort('created_at', 'desc')
->deferFilters(false)
->paginated([25, 50, 100, 'all'])
->deferLoading();
}
public static function getEloquentQuery(): Builder
{
return parent::getEloquentQuery()
->when(auth()->user()->hasRole(RoleEnum::PERUSAHAAN->value), function (Builder $q): Builder {
return $q->where('user_id', auth()->id());
});
}
public static function getPages(): array
{
return [
'index' => ManageDataChanges::route('/'),
'view' => ViewDataChange::route('/{record}'),
];
}
}