diff --git a/docs/fields.md b/docs/fields.md
index 9bdfa7d..c1cd099 100644
--- a/docs/fields.md
+++ b/docs/fields.md
@@ -107,6 +107,19 @@ $logger->fields([
->keyValue(differenceOnly: true)
->label('Attributes'),
])
+```
+
+ #### Key-Value with fields
+```php
+$logger->fields([
+ Field::make('recipient')
+ ->hasOne('recipient')
+ ->keyValue([
+ Field::make('recipient.full_name'),
+ Field::make('recipient.phone'),
+ Field::make('recipient.shipping_provider'),
+ ]),
+])
```
![Screenshot](./assets/images/key-value-screenshot.png)
diff --git a/resources/dist/filament-activity-log.css b/resources/dist/filament-activity-log.css
index 81a05ef..e4042af 100644
--- a/resources/dist/filament-activity-log.css
+++ b/resources/dist/filament-activity-log.css
@@ -1 +1 @@
-.right-4{right:1rem}.top-1{top:.25rem}.top-20{top:5rem}.mr-2{margin-right:.5rem}.w-fit{width:-moz-fit-content;width:fit-content}.\!whitespace-normal{white-space:normal!important}.break-all{word-break:break-all}.bg-blue-50\/70{background-color:#eff6ffb3}.bg-gray-100\/30{background-color:rgba(var(--gray-100),.3)}.bg-gray-50\/70{background-color:rgba(var(--gray-50),.7)}.bg-green-50\/70{background-color:#f0fdf4b3}.bg-orange-50\/70{background-color:#fff7edb3}.bg-red-50\/70{background-color:#fef2f2b3}.\!py-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.align-top{vertical-align:top}.text-blue-700{--tw-text-opacity:1;color:rgb(29 78 216/var(--tw-text-opacity))}.text-green-700{--tw-text-opacity:1;color:rgb(21 128 61/var(--tw-text-opacity))}.text-orange-700{--tw-text-opacity:1;color:rgb(194 65 12/var(--tw-text-opacity))}.text-red-700{--tw-text-opacity:1;color:rgb(185 28 28/var(--tw-text-opacity))}.shadow,.shadow-md{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-md{--tw-shadow:0 4px 6px -1px #0000001a,0 2px 4px -2px #0000001a;--tw-shadow-colored:0 4px 6px -1px var(--tw-shadow-color),0 2px 4px -2px var(--tw-shadow-color)}.group:hover .group-hover\:opacity-100{opacity:1}:is([dir=rtl] .rtl\:divide-x-reverse)>:not([hidden])~:not([hidden]){--tw-divide-x-reverse:1}:is(.dark .dark\:divide-white\/20)>:not([hidden])~:not([hidden]){border-color:#fff3}:is(.dark .dark\:border-blue-600){--tw-border-opacity:1;border-color:rgb(37 99 235/var(--tw-border-opacity))}:is(.dark .dark\:border-gray-600){--tw-border-opacity:1;border-color:rgba(var(--gray-600),var(--tw-border-opacity))}:is(.dark .dark\:border-green-600){--tw-border-opacity:1;border-color:rgb(22 163 74/var(--tw-border-opacity))}:is(.dark .dark\:border-orange-600){--tw-border-opacity:1;border-color:rgb(234 88 12/var(--tw-border-opacity))}:is(.dark .dark\:border-red-600){--tw-border-opacity:1;border-color:rgb(220 38 38/var(--tw-border-opacity))}:is(.dark .dark\:bg-blue-100\/10){background-color:#dbeafe1a}:is(.dark .dark\:bg-gray-100\/10){background-color:rgba(var(--gray-100),.1)}:is(.dark .dark\:bg-gray-700){--tw-bg-opacity:1;background-color:rgba(var(--gray-700),var(--tw-bg-opacity))}:is(.dark .dark\:bg-gray-800){--tw-bg-opacity:1;background-color:rgba(var(--gray-800),var(--tw-bg-opacity))}:is(.dark .dark\:bg-gray-900){--tw-bg-opacity:1;background-color:rgba(var(--gray-900),var(--tw-bg-opacity))}:is(.dark .dark\:bg-green-100\/10){background-color:#dcfce71a}:is(.dark .dark\:bg-orange-100\/10){background-color:#ffedd51a}:is(.dark .dark\:bg-red-100\/10){background-color:#fee2e21a}:is(.dark .dark\:bg-transparent){background-color:initial}:is(.dark .dark\:text-blue-400){--tw-text-opacity:1;color:rgb(96 165 250/var(--tw-text-opacity))}:is(.dark .dark\:text-gray-200){--tw-text-opacity:1;color:rgba(var(--gray-200),var(--tw-text-opacity))}:is(.dark .dark\:text-gray-300){--tw-text-opacity:1;color:rgba(var(--gray-300),var(--tw-text-opacity))}:is(.dark .dark\:text-gray-400){--tw-text-opacity:1;color:rgba(var(--gray-400),var(--tw-text-opacity))}:is(.dark .dark\:text-green-400){--tw-text-opacity:1;color:rgb(74 222 128/var(--tw-text-opacity))}:is(.dark .dark\:text-orange-400){--tw-text-opacity:1;color:rgb(251 146 60/var(--tw-text-opacity))}:is(.dark .dark\:text-red-400){--tw-text-opacity:1;color:rgb(248 113 113/var(--tw-text-opacity))}:is(.dark .dark\:ring-white\/20){--tw-ring-color:#fff3}
\ No newline at end of file
+.right-4{right:1rem}.top-1{top:.25rem}.top-20{top:5rem}.mr-2{margin-right:.5rem}.w-\[1\%\]{width:1%}.w-fit{width:-moz-fit-content;width:fit-content}.max-w-0{max-width:0}.\!table-fixed{table-layout:fixed!important}.overflow-x-scroll{overflow-x:scroll}.\!whitespace-normal{white-space:normal!important}.break-all{word-break:break-all}.bg-blue-50\/70{background-color:#eff6ffb3}.bg-gray-100\/30{background-color:rgba(var(--gray-100),.3)}.bg-gray-50\/70{background-color:rgba(var(--gray-50),.7)}.bg-green-50\/70{background-color:#f0fdf4b3}.bg-orange-50\/70{background-color:#fff7edb3}.bg-red-50\/70{background-color:#fef2f2b3}.\!p-2{padding:.5rem!important}.\!py-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.align-top{vertical-align:top}.text-blue-700{--tw-text-opacity:1;color:rgb(29 78 216/var(--tw-text-opacity))}.text-green-700{--tw-text-opacity:1;color:rgb(21 128 61/var(--tw-text-opacity))}.text-orange-700{--tw-text-opacity:1;color:rgb(194 65 12/var(--tw-text-opacity))}.text-red-700{--tw-text-opacity:1;color:rgb(185 28 28/var(--tw-text-opacity))}.shadow,.shadow-md{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-md{--tw-shadow:0 4px 6px -1px #0000001a,0 2px 4px -2px #0000001a;--tw-shadow-colored:0 4px 6px -1px var(--tw-shadow-color),0 2px 4px -2px var(--tw-shadow-color)}.group:hover .group-hover\:opacity-100{opacity:1}:is([dir=rtl] .rtl\:divide-x-reverse)>:not([hidden])~:not([hidden]){--tw-divide-x-reverse:1}:is(.dark .dark\:divide-white\/20)>:not([hidden])~:not([hidden]){border-color:#fff3}:is(.dark .dark\:border-blue-600){--tw-border-opacity:1;border-color:rgb(37 99 235/var(--tw-border-opacity))}:is(.dark .dark\:border-gray-600){--tw-border-opacity:1;border-color:rgba(var(--gray-600),var(--tw-border-opacity))}:is(.dark .dark\:border-green-600){--tw-border-opacity:1;border-color:rgb(22 163 74/var(--tw-border-opacity))}:is(.dark .dark\:border-orange-600){--tw-border-opacity:1;border-color:rgb(234 88 12/var(--tw-border-opacity))}:is(.dark .dark\:border-red-600){--tw-border-opacity:1;border-color:rgb(220 38 38/var(--tw-border-opacity))}:is(.dark .dark\:border-white\/5){border-color:#ffffff0d}:is(.dark .dark\:bg-blue-100\/10){background-color:#dbeafe1a}:is(.dark .dark\:bg-gray-100\/10){background-color:rgba(var(--gray-100),.1)}:is(.dark .dark\:bg-gray-700){--tw-bg-opacity:1;background-color:rgba(var(--gray-700),var(--tw-bg-opacity))}:is(.dark .dark\:bg-gray-800){--tw-bg-opacity:1;background-color:rgba(var(--gray-800),var(--tw-bg-opacity))}:is(.dark .dark\:bg-gray-900){--tw-bg-opacity:1;background-color:rgba(var(--gray-900),var(--tw-bg-opacity))}:is(.dark .dark\:bg-gray-900\/20){background-color:rgba(var(--gray-900),.2)}:is(.dark .dark\:bg-green-100\/10){background-color:#dcfce71a}:is(.dark .dark\:bg-orange-100\/10){background-color:#ffedd51a}:is(.dark .dark\:bg-red-100\/10){background-color:#fee2e21a}:is(.dark .dark\:bg-transparent){background-color:initial}:is(.dark .dark\:text-blue-400){--tw-text-opacity:1;color:rgb(96 165 250/var(--tw-text-opacity))}:is(.dark .dark\:text-gray-200){--tw-text-opacity:1;color:rgba(var(--gray-200),var(--tw-text-opacity))}:is(.dark .dark\:text-gray-300){--tw-text-opacity:1;color:rgba(var(--gray-300),var(--tw-text-opacity))}:is(.dark .dark\:text-gray-400){--tw-text-opacity:1;color:rgba(var(--gray-400),var(--tw-text-opacity))}:is(.dark .dark\:text-green-400){--tw-text-opacity:1;color:rgb(74 222 128/var(--tw-text-opacity))}:is(.dark .dark\:text-orange-400){--tw-text-opacity:1;color:rgb(251 146 60/var(--tw-text-opacity))}:is(.dark .dark\:text-red-400){--tw-text-opacity:1;color:rgb(248 113 113/var(--tw-text-opacity))}:is(.dark .dark\:ring-white\/20){--tw-ring-color:#fff3}
\ No newline at end of file
diff --git a/resources/views/components/badge.blade.php b/resources/views/components/badge.blade.php
index e23c8bd..d5cb790 100644
--- a/resources/views/components/badge.blade.php
+++ b/resources/views/components/badge.blade.php
@@ -2,6 +2,8 @@
use Filament\Support\Contracts\HasColor;
use Filament\Support\Contracts\HasLabel;
use Filament\Support\Contracts\HasIcon;
+
+ $isHtmlAllowed = $field->isHtmlAllowed();
@endphp
@@ -10,8 +12,13 @@
- {{ $label }}
+ @if ($isHtmlAllowed)
+ {!! $label !!}
+ @else
+ {{ $label }}
+ @endif
@endforeach
@elseif($field->is('enum'))
@@ -25,16 +32,25 @@ class="w-fit"
:color="$color"
:icon="$icon"
class="w-fit"
+ :tooltip="$label"
>
- {{ $label }}
+ @if ($isHtmlAllowed)
+ {!! $label !!}
+ @else
+ {{ $label }}
+ @endif
@else
- {{ $value }}
+ @if ($isHtmlAllowed)
+ {!! $value !!}
+ @else
+ {{ $value }}
+ @endif
@endif
-
diff --git a/resources/views/components/default.blade.php b/resources/views/components/default.blade.php
index 205727d..1a5648e 100644
--- a/resources/views/components/default.blade.php
+++ b/resources/views/components/default.blade.php
@@ -1,5 +1,19 @@
+@php
+ $isHtmlAllowed = $field->isHtmlAllowed();
+@endphp
+
@if (is_array($value))
- {{ json_encode($value, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) }}
+
+ @if ($isHtmlAllowed)
+{!! json_encode($value, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) !!}
@else
- {{ $value }}
+{{ json_encode($value, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) }}
+@endif
+
+@else
+ @if ($isHtmlAllowed)
+ {!! $value !!}
+ @else
+ {{ $value }}
+ @endif
@endif
diff --git a/resources/views/components/key-value.blade.php b/resources/views/components/key-value.blade.php
index 7fb9ed0..9486010 100644
--- a/resources/views/components/key-value.blade.php
+++ b/resources/views/components/key-value.blade.php
@@ -1,17 +1,60 @@
-
-
-
- @foreach ((array) $value as $key => $_value)
-
-
- {{ $key }}
- |
+@if (!empty($value))
+ @php
+ $hasFields = $field->keyValue instanceof \Noxo\FilamentActivityLog\ResourceLogger\Types\KeyValueField;
+ if ($hasFields) {
+ $fields = $field->keyValue->getFields();
+ }
+ $isHtmlAllowed = $field->isHtmlAllowed();
+ @endphp
-
- {{ $_value }}
- |
-
- @endforeach
-
-
-
+
+
+
+ @if ($hasFields)
+
+ @foreach ($fields as $key => $keyValueField)
+ @php
+ if (!array_key_exists($keyValueField->name, $value)) {
+ continue;
+ }
+ $rawValue = $value[$keyValueField->name];
+ $dispayValue = $keyValueField->display($rawValue);
+ @endphp
+
+
+
+ {{ $keyValueField->getLabel() }}
+ |
+
+
+ @if ($isHtmlAllowed)
+ {!! $dispayValue !!}
+ @else
+ {{ $dispayValue }}
+ @endif
+ |
+
+ @endforeach
+
+ @else
+
+ @foreach ((array) $value as $key => $_value)
+
+
+ {{ $key }}
+ |
+
+
+ @if ($isHtmlAllowed)
+ {!! $_value !!}
+ @else
+ {{ $_value }}
+ @endif
+ |
+
+ @endforeach
+
+ @endif
+
+
+@endif
diff --git a/resources/views/components/table.blade.php b/resources/views/components/table.blade.php
index 93cd0b4..bd6e329 100644
--- a/resources/views/components/table.blade.php
+++ b/resources/views/components/table.blade.php
@@ -1,22 +1,40 @@
@if (!empty($value))
-
-
- @foreach (array_keys($value[0]) as $key)
-
- {{ $key }}
-
- @endforeach
-
+ @php
+ $fields = $field->table->getFields();
+ $isHtmlAllowed = $field->isHtmlAllowed();
+ @endphp
+
+
+
+
+
+ @foreach ($fields as $field)
+
+ {{ $field->getLabel() }}
+
+ @endforeach
+
+
- @foreach ($value as $item)
- $loop->even])>
- @foreach ($item as $_value)
-
- {{ $_value }}
-
- @endforeach
-
- @endforeach
-
+ @foreach ($value as $item)
+ $loop->even])>
+ @foreach ($fields as $field)
+
+ @php
+ $rawValue = $item[$field->name] ?? data_get($item, $field->name);
+ $dispayValue = $field->display($rawValue);
+ @endphp
+
+ @if ($isHtmlAllowed)
+ {!! $dispayValue !!}
+ @else
+ {{ $dispayValue }}
+ @endif
+
+ @endforeach
+
+ @endforeach
+
+
@endif
diff --git a/resources/views/list/tables/default.blade.php b/resources/views/list/tables/default.blade.php
index 577a3b0..c51f466 100644
--- a/resources/views/list/tables/default.blade.php
+++ b/resources/views/list/tables/default.blade.php
@@ -1,12 +1,21 @@
-
+
-
+
@lang('filament-activity-log::activities.table.field')
-
+
@lang('filament-activity-log::activities.table.old')
-
+
@lang('filament-activity-log::activities.table.new')
@@ -22,10 +31,7 @@
@endphp
$loop->even])>
-
+
{{ $field->getLabel() }}
@@ -41,17 +47,11 @@ class="px-4 py-2 align-top break-all !whitespace-normal"
]) }}
@else
-
+
{{ $field->display($oldValue) }}
-
+
{{ $field->display($newValue) }}
@endif
diff --git a/resources/views/list/tables/simple.blade.php b/resources/views/list/tables/simple.blade.php
index c08f8e5..c052232 100644
--- a/resources/views/list/tables/simple.blade.php
+++ b/resources/views/list/tables/simple.blade.php
@@ -1,9 +1,15 @@
-
+
-
+
@lang('filament-activity-log::activities.table.field')
-
+
@lang('filament-activity-log::activities.table.value')
@@ -17,17 +23,11 @@
@endphp
$loop->even])>
-
+
{{ $field->getLabel() }}
-
+
{{ $field->display($value) }}
diff --git a/src/Loggers/Logger.php b/src/Loggers/Logger.php
index 117731f..5c8ba45 100644
--- a/src/Loggers/Logger.php
+++ b/src/Loggers/Logger.php
@@ -39,9 +39,10 @@ public function __construct(Model $newModel = null, Model $oldModel = null)
*/
public function through(Closure $callback): static
{
- $callback(clone $this->oldModel);
+ $callback(clone $this->newModel);
- $this->newModel = $this->oldModel->fresh();
+ $this->oldModel = clone $this->newModel;
+ $this->newModel->refresh();
return $this;
}
diff --git a/src/ResourceLogger/Concerns/Types/KeyValue.php b/src/ResourceLogger/Concerns/Types/KeyValue.php
index 618a161..42f5669 100644
--- a/src/ResourceLogger/Concerns/Types/KeyValue.php
+++ b/src/ResourceLogger/Concerns/Types/KeyValue.php
@@ -2,17 +2,33 @@
namespace Noxo\FilamentActivityLog\ResourceLogger\Concerns\Types;
+use Noxo\FilamentActivityLog\ResourceLogger\Types\KeyValueField;
+
trait KeyValue
{
+ public ?KeyValueField $keyValue = null;
public bool $keyValueDifferenceOnly = true;
- public function keyValue(bool $differenceOnly = true): static
- {
+ public function keyValue(
+ array $fields = [],
+ bool $differenceOnly = true,
+ ): static {
$this->type('key-value');
$this->view('key-value');
+ $this->formatStateUsing('array');
$this->keyValueDifferenceOnly = $differenceOnly;
- $this->formatStateUsing('array');
+ if (! empty($fields)) {
+ $this->keyValue = KeyValueField::make($fields);
+
+ $this->resolveStateUsing(function ($record) {
+ $fields = collect($this->keyValue->getFields());
+
+ return $fields->mapWithKeys(fn ($field) => [
+ $field->name => $field->getStorableValue($record),
+ ])->toArray();
+ });
+ }
return $this;
}
@@ -23,14 +39,33 @@ public function resolveKeyValueDifference(mixed $array1, mixed $array2): array
return [$array1, $array2];
}
- foreach ($array1 as $key1 => $row1) {
- foreach ($array2 as $key2 => $row2) {
- if ($row1 === $row2) {
- unset($array1[$key1], $array2[$key2]);
+ $diff1 = $this->arrayRecursiveDiff($array1, $array2);
+ $diff2 = $this->arrayRecursiveDiff($array2, $array1);
+
+ return [$diff1, $diff2];
+ }
+
+ private function arrayRecursiveDiff(array $aArray1, array $aArray2): array
+ {
+ $aReturn = [];
+
+ foreach ($aArray1 as $mKey => $mValue) {
+ if (array_key_exists($mKey, $aArray2)) {
+ if (is_array($mValue)) {
+ $aRecursiveDiff = $this->arrayRecursiveDiff($mValue, $aArray2[$mKey]);
+ if (count($aRecursiveDiff)) {
+ $aReturn[$mKey] = $aRecursiveDiff;
+ }
+ } else {
+ if ($mValue != $aArray2[$mKey]) {
+ $aReturn[$mKey] = $mValue;
+ }
}
+ } else {
+ $aReturn[$mKey] = $mValue;
}
}
- return [$array1, $array2];
+ return $aReturn;
}
}
diff --git a/src/ResourceLogger/Concerns/Types/Table.php b/src/ResourceLogger/Concerns/Types/Table.php
index 2b5a34a..e638328 100644
--- a/src/ResourceLogger/Concerns/Types/Table.php
+++ b/src/ResourceLogger/Concerns/Types/Table.php
@@ -2,17 +2,41 @@
namespace Noxo\FilamentActivityLog\ResourceLogger\Concerns\Types;
+use Closure;
+use Noxo\FilamentActivityLog\ResourceLogger\Types\TableField;
+
trait Table
{
+ public ?TableField $table;
+
public bool $tableDifferenceOnly = true;
- public function table(bool $differenceOnly = true): static
- {
+ public function table(
+ array $fields,
+ Closure $resolveRecords = null,
+ bool $differenceOnly = true,
+ ): static {
$this->type('table');
$this->view('table');
+ $this->table = TableField::make($fields);
$this->tableDifferenceOnly = $differenceOnly;
$this->formatStateUsing('array');
+ $this->resolveStateUsing(function ($record) use ($resolveRecords) {
+ $records = collect(
+ is_null($resolveRecords)
+ ? data_get($record, $this->name)
+ : $resolveRecords($record)
+ );
+
+ $fields = collect($this->table->getFields());
+
+ return $records->map(function ($record) use ($fields) {
+ return $fields->mapWithKeys(fn ($field) => [
+ $field->name => $field->getStorableValue($record),
+ ])->toArray();
+ })->toArray();
+ });
return $this;
}
diff --git a/src/ResourceLogger/Field.php b/src/ResourceLogger/Field.php
index 12becdd..3c56bf3 100644
--- a/src/ResourceLogger/Field.php
+++ b/src/ResourceLogger/Field.php
@@ -3,6 +3,8 @@
namespace Noxo\FilamentActivityLog\ResourceLogger;
use DragonCode\Support\Concerns\Makeable;
+use Filament\Forms\Components\Concerns\CanAllowHtml;
+use Filament\Support\Concerns\EvaluatesClosures;
class Field
{
@@ -25,6 +27,8 @@ class Field
use Concerns\Types\Relation;
use Concerns\Types\Table;
use Makeable;
+ use CanAllowHtml;
+ use EvaluatesClosures;
public function __construct(string $name, string $type = null)
{
diff --git a/src/ResourceLogger/Types/KeyValueField.php b/src/ResourceLogger/Types/KeyValueField.php
new file mode 100644
index 0000000..f55a4e7
--- /dev/null
+++ b/src/ResourceLogger/Types/KeyValueField.php
@@ -0,0 +1,17 @@
+fields($fields);
+ }
+}
diff --git a/src/ResourceLogger/Types/TableField.php b/src/ResourceLogger/Types/TableField.php
new file mode 100644
index 0000000..25b4c09
--- /dev/null
+++ b/src/ResourceLogger/Types/TableField.php
@@ -0,0 +1,17 @@
+fields($fields);
+ }
+}