284 lines
14 KiB
PHP
284 lines
14 KiB
PHP
<?php
|
|
|
|
namespace App\Filament\Resources\PricelistFiles\RelationManagers;
|
|
|
|
use App\Enums\PricelistFileLineStatusEnum;
|
|
use App\Enums\PricelistFileStatusEnum;
|
|
use App\Services\PriceListService;
|
|
use Filament\Actions\ViewAction;
|
|
use Filament\Infolists\Components\TextEntry;
|
|
use Filament\Resources\RelationManagers\RelationManager;
|
|
use Filament\Schemas\Components\EmbeddedTable;
|
|
use Filament\Schemas\Components\Grid;
|
|
use Filament\Schemas\Components\RenderHook;
|
|
use Filament\Schemas\Components\Section;
|
|
use Filament\Schemas\Components\Text;
|
|
use Filament\Schemas\Schema;
|
|
use Filament\Support\Facades\FilamentView;
|
|
use Filament\Tables\Columns\TextColumn;
|
|
use Filament\Tables\Table;
|
|
use Filament\Tables\View\TablesRenderHook;
|
|
use Filament\View\PanelsRenderHook;
|
|
use Filament\Support\Enums\Width;
|
|
use Illuminate\Support\HtmlString;
|
|
use Filament\Schemas\Components\Tabs\Tab;
|
|
use Illuminate\Database\Eloquent\Builder;
|
|
|
|
class LinesRelationManager extends RelationManager
|
|
{
|
|
protected static string $relationship = 'lines';
|
|
|
|
protected static ?string $title = 'Feldolgozott sorok';
|
|
|
|
public function booted()
|
|
{
|
|
FilamentView::registerRenderHook(
|
|
TablesRenderHook::TOOLBAR_START,
|
|
fn () => $this->getTabsContentComponent()->container($this->makeSchema())->toHtml(),
|
|
scopes: static::class,
|
|
);
|
|
}
|
|
|
|
public function content(Schema $schema): Schema
|
|
{
|
|
return $schema
|
|
->components([
|
|
RenderHook::make(PanelsRenderHook::RESOURCE_RELATION_MANAGER_BEFORE),
|
|
EmbeddedTable::make(),
|
|
RenderHook::make(PanelsRenderHook::RESOURCE_RELATION_MANAGER_AFTER),
|
|
]);
|
|
}
|
|
|
|
public function getTabs(): array
|
|
{
|
|
$ownerRecord = $this->getOwnerRecord();
|
|
$counts = $ownerRecord->lines()
|
|
->selectRaw('status, count(*) as count')
|
|
->groupBy('status')
|
|
->pluck('count', 'status');
|
|
|
|
return [
|
|
'all' => Tab::make('Mind')
|
|
->badge($counts->sum()),
|
|
'hibas' => Tab::make('Hibás')
|
|
->icon(PricelistFileLineStatusEnum::error->icon())
|
|
->badgeColor(PricelistFileLineStatusEnum::error->color())
|
|
->badge($counts->get(PricelistFileLineStatusEnum::error->value, 0))
|
|
->modifyQueryUsing(fn (Builder $query) => $query->where('status', PricelistFileLineStatusEnum::error)),
|
|
'warning' => Tab::make('Figyelmeztetések')
|
|
->icon(PricelistFileLineStatusEnum::warning->icon())
|
|
->badgeColor(PricelistFileLineStatusEnum::warning->color())
|
|
->badge($counts->get(PricelistFileLineStatusEnum::warning->value, 0))
|
|
->modifyQueryUsing(fn (Builder $query) => $query->where('status', PricelistFileLineStatusEnum::warning)),
|
|
'uj' => Tab::make('Új')
|
|
->icon(PricelistFileLineStatusEnum::new_product->icon())
|
|
->badgeColor(PricelistFileLineStatusEnum::new_product->color())
|
|
->badge($counts->get(PricelistFileLineStatusEnum::new_product->value, 0))
|
|
->modifyQueryUsing(fn (Builder $query) => $query->where('status', PricelistFileLineStatusEnum::new_product)),
|
|
'modosult' => Tab::make('Módosult')
|
|
->icon(PricelistFileLineStatusEnum::updated->icon())
|
|
->badgeColor(PricelistFileLineStatusEnum::updated->color())
|
|
->badge($counts->get(PricelistFileLineStatusEnum::updated->value, 0))
|
|
->modifyQueryUsing(fn (Builder $query) => $query->where('status', PricelistFileLineStatusEnum::updated)),
|
|
];
|
|
}
|
|
|
|
public function form(Schema $schema): Schema
|
|
{
|
|
return $schema
|
|
->components([
|
|
Grid::make(1)
|
|
->schema([
|
|
Section::make('Módosulások')
|
|
->schema([
|
|
Text::make(fn ($record) => new HtmlString(
|
|
'<div class="grid grid-cols-1 gap-4">' .
|
|
collect($record->diff ?? [])->map(function ($d, $field) {
|
|
$old = $d['old_label'] ?? $d['old'] ?? 'N/A';
|
|
$new = $d['new_label'] ?? $d['new'] ?? 'N/A';
|
|
$label = $d['label'] ?? $field;
|
|
|
|
if ($old === '' || $old === null) $old = '<i>(üres)</i>';
|
|
if ($new === '' || $new === null) $new = '<i>(üres)</i>';
|
|
|
|
// Ár formázása ha árról van szó
|
|
if ($field === 'price') {
|
|
$old = number_format((float)($d['old'] ?? 0), 2, ',', ' ') . ' Ft';
|
|
$new = number_format((float)($d['new'] ?? 0), 2, ',', ' ') . ' Ft';
|
|
}
|
|
|
|
return "<div class='text-sm border-l-4 border-info-500 pl-3 py-1 bg-info-50/50 dark:bg-info-950/20'>
|
|
<div class='font-bold text-gray-700 dark:text-gray-300'>$label</div>
|
|
<div class='flex items-center gap-2 mt-1'>
|
|
<span class='text-gray-500 line-through'>$old</span>
|
|
<span class='text-gray-400'>→</span>
|
|
<span class='text-info-600 font-bold'>$new</span>
|
|
</div>
|
|
</div>";
|
|
})->join('') .
|
|
'</div>'
|
|
)),
|
|
])
|
|
->visible(fn ($record) => !empty($record->diff)),
|
|
|
|
Section::make('Validációs üzenetek')
|
|
->schema([
|
|
Text::make(fn ($record) => new HtmlString(
|
|
'<ul class="list-disc ml-5">' .
|
|
collect($record->validation_messages ?? [])->map(fn ($msg) => "<li>$msg</li>")->join('') .
|
|
'</ul>'
|
|
)),
|
|
])
|
|
->visible(fn ($record) => !empty($record->validation_messages)),
|
|
])
|
|
->columnSpan(1)
|
|
->visible(fn ($record) => !empty($record->diff) || !empty($record->validation_messages)),
|
|
|
|
Section::make('Adatok')
|
|
->schema([
|
|
Text::make(fn ($record) => new HtmlString(
|
|
'
|
|
<table class="min-w-full divide-y divide-gray-200 dark:divide-white/10">
|
|
<thead class="bg-gray-50 dark:bg-white/5">
|
|
<tr>
|
|
<th class="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider w-1/3">Oszlop</th>
|
|
<th class="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Érték</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody class="bg-white dark:bg-transparent divide-y divide-gray-200 dark:divide-white/10">
|
|
' . collect($record->payload ?? [])->map(function ($value, $key) {
|
|
$valStr = is_array($value) ? json_encode($value) : (string)$value;
|
|
return '<tr>
|
|
<td class="px-3 py-2 text-sm text-gray-500 dark:text-gray-400 font-medium bg-gray-50/50 dark:bg-white/5">' . e($key) . '</td>
|
|
<td class="px-3 py-2 text-sm text-gray-900 dark:text-gray-200 whitespace-normal break-words">' . e($valStr) . '</td>
|
|
</tr>';
|
|
})->join('') . '
|
|
</tbody>
|
|
</table>
|
|
'
|
|
)),
|
|
])
|
|
->columnSpan(fn ($record) => (empty($record->diff) && empty($record->validation_messages)) ? 2 : 1),
|
|
]);
|
|
}
|
|
|
|
public function table(Table $table): Table
|
|
{
|
|
return $table
|
|
->recordTitleAttribute('id')
|
|
->columns([
|
|
TextColumn::make('row_number')
|
|
->label('#')
|
|
->sortable()
|
|
->width('50px'),
|
|
|
|
TextColumn::make('status')
|
|
->label('Státusz')
|
|
->badge()
|
|
->color(fn (PricelistFileLineStatusEnum $state) => $state->color())
|
|
->formatStateUsing(fn (PricelistFileLineStatusEnum $state) => $state->label())
|
|
->icon(fn (PricelistFileLineStatusEnum $state) => $state->icon())
|
|
->sortable(),
|
|
|
|
TextColumn::make('product_info')
|
|
->label('Termék')
|
|
->description(fn ($record) => $record->payload[PriceListService::EXPECTED_HEADERS[4]] ?? '')
|
|
->getStateUsing(fn ($record) => $record->payload[PriceListService::EXPECTED_HEADERS[0]] ?? 'N/A')
|
|
->color(fn ($record) => !empty($record->diff) ? 'info' : null)
|
|
->wrap()
|
|
->searchable(query: function ($query, string $search) {
|
|
$sku = PriceListService::EXPECTED_HEADERS[0];
|
|
$name = PriceListService::EXPECTED_HEADERS[4];
|
|
$query->where("payload->$sku", 'like', "%{$search}%")
|
|
->orWhere("payload->$name", 'like', "%{$search}%");
|
|
}),
|
|
|
|
|
|
TextColumn::make('price_diff')
|
|
->label('Árváltozás')
|
|
->html()
|
|
->getStateUsing(function ($record) {
|
|
// Biztonságos elérés data_get-tel, mert a diff lehet null
|
|
$oldPrice = data_get($record->diff, 'price.old');
|
|
|
|
// Az új árat a diff-ből vesszük (már float), vagy a payload-ból (string lehet)
|
|
$newPriceRaw = data_get($record->diff, 'price.new') ?? data_get($record->payload, PriceListService::EXPECTED_HEADERS[14]);
|
|
|
|
// Normalizálás: szóközök eltávolítása és tizedesvessző cseréje
|
|
$newPrice = is_string($newPriceRaw)
|
|
? (float) str_replace([' ', ','], ['', '.'], $newPriceRaw)
|
|
: (float) $newPriceRaw;
|
|
|
|
// Ha nincs régi ár, vagy az nulla, akkor nincs százalékos számítás
|
|
if (empty($oldPrice) || (float)$oldPrice === 0.0) {
|
|
return number_format($newPrice, 2, ',', ' ') . ' Ft';
|
|
}
|
|
|
|
$oldPrice = (float) $oldPrice;
|
|
$diffPercent = (($newPrice / $oldPrice) - 1) * 100;
|
|
|
|
$colorClass = $diffPercent > 0 ? 'text-danger-600' : ($diffPercent < 0 ? 'text-success-600' : 'text-gray-600');
|
|
$icon = $diffPercent > 0 ? '↑' : ($diffPercent < 0 ? '↓' : '');
|
|
|
|
return sprintf(
|
|
'<div class="flex flex-col">
|
|
<span class="text-xs text-gray-500 line-through">%s Ft</span>
|
|
<span class="font-bold">%s Ft <span class="%s">%s %s%%</span></span>
|
|
</div>',
|
|
number_format($oldPrice, 2, ',', ' '),
|
|
number_format($newPrice, 2, ',', ' '),
|
|
$colorClass,
|
|
$icon,
|
|
number_format(abs($diffPercent), 1, ',', ' ')
|
|
);
|
|
})
|
|
->visible(fn ($livewire) => $livewire->getOwnerRecord()->status !== PricelistFileStatusEnum::todo),
|
|
|
|
TextColumn::make('validation_messages')
|
|
->label('Megjegyzés')
|
|
->listWithLineBreaks()
|
|
->bulleted()
|
|
->wrap()
|
|
->color(fn ($state) => collect($state)->contains(fn ($msg) => str_contains($msg, 'Hiányzó') || str_contains($msg, 'Ismeretlen') || str_contains($msg, 'Érvénytelen')) ? 'danger' : 'info')
|
|
->getStateUsing(function ($record) {
|
|
$messages = collect($record->validation_messages ?? [])->values();
|
|
|
|
$diffMessages = collect($record->diff ?? [])->map(function ($d, $field) {
|
|
$label = $d['label'] ?? $field;
|
|
$old = $d['old_label'] ?? $d['old'] ?? 'N/A';
|
|
$new = $d['new_label'] ?? $d['new'] ?? 'N/A';
|
|
|
|
if ($field === 'price') {
|
|
$old = number_format((float)($d['old'] ?? 0), 0, ',', ' ') . ' Ft';
|
|
$new = number_format((float)($d['new'] ?? 0), 0, ',', ' ') . ' Ft';
|
|
}
|
|
|
|
return "Módosult $label: $old -> $new";
|
|
})->values();
|
|
|
|
return $messages->merge($diffMessages)->toArray();
|
|
}),
|
|
])
|
|
->filters([
|
|
//
|
|
])
|
|
->headerActions([
|
|
//
|
|
])
|
|
->actions([
|
|
ViewAction::make()
|
|
->slideOver()
|
|
->modalWidth(Width::FourExtraLarge)
|
|
->iconButton()
|
|
->modalHeading(fn ($record) => "Sor száma: #{$record->row_number}")
|
|
->modalDescription(fn ($record) => "Státusz: " . $record->status->label()),
|
|
])
|
|
->bulkActions([
|
|
//
|
|
])
|
|
->defaultSort('row_number', 'asc')
|
|
->striped()
|
|
->poll('5s');
|
|
}
|
|
}
|