feat: tree/list view toggle for labor catalog
ListLabors now has a toggle button (persistat în localStorage) care comută între: - Listă: tabelul Filament grupat pe categorii (existent) - Arbore: accordion cu toate categoriile ca noduri colapsabile, fiecare cu tabelul manoperelor sale (Manoperă, Mod, Ore, Preț, Cost, link Editează); butoane Deschide tot / Închide tot Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,149 @@
|
||||
<x-filament-panels::page>
|
||||
|
||||
<div x-data="{
|
||||
mode: localStorage.getItem('labor_view_mode') || 'list',
|
||||
open: {},
|
||||
toggleAll(val) {
|
||||
@js($this->getLaborTree()->keys()->values())
|
||||
.forEach(k => this.open[k] = val);
|
||||
}
|
||||
}"
|
||||
x-init="$watch('mode', v => localStorage.setItem('labor_view_mode', v));
|
||||
$nextTick(() => { if (mode === 'tree') toggleAll(true); })">
|
||||
|
||||
{{-- ── View toggle bar ── --}}
|
||||
<div style="display:flex;justify-content:flex-end;gap:8px;margin-bottom:14px;align-items:center;">
|
||||
|
||||
{{-- Expand / Collapse all (only in tree mode) --}}
|
||||
<span x-show="mode === 'tree'" style="display:flex;gap:6px;">
|
||||
<button type="button" @click="toggleAll(true)"
|
||||
style="font-size:.8rem;color:#6b7280;background:none;border:none;cursor:pointer;padding:4px 8px;">
|
||||
↕ {{ __('Deschide tot') }}
|
||||
</button>
|
||||
<button type="button" @click="toggleAll(false)"
|
||||
style="font-size:.8rem;color:#6b7280;background:none;border:none;cursor:pointer;padding:4px 8px;">
|
||||
↕ {{ __('Închide tot') }}
|
||||
</button>
|
||||
</span>
|
||||
|
||||
{{-- Toggle buttons --}}
|
||||
<div style="display:inline-flex;border:1px solid #e5e7eb;border-radius:8px;overflow:hidden;box-shadow:0 1px 2px rgba(0,0,0,.05);">
|
||||
<button type="button" @click="mode='list'"
|
||||
:style="mode==='list'
|
||||
? 'background:#2563eb;color:#fff;'
|
||||
: 'background:#fff;color:#374151;'"
|
||||
style="padding:6px 16px;border:none;cursor:pointer;font-size:.875rem;font-weight:500;display:flex;align-items:center;gap:6px;transition:background .15s;">
|
||||
<svg style="width:15px;height:15px;flex-shrink:0;" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" d="M4 6h16M4 12h16M4 18h16"/>
|
||||
</svg>
|
||||
{{ __('Listă') }}
|
||||
</button>
|
||||
<button type="button" @click="mode='tree'; $nextTick(() => toggleAll(true))"
|
||||
:style="mode==='tree'
|
||||
? 'background:#2563eb;color:#fff;'
|
||||
: 'background:#fff;color:#374151;'"
|
||||
style="padding:6px 16px;border:none;cursor:pointer;font-size:.875rem;font-weight:500;display:flex;align-items:center;gap:6px;transition:background .15s;border-left:1px solid #e5e7eb;">
|
||||
<svg style="width:15px;height:15px;flex-shrink:0;" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" d="M3 7h4M3 12h8M3 17h12M9 7h12M13 12h8M17 17h4"/>
|
||||
</svg>
|
||||
{{ __('Arbore') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- ── LIST mode: standard Filament table ── --}}
|
||||
<div x-show="mode === 'list'">
|
||||
{{ $this->table }}
|
||||
</div>
|
||||
|
||||
{{-- ── TREE mode: category accordion ── --}}
|
||||
<div x-show="mode === 'tree'" x-cloak style="display:flex;flex-direction:column;gap:6px;">
|
||||
|
||||
@php $tree = $this->getLaborTree(); @endphp
|
||||
|
||||
@forelse ($tree as $cat)
|
||||
<div x-data="{ key: '{{ $cat->name }}' }"
|
||||
style="border:1px solid #e5e7eb;border-radius:10px;overflow:hidden;background:#fff;">
|
||||
|
||||
{{-- Category header --}}
|
||||
<button type="button"
|
||||
@click="open[key] = !open[key]"
|
||||
style="width:100%;display:flex;align-items:center;justify-content:space-between;
|
||||
padding:11px 16px;background:none;border:none;cursor:pointer;text-align:left;">
|
||||
<span style="display:flex;align-items:center;gap:10px;">
|
||||
<span style="font-weight:600;font-size:.95rem;color:#111827;">{{ $cat->label }}</span>
|
||||
<span style="font-size:.75rem;color:#9ca3af;background:#f3f4f6;border-radius:20px;padding:1px 8px;">
|
||||
{{ $cat->items->count() }}
|
||||
</span>
|
||||
</span>
|
||||
<svg :style="open[key] ? 'transform:rotate(180deg)' : ''"
|
||||
style="width:16px;height:16px;color:#9ca3af;transition:transform .2s;flex-shrink:0;"
|
||||
fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5">
|
||||
<path stroke-linecap="round" d="M19 9l-7 7-7-7"/>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
{{-- Labor items --}}
|
||||
<div x-show="open[key]" x-cloak
|
||||
style="border-top:1px solid #f3f4f6;">
|
||||
<table style="width:100%;border-collapse:collapse;font-size:.875rem;">
|
||||
<thead>
|
||||
<tr style="background:#f9fafb;color:#6b7280;font-size:.75rem;text-transform:uppercase;letter-spacing:.04em;">
|
||||
<th style="padding:7px 16px;text-align:left;font-weight:500;">{{ __('Manoperă') }}</th>
|
||||
<th style="padding:7px 8px;text-align:center;font-weight:500;">{{ __('Mod') }}</th>
|
||||
<th style="padding:7px 8px;text-align:right;font-weight:500;">{{ __('Ore') }}</th>
|
||||
<th style="padding:7px 8px;text-align:right;font-weight:500;">{{ __('Preț') }}</th>
|
||||
<th style="padding:7px 8px;text-align:right;font-weight:500;">{{ __('Cost') }}</th>
|
||||
<th style="padding:7px 16px;text-align:right;font-weight:500;"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach ($cat->items as $labor)
|
||||
<tr style="border-top:1px solid #f3f4f6;"
|
||||
x-on:mouseenter="$el.style.background='#f9fafb'"
|
||||
x-on:mouseleave="$el.style.background=''">
|
||||
<td style="padding:8px 16px;">
|
||||
<span style="font-weight:500;color:#111827;">{{ $labor->label() }}</span>
|
||||
@if ($labor->code)
|
||||
<span style="font-size:.75rem;color:#9ca3af;margin-left:6px;">{{ $labor->code }}</span>
|
||||
@endif
|
||||
@if (! $labor->is_active)
|
||||
<span style="font-size:.7rem;background:#fee2e2;color:#dc2626;border-radius:4px;padding:1px 5px;margin-left:6px;">{{ __('Inactiv') }}</span>
|
||||
@endif
|
||||
</td>
|
||||
<td style="padding:8px;text-align:center;">
|
||||
<span style="font-size:.75rem;background:{{ $labor->pricing_mode === 'fixed' ? '#dbeafe' : '#f3f4f6' }};
|
||||
color:{{ $labor->pricing_mode === 'fixed' ? '#1d4ed8' : '#6b7280' }};
|
||||
border-radius:4px;padding:2px 7px;">
|
||||
{{ $labor->pricing_mode === 'fixed' ? __('Fix') : __('Pe oră') }}
|
||||
</span>
|
||||
</td>
|
||||
<td style="padding:8px;text-align:right;color:#374151;">
|
||||
{{ $labor->pricing_mode === 'hourly' ? rtrim(rtrim(number_format((float)$labor->hours, 2), '0'), '.') : '—' }}
|
||||
</td>
|
||||
<td style="padding:8px;text-align:right;font-weight:500;color:#111827;">
|
||||
{{ number_format((float)($labor->pricing_mode === 'fixed' ? $labor->fixed_price : $labor->price), 2, '.', ' ') }} MDL
|
||||
</td>
|
||||
<td style="padding:8px;text-align:right;color:#6b7280;font-size:.82rem;">
|
||||
{{ $labor->cost_price > 0 ? number_format((float)$labor->cost_price, 2, '.', ' ') . ' MDL' : '—' }}
|
||||
</td>
|
||||
<td style="padding:8px 16px;text-align:right;white-space:nowrap;">
|
||||
<a href="{{ \App\Filament\Tenant\Resources\LaborResource::getUrl('edit', ['record' => $labor->id]) }}"
|
||||
style="font-size:.8rem;color:#2563eb;text-decoration:none;margin-right:10px;">
|
||||
{{ __('Editează') }}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
@empty
|
||||
<div style="text-align:center;color:#9ca3af;padding:32px;">{{ __('Nicio manoperă găsită.') }}</div>
|
||||
@endforelse
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</x-filament-panels::page>
|
||||
Reference in New Issue
Block a user