feat(work-orders): new Mitchell1-style dashboard view (Phase 1 — layout + tabs)

Custom Filament Page at /app/work-orders/{id}/dashboard renders a
Mitchell1-inspired 3-column layout:
  - Top bar: WO#, status badge, actions (Edit / Tracking link)
  - Meta header row: creation / opened / ETA / responsible / urgency /
    paid amount (6 cells)
  - Left sidebar (300px): Client card (avatar, phone, email, status
    tag, 3-stat grid: visits/total/debt) + Vehicle card (photo, plate,
    VIN, mileage, engine, gearbox) + Repair history (latest 5 for this
    vehicle, links to dashboards)
  - Middle: tab bar (Lucrări/Piese/Diagnostic/Foto/Documente/Note)
    with Alpine-driven switching. Works & Parts show the tables read-
    only; add/edit still goes through existing EditWorkOrder Filament
    resource. Photos tab shows gallery from spatie/media-library.
  - Right (300px): Finance summary card (works cost, parts cost,
    discount, total, paid, balance) + placeholder for Timeline+Chat
    (Phase 2)
  - Responsive: right column collapses <1280px, left <900px.

'Vizualizare dashboard' button added on top of the existing
EditWorkOrder page so users can switch between edit form and info-
dense dashboard.

Fixed getSlug() signature (must match parent with ?Panel $panel).

+14 translations. All 306 tests pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-05 18:48:52 +00:00
parent 6de9c615f7
commit fa8704f8b6
5 changed files with 818 additions and 2 deletions
@@ -0,0 +1,695 @@
<x-filament-panels::page>
@php
/** @var \App\Models\Tenant\WorkOrder $wo */
$wo = $this->record;
$finance = $this->getFinance();
$history = $this->getRepairHistory();
$tabs = [
'works' => ['label' => __('Lucrări'), 'count' => $wo->works->count()],
'parts' => ['label' => __('Piese'), 'count' => $wo->parts->count()],
'diag' => ['label' => __('Diagnostic'), 'count' => null],
'photos' => ['label' => __('Foto'), 'count' => $wo->getMedia('photos')->count() ?? 0],
'docs' => ['label' => __('Documente'), 'count' => null],
'notes' => ['label' => __('Note'), 'count' => null],
];
$statusClass = match ($wo->status) {
'in_work' => 'wd-status-active',
'awaiting_parts' => 'wd-status-warn',
'ready' => 'wd-status-ready',
'done' => 'wd-status-done',
'cancelled' => 'wd-status-cancel',
default => 'wd-status-new',
};
@endphp
<style>
/* ══ WORK-ORDER DASHBOARD (Mitchell1-style) ══ */
:root {
--wd-bg: #F7F7F5;
--wd-surface: #FFFFFF;
--wd-border: #E5E7EB;
--wd-border-md: #D1D5DB;
--wd-text: #111827;
--wd-text-2: #4B5563;
--wd-text-3: #9CA3AF;
--wd-blue: #2563EB;
--wd-blue-bg: #EFF6FF;
--wd-green: #16A34A;
--wd-green-bg: #DCFCE7;
--wd-amber: #D97706;
--wd-amber-bg: #FEF3C7;
--wd-red: #DC2626;
--wd-red-bg: #FEE2E2;
--wd-slate: #64748B;
}
.dark {
--wd-bg: #0F172A;
--wd-surface: #1E293B;
--wd-border: #334155;
--wd-border-md: #475569;
--wd-text: #F1F5F9;
--wd-text-2: #CBD5E1;
--wd-text-3: #94A3B8;
}
/* Break out of Filament content wrapper */
.fi-main-ctn:has(.wd-shell) { padding: 0 !important; }
.fi-main:has(.wd-shell) { padding: 0 !important; }
.fi-page:has(.wd-shell) > div { padding: 0 !important; gap: 0 !important; }
.fi-page:has(.wd-shell) .fi-header { display: none !important; }
.wd-shell {
background: var(--wd-bg);
color: var(--wd-text);
min-height: calc(100vh - 64px);
display: flex;
flex-direction: column;
font-size: 13px;
}
/* ── TOP BAR ── */
.wd-topbar {
background: var(--wd-surface);
border-bottom: 1px solid var(--wd-border);
padding: 10px 20px;
display: flex;
align-items: center;
gap: 12px;
flex-shrink: 0;
}
.wd-wo-title { font-size: 15px; font-weight: 700; letter-spacing: -.2px; }
.wd-status {
padding: 4px 10px;
border-radius: 6px;
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: .3px;
cursor: pointer;
display: inline-flex; align-items: center; gap: 4px;
}
.wd-status-active { background: #DBEAFE; color: #1E40AF; }
.wd-status-warn { background: var(--wd-amber-bg); color: var(--wd-amber); }
.wd-status-ready { background: var(--wd-green-bg); color: #166534; }
.wd-status-done { background: #F3F4F6; color: #6B7280; }
.wd-status-cancel { background: var(--wd-red-bg); color: var(--wd-red); }
.wd-status-new { background: #EDE9FE; color: #6D28D9; }
.wd-btn {
padding: 7px 12px;
border: 1px solid var(--wd-border-md);
border-radius: 6px;
font-size: 12px;
font-weight: 500;
background: var(--wd-surface);
color: var(--wd-text);
cursor: pointer;
display: inline-flex; align-items: center; gap: 5px;
text-decoration: none;
}
.wd-btn:hover { background: var(--wd-bg); }
.wd-btn-primary { background: var(--wd-green); color: #fff; border-color: var(--wd-green); }
.wd-btn-primary:hover { background: #15803d; }
.wd-btn-secondary { background: var(--wd-text); color: #fff; border-color: var(--wd-text); }
.wd-btn-secondary:hover { background: #000; }
.wd-topbar-actions { margin-left: auto; display: flex; gap: 6px; }
/* ── META HEADER ROW ── */
.wd-meta {
background: var(--wd-surface);
border-bottom: 1px solid var(--wd-border);
padding: 10px 20px;
display: grid;
grid-template-columns: repeat(6, 1fr);
gap: 16px;
flex-shrink: 0;
}
.wd-meta-item { display: flex; flex-direction: column; gap: 3px; }
.wd-meta-lbl { font-size: 10px; color: var(--wd-text-3); text-transform: uppercase; letter-spacing: .4px; font-weight: 600; }
.wd-meta-val { font-size: 13px; color: var(--wd-text); }
.wd-meta-val.person { display: flex; align-items: center; gap: 6px; }
.wd-avatar {
width: 22px; height: 22px; border-radius: 50%;
background: var(--wd-blue-bg); color: var(--wd-blue);
display: inline-flex; align-items: center; justify-content: center;
font-size: 10px; font-weight: 700;
}
.wd-priority-dot {
display: inline-block; width: 8px; height: 8px; border-radius: 50%;
margin-right: 5px;
}
.wd-priority-normal { background: var(--wd-amber); }
.wd-priority-urgent, .wd-priority-express { background: var(--wd-red); }
/* ── 3-COLUMN GRID ── */
.wd-body {
flex: 1;
display: grid;
grid-template-columns: 300px 1fr 300px;
gap: 12px;
padding: 12px;
min-height: 0;
overflow: hidden;
}
@media (max-width: 1280px) {
.wd-body { grid-template-columns: 260px 1fr; }
.wd-col-right { display: none; }
}
@media (max-width: 900px) {
.wd-body { grid-template-columns: 1fr; }
.wd-col-left { display: none; }
}
.wd-col-left, .wd-col-mid, .wd-col-right {
display: flex; flex-direction: column; gap: 12px;
overflow-y: auto;
min-height: 0;
}
/* ── CARDS (common) ── */
.wd-card {
background: var(--wd-surface);
border: 1px solid var(--wd-border);
border-radius: 10px;
padding: 14px;
}
.wd-card-hd {
display: flex; align-items: center; justify-content: space-between;
margin-bottom: 10px;
}
.wd-card-hd h3 { font-size: 13px; font-weight: 600; margin: 0; }
.wd-card-hd a { font-size: 11px; color: var(--wd-blue); text-decoration: none; }
.wd-card-hd a:hover { text-decoration: underline; }
/* ── LEFT SIDEBAR ── */
.wd-person-row {
display: flex; align-items: center; gap: 10px;
margin-bottom: 8px;
}
.wd-person-avatar {
width: 40px; height: 40px; border-radius: 50%;
background: var(--wd-bg); border: 1px solid var(--wd-border);
display: inline-flex; align-items: center; justify-content: center;
font-size: 15px; font-weight: 700; color: var(--wd-text-2);
flex-shrink: 0;
}
.wd-person-name { font-size: 13px; font-weight: 600; line-height: 1.2; }
.wd-person-phone { font-size: 11px; color: var(--wd-blue); display: block; margin-top: 2px; }
.wd-person-email { font-size: 11px; color: var(--wd-text-2); display: block; margin-top: 1px; }
.wd-tag {
display: inline-block;
background: var(--wd-blue-bg); color: var(--wd-blue);
padding: 2px 8px; border-radius: 4px;
font-size: 10px; font-weight: 600;
margin-top: 6px;
}
.wd-stat-grid {
display: grid; grid-template-columns: repeat(3, 1fr);
gap: 6px; margin-top: 10px;
}
.wd-stat-cell {
text-align: center;
background: var(--wd-bg);
border-radius: 6px;
padding: 6px 4px;
}
.wd-stat-cell .l { font-size: 9px; color: var(--wd-text-3); text-transform: uppercase; }
.wd-stat-cell .v { font-size: 12px; font-weight: 700; margin-top: 2px; }
.wd-stat-cell .v.money { color: var(--wd-green); }
.wd-stat-cell .v.debt { color: var(--wd-red); }
.wd-vehicle-photo {
width: 100%; aspect-ratio: 16/10; object-fit: cover;
background: var(--wd-bg);
border-radius: 6px;
margin-bottom: 8px;
}
.wd-veh-title { font-size: 13px; font-weight: 700; line-height: 1.2; }
.wd-veh-facts {
display: grid; grid-template-columns: auto 1fr;
gap: 4px 10px; font-size: 11px;
margin-top: 6px;
}
.wd-veh-facts dt { color: var(--wd-text-3); }
.wd-veh-facts dd { color: var(--wd-text); font-weight: 500; margin: 0; }
.wd-history-item {
padding: 6px 0;
border-bottom: 1px solid var(--wd-border);
font-size: 11px;
display: flex; justify-content: space-between; gap: 8px;
}
.wd-history-item:last-child { border-bottom: 0; }
.wd-history-item .h-date { color: var(--wd-text-3); }
.wd-history-item .h-total { font-weight: 600; }
.wd-history-item a { color: var(--wd-text); text-decoration: none; }
.wd-history-item a:hover { color: var(--wd-blue); }
/* ── MIDDLE: TABS ── */
.wd-tabs-bar {
background: var(--wd-surface);
border: 1px solid var(--wd-border);
border-radius: 10px 10px 0 0;
border-bottom: 0;
display: flex;
padding: 0 8px;
overflow-x: auto;
flex-shrink: 0;
}
.wd-tab {
padding: 12px 16px;
border-bottom: 2px solid transparent;
color: var(--wd-text-2);
cursor: pointer;
font-size: 13px;
font-weight: 500;
text-transform: uppercase;
letter-spacing: .3px;
background: none;
border-left: 0; border-right: 0; border-top: 0;
white-space: nowrap;
display: inline-flex; align-items: center; gap: 5px;
}
.wd-tab.active { color: var(--wd-blue); border-bottom-color: var(--wd-blue); font-weight: 600; }
.wd-tab-count {
background: var(--wd-bg); color: var(--wd-text-2);
padding: 1px 6px; border-radius: 8px;
font-size: 10px; font-weight: 600;
}
.wd-tab-content {
background: var(--wd-surface);
border: 1px solid var(--wd-border);
border-top: 0;
border-radius: 0 0 10px 10px;
padding: 16px;
flex: 1;
overflow: auto;
min-height: 0;
}
/* Tables inside tabs */
.wd-tbl { width: 100%; border-collapse: collapse; font-size: 12px; }
.wd-tbl th, .wd-tbl td {
padding: 8px 10px;
text-align: left;
border-bottom: 1px solid var(--wd-border);
}
.wd-tbl th {
font-size: 10px; color: var(--wd-text-3);
text-transform: uppercase; letter-spacing: .3px;
font-weight: 600; background: var(--wd-bg);
}
.wd-tbl td.right, .wd-tbl th.right { text-align: right; font-variant-numeric: tabular-nums; }
.wd-tbl tr:last-child td { border-bottom: 0; }
.wd-tbl tr:hover td { background: rgba(37,99,235,.03); }
.wd-mini-badge {
display: inline-block;
padding: 2px 7px; border-radius: 4px;
font-size: 10px; font-weight: 600;
text-transform: uppercase;
}
.wd-mini-todo { background: #F3F4F6; color: #6B7280; }
.wd-mini-progress { background: var(--wd-blue-bg); color: var(--wd-blue); }
.wd-mini-done { background: var(--wd-green-bg); color: #166534; }
.wd-empty { padding: 40px 20px; text-align: center; color: var(--wd-text-3); font-size: 12px; }
.wd-empty-add { margin-top: 8px; }
.wd-footer-row {
display: flex; justify-content: space-between; align-items: center;
padding: 10px 4px; margin-top: 8px;
font-size: 12px;
border-top: 1px solid var(--wd-border);
}
.wd-footer-total { font-weight: 700; font-size: 13px; }
/* ── RIGHT SIDEBAR (placeholder for phase 2) ── */
.wd-placeholder {
padding: 20px;
text-align: center;
color: var(--wd-text-3);
font-size: 11px;
font-style: italic;
}
</style>
<div class="wd-shell" x-data="{ tab: @entangle('activeTab').live }">
{{-- ── TOP BAR ── --}}
<div class="wd-topbar">
<span class="wd-wo-title">{{ __('Fișă') }} {{ $wo->number }}</span>
<span class="wd-status {{ $statusClass }}">
{{ __(\App\Models\Tenant\WorkOrder::STATUSES[$wo->status] ?? $wo->status) }}
<span style="opacity:.6;"></span>
</span>
<div class="wd-topbar-actions">
<a class="wd-btn" href="{{ route('filament.tenant.resources.work-orders.edit', ['record' => $wo->id]) }}">
{{ __('Editează') }}
</a>
@if ($wo->tracking_token)
<a class="wd-btn" target="_blank" href="{{ $wo->trackingUrl() }}">
🔗 {{ __('Link tracking') }}
</a>
@endif
<button type="button" class="wd-btn wd-btn-primary">{{ __('Salvează') }}</button>
</div>
</div>
{{-- ── META HEADER ── --}}
<div class="wd-meta">
<div class="wd-meta-item">
<span class="wd-meta-lbl">{{ __('Data creării') }}</span>
<span class="wd-meta-val">{{ $wo->created_at?->format('d.m.Y H:i') ?? '—' }}</span>
</div>
<div class="wd-meta-item">
<span class="wd-meta-lbl">{{ __('Deschis') }}</span>
<span class="wd-meta-val">{{ $wo->opened_at?->format('d.m.Y H:i') ?? '—' }}</span>
</div>
<div class="wd-meta-item">
<span class="wd-meta-lbl">{{ __('ETA') }}</span>
<span class="wd-meta-val">{{ $wo->eta_at?->format('d.m.Y H:i') ?? '—' }}</span>
</div>
<div class="wd-meta-item">
<span class="wd-meta-lbl">{{ __('Responsabil') }}</span>
<span class="wd-meta-val person">
@if ($wo->master)
<span class="wd-avatar">{{ mb_strtoupper(mb_substr($wo->master->name, 0, 2)) }}</span>
{{ $wo->master->name }}
@else @endif
</span>
</div>
<div class="wd-meta-item">
<span class="wd-meta-lbl">{{ __('Urgență') }}</span>
<span class="wd-meta-val">
<span class="wd-priority-dot wd-priority-{{ $wo->urgency ?? 'normal' }}"></span>
{{ __(\App\Models\Tenant\PricingCoefficient::URGENCY[$wo->urgency ?? 'normal'] ?? 'Normal') }}
</span>
</div>
<div class="wd-meta-item">
<span class="wd-meta-lbl">{{ __('Data plății') }}</span>
<span class="wd-meta-val">{{ $wo->paidAmount() > 0 ? number_format($wo->paidAmount(), 2, '.', ' ') . ' MDL' : '—' }}</span>
</div>
</div>
{{-- ── 3-COLUMN BODY ── --}}
<div class="wd-body">
{{-- ═══ LEFT SIDEBAR ═══ --}}
<div class="wd-col-left">
{{-- CLIENT CARD --}}
<div class="wd-card">
<div class="wd-card-hd">
<h3>{{ __('Client') }}</h3>
@if ($wo->client)
<a href="{{ route('filament.tenant.resources.clients.edit', ['record' => $wo->client_id]) }}">{{ __('Editează') }}</a>
@endif
</div>
@if ($wo->client)
<div class="wd-person-row">
<div class="wd-person-avatar">{{ mb_strtoupper(mb_substr($wo->client->name, 0, 1)) }}</div>
<div style="flex:1;min-width:0;">
<div class="wd-person-name">{{ $wo->client->name }}</div>
@if ($wo->client->phone)
<a href="tel:{{ $wo->client->phone }}" class="wd-person-phone">📞 {{ $wo->client->phone }}</a>
@endif
@if ($wo->client->email)
<span class="wd-person-email">{{ $wo->client->email }}</span>
@endif
</div>
</div>
@if (! empty($wo->client->status))
<span class="wd-tag">{{ __(ucfirst($wo->client->status)) }}</span>
@endif
@php
$totalOrders = \App\Models\Tenant\WorkOrder::where('client_id', $wo->client_id)->count();
$totalSpent = (float) \App\Models\Tenant\WorkOrder::where('client_id', $wo->client_id)->sum('total');
$debt = (float) $wo->client->balance;
@endphp
<div class="wd-stat-grid">
<div class="wd-stat-cell">
<div class="l">{{ __('Vizite') }}</div>
<div class="v">{{ $totalOrders }}</div>
</div>
<div class="wd-stat-cell">
<div class="l">{{ __('Total') }}</div>
<div class="v money">{{ number_format($totalSpent, 0, '.', ' ') }}</div>
</div>
<div class="wd-stat-cell">
<div class="l">{{ __('Datorie') }}</div>
<div class="v {{ $debt > 0 ? 'debt' : '' }}">{{ number_format($debt, 0, '.', ' ') }}</div>
</div>
</div>
@else
<div class="wd-empty">{{ __('Fără client') }}</div>
@endif
</div>
{{-- VEHICLE CARD --}}
<div class="wd-card">
<div class="wd-card-hd">
<h3>{{ __('Automobil') }}</h3>
@if ($wo->vehicle)
<a href="{{ route('filament.tenant.resources.vehicles.edit', ['record' => $wo->vehicle_id]) }}">{{ __('Editează') }}</a>
@endif
</div>
@if ($wo->vehicle)
@php
$photoUrl = $wo->vehicle->getFirstMediaUrl('photos') ?? null;
@endphp
@if ($photoUrl)
<img class="wd-vehicle-photo" src="{{ $photoUrl }}" alt="{{ $wo->vehicle->make }}">
@else
<div class="wd-vehicle-photo" style="display:flex;align-items:center;justify-content:center;color:var(--wd-text-3);font-size:24px;">🚗</div>
@endif
<div class="wd-veh-title">{{ $wo->vehicle->make }} {{ $wo->vehicle->model }} {{ $wo->vehicle->year }}</div>
<dl class="wd-veh-facts">
@if ($wo->vehicle->plate)
<dt>{{ __('Nr.') }}</dt><dd>{{ $wo->vehicle->plate }}</dd>
@endif
@if ($wo->vehicle->vin)
<dt>VIN</dt><dd style="font-family:monospace;font-size:10px;">{{ $wo->vehicle->vin }}</dd>
@endif
@if ($wo->vehicle->mileage)
<dt>{{ __('Km') }}</dt><dd>{{ number_format($wo->vehicle->mileage, 0, '.', ' ') }}</dd>
@endif
@if ($wo->vehicle->engine)
<dt>{{ __('Motor') }}</dt><dd>{{ $wo->vehicle->engine }}</dd>
@endif
@if ($wo->vehicle->gearbox)
<dt>{{ __('Cutie') }}</dt><dd>{{ $wo->vehicle->gearbox }}</dd>
@endif
</dl>
@else
<div class="wd-empty">{{ __('Fără mașină') }}</div>
@endif
</div>
{{-- REPAIR HISTORY --}}
<div class="wd-card">
<div class="wd-card-hd">
<h3>{{ __('Istoric reparații') }}</h3>
</div>
@forelse ($history as $h)
<div class="wd-history-item">
<a href="{{ route('filament.tenant.pages.work-order-dashboard', ['record' => $h->id]) }}">
<span class="h-date">{{ $h->opened_at?->format('d.m.Y') }}</span>
<span>{{ $h->number }}</span>
</a>
<span class="h-total">{{ number_format($h->total, 0, '.', ' ') }} MDL</span>
</div>
@empty
<div class="wd-empty" style="padding:16px 0;">{{ __('Prima vizită') }}</div>
@endforelse
</div>
</div>
{{-- ═══ MIDDLE: TABS ═══ --}}
<div class="wd-col-mid">
<div class="wd-tabs-bar">
@foreach ($tabs as $key => $t)
<button type="button" class="wd-tab" :class="tab === '{{ $key }}' && 'active'" @click="tab = '{{ $key }}'">
{{ $t['label'] }}
@if (! is_null($t['count']))
<span class="wd-tab-count">{{ $t['count'] }}</span>
@endif
</button>
@endforeach
</div>
<div class="wd-tab-content">
{{-- ── WORKS TAB ── --}}
<div x-show="tab === 'works'" x-cloak>
@if ($wo->works->isNotEmpty())
<table class="wd-tbl">
<thead>
<tr>
<th>#</th>
<th>{{ __('Manoperă') }}</th>
<th class="right">{{ __('Ore') }}</th>
<th>{{ __('Mecanic') }}</th>
<th class="right">{{ __('Preț/h') }}</th>
<th class="right">{{ __('Total') }}</th>
<th>{{ __('Status') }}</th>
</tr>
</thead>
<tbody>
@foreach ($wo->works as $i => $w)
<tr>
<td>{{ $i + 1 }}</td>
<td>{{ $w->labor?->label() ?: $w->name }}</td>
<td class="right">{{ rtrim(rtrim(number_format($w->hours, 2), '0'), '.') }}</td>
<td>{{ $w->master?->name ?? '—' }}</td>
<td class="right">{{ number_format($w->price_per_hour, 2, '.', ' ') }}</td>
<td class="right">{{ number_format($w->total, 2, '.', ' ') }}</td>
<td>
<span class="wd-mini-badge wd-mini-{{ $w->status === 'done' ? 'done' : ($w->status === 'in_progress' ? 'progress' : 'todo') }}">
{{ __(\App\Models\Tenant\WorkOrderWork::STATUSES[$w->status] ?? $w->status) }}
</span>
</td>
</tr>
@endforeach
</tbody>
</table>
<div class="wd-footer-row">
<a class="wd-btn" href="{{ route('filament.tenant.resources.work-orders.edit', ['record' => $wo->id, 'relation' => 0]) }}">+ {{ __('Adaugă manoperă') }}</a>
<span class="wd-footer-total">{{ __('Total manopere:') }} {{ number_format($finance['works_sum'], 2, '.', ' ') }} MDL</span>
</div>
@else
<div class="wd-empty">
{{ __('Nicio manoperă efectuată.') }}
<div class="wd-empty-add"><a class="wd-btn" href="{{ route('filament.tenant.resources.work-orders.edit', ['record' => $wo->id, 'relation' => 0]) }}">+ {{ __('Adaugă manoperă') }}</a></div>
</div>
@endif
</div>
{{-- ── PARTS TAB ── --}}
<div x-show="tab === 'parts'" x-cloak>
@if ($wo->parts->isNotEmpty())
<table class="wd-tbl">
<thead>
<tr>
<th>{{ __('Cod') }}</th>
<th>{{ __('Denumire') }}</th>
<th>{{ __('Brand') }}</th>
<th class="right">{{ __('Cant.') }}</th>
<th class="right">{{ __('Preț') }}</th>
<th class="right">{{ __('Total') }}</th>
<th>{{ __('Status') }}</th>
</tr>
</thead>
<tbody>
@foreach ($wo->parts as $p)
<tr>
<td style="font-family:monospace;font-size:11px;">{{ $p->article ?? '—' }}</td>
<td>{{ $p->name }}</td>
<td>{{ $p->brand ?? '—' }}</td>
<td class="right">{{ rtrim(rtrim(number_format($p->qty, 2), '0'), '.') }} {{ $p->unitLabel() ?: '' }}</td>
<td class="right">{{ number_format($p->sell_price, 2, '.', ' ') }}</td>
<td class="right">{{ number_format($p->total, 2, '.', ' ') }}</td>
<td>
<span class="wd-mini-badge wd-mini-{{ $p->status === 'installed' ? 'done' : ($p->status === 'delivered' ? 'progress' : 'todo') }}">
{{ __(\App\Models\Tenant\WorkOrderPart::STATUSES[$p->status] ?? $p->status) }}
</span>
</td>
</tr>
@endforeach
</tbody>
</table>
<div class="wd-footer-row">
<a class="wd-btn" href="{{ route('filament.tenant.resources.work-orders.edit', ['record' => $wo->id, 'relation' => 1]) }}">+ {{ __('Adaugă piesă') }}</a>
<span class="wd-footer-total">{{ __('Total piese:') }} {{ number_format($finance['parts_sum'], 2, '.', ' ') }} MDL</span>
</div>
@else
<div class="wd-empty">
{{ __('Nicio piesă montată.') }}
<div class="wd-empty-add"><a class="wd-btn" href="{{ route('filament.tenant.resources.work-orders.edit', ['record' => $wo->id, 'relation' => 1]) }}">+ {{ __('Adaugă piesă') }}</a></div>
</div>
@endif
</div>
{{-- ── DIAGNOSTIC TAB ── --}}
<div x-show="tab === 'diag'" x-cloak>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:16px;">
<div>
<h4 style="font-size:11px;color:var(--wd-text-3);text-transform:uppercase;margin:0 0 8px;">{{ __('Plângere client') }}</h4>
<div style="background:var(--wd-bg);padding:10px 12px;border-radius:6px;font-size:12px;white-space:pre-wrap;min-height:60px;">{{ $wo->complaint ?: '—' }}</div>
</div>
<div>
<h4 style="font-size:11px;color:var(--wd-text-3);text-transform:uppercase;margin:0 0 8px;">{{ __('Diagnostic') }}</h4>
<div style="background:var(--wd-bg);padding:10px 12px;border-radius:6px;font-size:12px;white-space:pre-wrap;min-height:60px;">{{ $wo->diagnosis ?: '—' }}</div>
</div>
</div>
</div>
{{-- ── PHOTOS TAB ── --}}
<div x-show="tab === 'photos'" x-cloak>
@php $photos = $wo->getMedia('photos'); @endphp
@if ($photos->isNotEmpty())
<div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(140px,1fr));gap:10px;">
@foreach ($photos as $ph)
<a href="{{ $ph->getFullUrl() }}" target="_blank">
<img src="{{ $ph->getFullUrl() }}" style="width:100%;aspect-ratio:4/3;object-fit:cover;border-radius:6px;border:1px solid var(--wd-border);">
</a>
@endforeach
</div>
@else
<div class="wd-empty">{{ __('Nicio fotografie') }}</div>
@endif
</div>
{{-- ── DOCUMENTS TAB ── --}}
<div x-show="tab === 'docs'" x-cloak>
<div class="wd-empty">{{ __('Nicio factură emisă încă') }}</div>
</div>
{{-- ── NOTES TAB ── --}}
<div x-show="tab === 'notes'" x-cloak>
<div style="background:var(--wd-bg);padding:10px 12px;border-radius:6px;font-size:12px;white-space:pre-wrap;min-height:80px;">{{ $wo->notes ?: __('Nicio notă internă.') }}</div>
</div>
</div>
</div>
{{-- ═══ RIGHT COLUMN (placeholder Phase 2) ═══ --}}
<div class="wd-col-right">
<div class="wd-card">
<div class="wd-card-hd">
<h3>{{ __('Finanțe') }}</h3>
</div>
<div style="display:flex;flex-direction:column;gap:6px;font-size:12px;">
<div style="display:flex;justify-content:space-between;">
<span style="color:var(--wd-text-2);">{{ __('Cost manopere') }}</span>
<b>{{ number_format($finance['works_sum'], 2, '.', ' ') }}</b>
</div>
<div style="display:flex;justify-content:space-between;">
<span style="color:var(--wd-text-2);">{{ __('Cost piese') }}</span>
<b>{{ number_format($finance['parts_sum'], 2, '.', ' ') }}</b>
</div>
@if ($finance['discount'] > 0)
<div style="display:flex;justify-content:space-between;">
<span style="color:var(--wd-text-2);">{{ __('Discount') }}</span>
<b style="color:var(--wd-red);">{{ number_format($finance['discount'], 2, '.', ' ') }}</b>
</div>
@endif
<div style="border-top:1px solid var(--wd-border);padding-top:8px;display:flex;justify-content:space-between;font-size:15px;">
<span>{{ __('Total') }}</span>
<b>{{ number_format($finance['total'], 2, '.', ' ') }} MDL</b>
</div>
<div style="display:flex;justify-content:space-between;color:var(--wd-green);">
<span>{{ __('Achitat') }}</span>
<b>{{ number_format($finance['paid'], 2, '.', ' ') }}</b>
</div>
@if ($finance['balance'] > 0)
<div style="display:flex;justify-content:space-between;color:var(--wd-red);font-weight:700;">
<span>{{ __('Rest de plată') }}</span>
<b>{{ number_format($finance['balance'], 2, '.', ' ') }} MDL</b>
</div>
@endif
</div>
</div>
<div class="wd-placeholder">
{{ __('Timeline & Chat — Phase 2') }}
</div>
</div>
</div>
</div>
</x-filament-panels::page>