simedkom/app/Filament/Resources/Manage/DataChanges/Infolists/DataChangeInfolist.php

233 lines
12 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace App\Filament\Resources\Manage\DataChanges\Infolists;
use App\Filament\Support\CheerfulNotification;
use App\Models\DataChangeRequest;
use Filament\Infolists\Components\TextEntry;
use Filament\Schemas\Components\Section;
use Filament\Schemas\Schema;
use Illuminate\Support\Facades\Storage;
class DataChangeInfolist
{
public static function configure(Schema $schema): Schema
{
return $schema
->schema([
Section::make()
->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 self::alert('Format data tidak valid.', 'warning');
}
if (isset($new['__delete_request__']) && $new['__delete_request__'] === true) {
return self::deletionView($old);
}
if (empty($old)) {
return self::additionView($new);
}
return self::updateView($old, $new);
}),
]),
])
->columns(1);
}
private static function deletionView(array $old): string
{
$title = CheerfulNotification::getByKey('data_change.deletion_request_title');
$desc = CheerfulNotification::getByKey('data_change.deletion_request_desc');
$rows = collect($old)
->map(fn ($v, $k) => self::row($k, self::renderValue($v)))
->implode('');
return self::card(
header: self::header('del', $title, $desc, self::badge('Penghapusan', 'del')),
body: $rows ?: self::emptyRow(),
);
}
private static function additionView(array $new): string
{
$title = CheerfulNotification::getByKey('data_change.addition_request_title');
$desc = CheerfulNotification::getByKey('data_change.addition_request_desc');
$rows = collect($new)
->map(fn ($v, $k) => self::row($k, self::renderValue($v)))
->implode('');
return self::card(
header: self::header('add', $title, $desc, self::badge('Penambahan Baru', 'add')),
body: $rows ?: self::emptyRow(),
);
}
private static function updateView(array $old, array $new): string
{
$changed = collect($new)
->map(function ($newVal, $key) use ($old) {
$oldVal = $old[$key] ?? null;
if ($oldVal === $newVal) {
return null;
}
return "
<div style='display:grid; grid-template-columns:180px 1fr; border-bottom:1px solid var(--dcr-row-border); align-items:stretch;'>
<div style='padding:11px 14px; font-size:0.75rem; font-weight:500; color:var(--dcr-key-color); letter-spacing:0.025em; background:var(--dcr-key-bg); border-right:1px solid var(--dcr-key-border); display:flex; align-items:center; word-break:break-word;'>".e($key)."</div>
<div style='display:grid; grid-template-columns:1fr 1fr;'>
<div style='padding:11px 14px; background:var(--dcr-old-bg); border-right:1px solid var(--dcr-old-border); color:var(--dcr-old-color); word-break:break-word; font-size:0.8125rem;'>".self::renderValue($oldVal)."</div>
<div style='padding:11px 14px; background:var(--dcr-new-bg); color:var(--dcr-new-color); word-break:break-word; font-size:0.8125rem;'>".self::renderValue($newVal).'</div>
</div>
</div>';
})
->filter();
if ($changed->isEmpty()) {
return self::alert('Tidak ada perubahan data yang terdeteksi.', 'info');
}
$count = $changed->count();
$label = $count === 1 ? '1 field diubah' : "{$count} field diubah";
$diffHeader = "
<div style='display:grid; grid-template-columns:180px 1fr;'>
<div style='padding:8px 14px; background:var(--dcr-key-bg); border-right:1px solid var(--dcr-key-border); border-bottom:1px solid var(--dcr-header-border);'></div>
<div style='display:grid; grid-template-columns:1fr 1fr; border-bottom:1px solid var(--dcr-header-border);'>
<div style='padding:8px 14px; font-size:0.7rem; font-weight:600; color:var(--dcr-key-color); text-transform:uppercase; letter-spacing:0.06em; background:var(--dcr-old-bg); border-right:1px solid var(--dcr-old-border);'>Sebelum</div>
<div style='padding:8px 14px; font-size:0.7rem; font-weight:600; color:var(--dcr-key-color); text-transform:uppercase; letter-spacing:0.06em; background:var(--dcr-new-bg);'>Sesudah</div>
</div>
</div>";
return self::card(
header: self::header('edit', CheerfulNotification::getByKey('data_change.comparison_title'), CheerfulNotification::getByKey('data_change.comparison_desc'), self::badge($label, 'default')),
body: $diffHeader.$changed->implode(''),
);
}
private static function card(string $header, string $body): string
{
return "
<div style='border:1px solid var(--dcr-border); border-radius:var(--c-border-radius); overflow:hidden; background:var(--dcr-bg); font-family:var(--font-family,Inter,ui-sans-serif,sans-serif); font-size:0.8125rem;'>
{$header}
<div>{$body}</div>
</div>";
}
private static function header(string $type, string $title, string $desc, string $badge): string
{
$icon = self::icon($type);
return "
<div style='padding:14px 18px; background:var(--dcr-header-bg); border-bottom:1px solid var(--dcr-header-border); display:flex; align-items:center; gap:10px;'>
{$icon}
<div style='flex:1; min-width:0;'>
<div style='font-size:0.875rem; font-weight:600; color:var(--dcr-title); line-height:1.4;'>{$title}</div>
<div style='font-size:0.75rem; color:var(--dcr-desc); margin-top:1px;'>{$desc}</div>
</div>
{$badge}
</div>";
}
private static function row(string $key, string $value): string
{
return "
<div style='display:grid; grid-template-columns:180px 1fr; border-bottom:1px solid var(--dcr-row-border);'>
<div style='padding:11px 14px; font-size:0.75rem; font-weight:500; color:var(--dcr-key-color); letter-spacing:0.025em; background:var(--dcr-key-bg); border-right:1px solid var(--dcr-key-border); display:flex; align-items:flex-start; word-break:break-word;'>".e($key)."</div>
<div style='padding:11px 14px; color:var(--dcr-val-color); word-break:break-word;'>{$value}</div>
</div>";
}
private static function emptyRow(): string
{
return "<div style='padding:18px; font-size:0.8125rem; color:var(--dcr-muted); font-style:italic;'>Tidak ada data tersedia.</div>";
}
private static function badge(string $label, string $variant): string
{
[$bg, $color, $border] = match ($variant) {
'del' => ['var(--dcr-badge-del-bg)', 'var(--dcr-badge-del-color)', 'var(--dcr-badge-del-border)'],
'add' => ['var(--dcr-badge-add-bg)', 'var(--dcr-badge-add-color)', 'var(--dcr-badge-add-border)'],
default => ['var(--dcr-badge-bg)', 'var(--dcr-badge-color)', 'var(--dcr-badge-border)'],
};
return "<span style='display:inline-flex; align-items:center; border-radius:var(--c-border-radius); padding:2px 8px; font-size:0.7rem; font-weight:600; letter-spacing:0.025em; white-space:nowrap; background:{$bg}; color:{$color}; border:1px solid {$border};'>{$label}</span>";
}
private static function alert(string $message, string $type = 'info'): string
{
$icon = $type === 'warning' ? '⚠' : '';
return "
<div style='background:var(--dcr-alert-bg); border:1px solid var(--dcr-alert-border); border-radius:var(--c-border-radius); padding:14px 16px; display:flex; align-items:flex-start; gap:10px; font-family:var(--font-family,Inter,ui-sans-serif,sans-serif);'>
<span style='color:var(--dcr-alert-color); font-size:0.875rem; margin-top:1px; flex-shrink:0;'>{$icon}</span>
<span style='color:var(--dcr-alert-color); font-size:0.8125rem; font-weight:500;'>{$message}</span>
</div>";
}
private static function icon(string $type): string
{
return match ($type) {
'del' => "<svg xmlns='http://www.w3.org/2000/svg' width='15' height='15' viewBox='0 0 24 24' fill='none' stroke='var(--dcr-icon-del)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' style='flex-shrink:0'><polyline points='3 6 5 6 21 6'/><path d='M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6'/><path d='M10 11v6M14 11v6'/><path d='M9 6V4h6v2'/></svg>",
'add' => "<svg xmlns='http://www.w3.org/2000/svg' width='15' height='15' viewBox='0 0 24 24' fill='none' stroke='var(--dcr-icon-add)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' style='flex-shrink:0'><line x1='12' y1='5' x2='12' y2='19'/><line x1='5' y1='12' x2='19' y2='12'/></svg>",
'edit' => "<svg xmlns='http://www.w3.org/2000/svg' width='15' height='15' viewBox='0 0 24 24' fill='none' stroke='var(--dcr-icon-edit)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' style='flex-shrink:0'><path d='M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7'/><path d='M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z'/></svg>",
default => '',
};
}
private static function renderValue(mixed $value): string
{
if ($value === null || $value === '' || (is_array($value) && empty($value))) {
return "<span style='color:var(--dcr-muted); font-size:0.8rem;'>—</span>";
}
$paths = is_array($value) ? $value : [$value];
return collect($paths)->map(function ($item) {
if (! is_string($item)) {
return "<span style='color:var(--dcr-val-color); font-size:0.8125rem;'>".e(json_encode($item)).'</span>';
}
if (preg_match('/^https?:\/\//i', $item)) {
return "<a href='{$item}' target='_blank' style='color:var(--dcr-link); text-decoration:underline; font-size:0.8125rem; word-break:break-all;'>".e($item).'</a>';
}
if ((str_contains($item, '/') || str_contains($item, '\\')) && ! str_contains($item, ' ')) {
$url = Storage::disk(config('filesystems.default'))->url($item);
$ext = strtolower(pathinfo($item, PATHINFO_EXTENSION));
if (in_array($ext, ['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg'])) {
return "<img src='{$url}' style='max-height:96px; border-radius:var(--c-border-radius); border:1px solid var(--dcr-img-border); margin-top:4px; display:block;'>";
}
if ($ext === 'pdf') {
return "
<div style='margin-top:6px;'>
<iframe src='{$url}' style='width:100%; height:280px; border:1px solid var(--dcr-img-border); border-radius:var(--c-border-radius);'></iframe>
</div>";
}
$extUp = $ext ? strtoupper($ext) : 'FILE';
return "<a href='{$url}' style='display:inline-flex; align-items:center; gap:4px; color:var(--dcr-link); font-size:0.8rem; font-weight:500; text-decoration:none;'>
<svg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><path d='M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48'/></svg>
Dokumen {$extUp}
</a>";
}
return "<span style='color:var(--dcr-val-color); font-size:0.8125rem;'>".e($item).'</span>';
})->implode('<br>');
}
}