150 lines
4.1 KiB
PHP
150 lines
4.1 KiB
PHP
<?php
|
|
|
|
namespace App\Services\System;
|
|
|
|
use Illuminate\Support\Str;
|
|
|
|
class ActivityFormatter
|
|
{
|
|
/**
|
|
* Get a friendly name for the model.
|
|
*/
|
|
public static function getFriendlyModelName(?string $model): string
|
|
{
|
|
if (! $model) {
|
|
return 'System';
|
|
}
|
|
|
|
$className = class_basename($model);
|
|
|
|
// Custom mapping for specific models
|
|
$mapping = [
|
|
'SystemSetting' => 'System Config',
|
|
'User' => 'User Profile',
|
|
'Role' => 'Access Role',
|
|
'Permission' => 'Access Permission',
|
|
'MobileSetting' => 'Mobile Config',
|
|
];
|
|
|
|
return $mapping[$className] ?? Str::headline($className);
|
|
}
|
|
|
|
/**
|
|
* Get badge class for the event.
|
|
*/
|
|
public static function getEventBadgeClass(string $event): string
|
|
{
|
|
return match (strtolower($event)) {
|
|
'created' => 'text-bg-success',
|
|
'updated' => 'text-bg-warning',
|
|
'deleted' => 'text-bg-danger',
|
|
'restored' => 'text-bg-info',
|
|
'login', 'login_attempt' => 'text-bg-info',
|
|
'logout' => 'text-bg-secondary',
|
|
'password_changed', 'password reset' => 'text-bg-primary',
|
|
default => 'text-bg-theme-1',
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Get icon for the event.
|
|
*/
|
|
public static function getEventIcon(string $event): string
|
|
{
|
|
return match (strtolower($event)) {
|
|
'created' => 'bi-plus-circle',
|
|
'updated' => 'bi-pencil-square',
|
|
'deleted' => 'bi-trash',
|
|
'restored' => 'bi-arrow-counterclockwise',
|
|
'login', 'login_attempt' => 'bi-box-arrow-in-right',
|
|
'logout' => 'bi-box-arrow-right',
|
|
'password_changed', 'password reset' => 'bi-key',
|
|
default => 'bi-info-circle',
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Formats the changes between old and new properties.
|
|
*/
|
|
public static function formatChanges(array $properties): array
|
|
{
|
|
$old = $properties['old'] ?? [];
|
|
$new = $properties['attributes'] ?? [];
|
|
|
|
$changes = [];
|
|
|
|
// If it's a "created" event, show all attributes
|
|
if (empty($old) && ! empty($new)) {
|
|
foreach ($new as $key => $value) {
|
|
if (self::isSensitive($key)) {
|
|
continue;
|
|
}
|
|
$changes[] = [
|
|
'field' => Str::headline($key),
|
|
'old' => null,
|
|
'new' => self::formatValue($value),
|
|
];
|
|
}
|
|
|
|
return $changes;
|
|
}
|
|
|
|
// For updates, show only changed fields
|
|
foreach ($new as $key => $value) {
|
|
if (self::isSensitive($key)) {
|
|
continue;
|
|
}
|
|
|
|
$oldValue = $old[$key] ?? null;
|
|
|
|
// Loose comparison to handle type juggling from DB
|
|
if ($oldValue != $value) {
|
|
$changes[] = [
|
|
'field' => Str::headline($key),
|
|
'old' => self::formatValue($oldValue),
|
|
'new' => self::formatValue($value),
|
|
];
|
|
}
|
|
}
|
|
|
|
return $changes;
|
|
}
|
|
|
|
private static function isSensitive(string $key): bool
|
|
{
|
|
$sensitive = ['password', 'remember_token', 'secret', 'key', 'token', '2fa_secret'];
|
|
foreach ($sensitive as $s) {
|
|
if (str_contains(strtolower($key), $s)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private static function formatValue(mixed $value): string
|
|
{
|
|
if (is_null($value)) {
|
|
return 'NULL';
|
|
}
|
|
if (is_bool($value)) {
|
|
return $value ? 'TRUE' : 'FALSE';
|
|
}
|
|
|
|
if (is_array($value) || is_object($value)) {
|
|
return json_encode($value, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
|
|
}
|
|
|
|
if ($value === '') {
|
|
return '[empty]';
|
|
}
|
|
|
|
// Truncate long values but keep it readable
|
|
if (is_string($value) && strlen($value) > 200) {
|
|
return substr($value, 0, 197).'...';
|
|
}
|
|
|
|
return (string) $value;
|
|
}
|
|
}
|