buildCreatedChanges($newValues); FormHistory::create([ 'causer_id' => Auth::id(), 'module' => $module, 'event' => 'created', 'description' => "{$module} ditambahkan", 'attribute_changes' => $changes ?: null, ]); } private function logUpdated(Model $model, string $module, array $oldValues, array $newValues): void { $changes = $this->buildUpdatedChanges($oldValues, $newValues); FormHistory::create([ 'causer_id' => Auth::id(), 'module' => $module, 'event' => 'updated', 'description' => "{$module} diperbarui", 'attribute_changes' => $changes ?: null, ]); } private function logDeleted(Model $model, string $module, array $oldValues): void { $changes = $this->buildDeletedChanges($oldValues); FormHistory::create([ 'causer_id' => Auth::id(), 'module' => $module, 'event' => 'deleted', 'description' => "{$module} dihapus", 'attribute_changes' => $changes ?: null, ]); } private function resolveItemName(array $item): string { $nameKeys = ['Nama Varian', 'Nama', 'name', 'variant', 'Variant', 'variant_name']; foreach ($nameKeys as $nameKey) { if (! empty($item[$nameKey])) { return (string) $item[$nameKey]; } } return '?'; } private function buildCreatedChanges(array $newValues): array { $changes = []; foreach ($newValues as $key => $value) { if (is_array($value)) { foreach ($value as $item) { if (is_array($item)) { $itemName = $this->resolveItemName($item); foreach ($item as $subKey => $subValue) { $changes["{$key}: {$itemName} — {$subKey}"] = ['new' => $subValue]; } } else { $changes["{$key}: {$item}"] = ['new' => null]; } } } else { $changes[$key] = ['new' => $value]; } } return $changes; } private function buildUpdatedChanges(array $oldValues, array $newValues): array { $changes = []; $allKeys = array_unique(array_merge(array_keys($oldValues), array_keys($newValues))); foreach ($allKeys as $key) { $old = $oldValues[$key] ?? null; $new = $newValues[$key] ?? null; if (is_array($old) && is_array($new)) { $nestedChanges = $this->diffArray($old, $new, $key); $changes = array_merge($changes, $nestedChanges); } elseif ($old !== $new) { $changes[$key] = array_filter([ 'old' => $old ?? null, 'new' => $new ?? null, ]); } } return $changes; } private function buildDeletedChanges(array $oldValues): array { $changes = []; foreach ($oldValues as $key => $value) { if (is_array($value)) { foreach ($value as $item) { if (is_array($item)) { $itemName = $this->resolveItemName($item); foreach ($item as $subKey => $subValue) { $changes["{$key}: {$itemName} — {$subKey}"] = ['old' => $subValue]; } } else { $changes["{$key}: {$item}"] = ['old' => null]; } } } else { $changes[$key] = ['old' => $value]; } } return $changes; } private function diffArray(array $old, array $new, string $prefix = ''): array { $changes = []; $isAssociative = ! empty($old) && array_is_list($old) === false; if ($isAssociative) { $allKeys = array_unique(array_merge(array_keys($old), array_keys($new))); foreach ($allKeys as $key) { $fullKey = "{$prefix}.{$key}"; if (isset($old[$key]) && is_array($old[$key]) && isset($new[$key]) && is_array($new[$key])) { $nested = $this->diffArray($old[$key], $new[$key], $fullKey); $changes = array_merge($changes, $nested); } else { $oldVal = $old[$key] ?? null; $newVal = $new[$key] ?? null; if ($oldVal !== $newVal) { $changes[$fullKey] = array_filter([ 'old' => $oldVal ?? null, 'new' => $newVal ?? null, ]); } } } } else { $max = max(count($old), count($new)); for ($i = 0; $i < $max; $i++) { $oldItem = $old[$i] ?? null; $newItem = $new[$i] ?? null; if (is_array($oldItem) && is_array($newItem)) { $itemName = $this->resolveItemName($newItem); $itemPrefix = "{$prefix}: {$itemName}"; $itemKeys = array_unique(array_merge(array_keys($oldItem), array_keys($newItem))); foreach ($itemKeys as $subKey) { $fullKey = "{$itemPrefix} — {$subKey}"; $oldVal = $oldItem[$subKey] ?? null; $newVal = $newItem[$subKey] ?? null; if ($oldVal !== $newVal) { $changes[$fullKey] = array_filter([ 'old' => $oldVal ?? null, 'new' => $newVal ?? null, ]); } } } elseif ($oldItem !== $newItem) { $itemName = $this->resolveItemName(is_array($oldItem) ? $oldItem : (is_array($newItem) ? $newItem : [])); $changes["{$prefix}: {$itemName}"] = array_filter([ 'old' => $oldItem, 'new' => $newItem, ]); } } } return $changes; } private function formatCurrency(int $value): string { return 'Rp ' . number_format($value, 0, ',', '.'); } private function formatDate(?string $date, string $format = 'l, d F Y'): ?string { return $date ? \Carbon\Carbon::parse($date)->translatedFormat($format) : null; } private function formatDateTime(?string $datetime): ?string { return $datetime ? \Carbon\Carbon::parse($datetime)->translatedFormat('l, d F Y H:i') : null; } private function formatBoolean(?bool $value): ?string { return $value === null ? null : ($value ? 'Ya' : 'Tidak'); } private function resolveRelation(Model $model, string $relation, string $attribute): ?string { $related = $model->{$relation}; return $related?->{$attribute} ?? null; } }