Files
autocrm/resources/views/filament/tenant/pages/work-order-dashboard.blade.php
T
Vasyka 1b8ccb116f feat(work-orders): dashboard becomes single hub — list/create/edit funnel through it
- WO list row click now opens /work-orders/{id}/dashboard instead of
  /edit (via ->recordUrl on the table). Edit remains reachable from
  the per-row Filament EditAction.
- CreateWorkOrder redirects to the dashboard after save instead of
  the edit page.
- Dashboard top bar exposes: "Listă" (back to WO list), "+ Nou"
  (create), and "Editare completă" (full edit form) so users don't
  have to hop through the sidebar to move between related WO screens.

The old /work-orders, /create, and /{id}/edit routes stay intact —
just the default navigation flow now converges on the dashboard.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-08-10 19:20:36 +00:00

881 lines
41 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<x-filament-panels::page>
@php
/** @var \App\Models\Tenant\WorkOrder $wo */
$wo = $this->record;
$finance = $this->getFinance();
$history = $this->getRepairHistory();
$timeline = $this->getTimeline();
$messages = $this->getNotifications();
$prev = $this->getPrevWo();
$next = $this->getNextWo();
$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; }
/* ── TIMELINE ── */
.wd-timeline { display: flex; flex-direction: column; gap: 10px; max-height: 240px; overflow-y: auto; }
.wd-tl-item { display: flex; gap: 8px; font-size: 11px; }
.wd-tl-icon {
width: 24px; height: 24px; border-radius: 50%;
background: var(--wd-bg); border: 1px solid var(--wd-border);
display: flex; align-items: center; justify-content: center;
flex-shrink: 0; font-size: 11px;
}
.wd-tl-body { flex: 1; min-width: 0; }
.wd-tl-body .lbl { font-weight: 600; color: var(--wd-text); line-height: 1.3; }
.wd-tl-body .meta { color: var(--wd-text-3); font-size: 10px; margin-top: 2px; }
/* ── CHAT ── */
.wd-chat { display: flex; flex-direction: column; height: 260px; }
.wd-chat-list { flex: 1; overflow-y: auto; padding-right: 4px; }
.wd-msg { margin-bottom: 8px; }
.wd-msg-meta { font-size: 10px; color: var(--wd-text-3); margin-bottom: 2px; display: flex; gap: 6px; }
.wd-msg-body {
background: var(--wd-blue-bg); color: var(--wd-text);
padding: 6px 10px; border-radius: 8px 8px 8px 2px;
font-size: 11px; line-height: 1.4;
white-space: pre-wrap;
}
.wd-msg.failed .wd-msg-body { background: var(--wd-red-bg); color: var(--wd-red); }
.wd-chat-input { display: flex; gap: 6px; margin-top: 8px; }
.wd-chat-input input {
flex: 1; padding: 7px 10px; font-size: 12px;
border: 1px solid var(--wd-border-md); border-radius: 6px;
background: var(--wd-surface); color: var(--wd-text);
}
/* ── BOTTOM ACTION BAR ── */
.wd-bottom {
background: var(--wd-surface);
border-top: 1px solid var(--wd-border);
padding: 10px 20px;
display: flex; gap: 8px; align-items: center;
flex-shrink: 0;
}
.wd-bottom-nav { display: flex; gap: 4px; }
.wd-bottom-actions { margin-left: auto; display: flex; gap: 6px; }
.wd-btn-danger { background: var(--wd-red); color: #fff; border-color: var(--wd-red); }
.wd-btn-danger:hover { background: #991b1b; }
</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="{{ url('/app/work-orders') }}" title="{{ __('Toate fișele') }}">
{{ __('Listă') }}
</a>
<a class="wd-btn wd-btn-primary" href="{{ url('/app/work-orders/create') }}" title="{{ __('Fișă nouă') }}">
+ {{ __('Nou') }}
</a>
<a class="wd-btn" href="{{ route('filament.tenant.resources.work-orders.edit', ['record' => $wo->id]) }}">
{{ __('Editare completă') }}
</a>
@if ($wo->tracking_token)
<a class="wd-btn" target="_blank" href="{{ $wo->trackingUrl() }}">
🔗 {{ __('Link tracking') }}
</a>
@endif
</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
// 1) Vehicle's own photo (if the model uses MediaLibrary), else
// 2) fall back to the first WO photo (uploaded via edit form).
$photoUrl = method_exists($wo->vehicle, 'getFirstMediaUrl')
? ($wo->vehicle->getFirstMediaUrl('photos') ?: null)
: null;
if (! $photoUrl) {
$woFirst = $wo->getFirstMedia('photos');
$photoUrl = $woFirst?->getFullUrl();
}
@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="{{ url('/app/work-orders/' . $h->id . '/dashboard') }}">
<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 style="display:flex;flex-direction:column;gap:8px;" x-data="{ pdfOpen: false }">
<button type="button" class="wd-btn" @click="pdfOpen = true">
🔍 {{ __('Vizualizează factură (PDF)') }}
</button>
<a class="wd-btn" href="{{ url('/app/work-orders/' . $wo->id . '/pdf?download=1') }}">
⬇️ {{ __('Descarcă factură (PDF)') }}
</a>
{{-- PDF preview modal (Alpine.js overlay) --}}
<div x-show="pdfOpen" x-cloak
style="position:fixed;inset:0;background:rgba(0,0,0,0.6);z-index:9999;display:flex;align-items:center;justify-content:center;padding:24px;"
@click.self="pdfOpen = false"
@keydown.escape.window="pdfOpen = false">
<div style="background:#fff;border-radius:8px;width:100%;max-width:1000px;height:90vh;display:flex;flex-direction:column;overflow:hidden;">
<div style="display:flex;align-items:center;justify-content:space-between;padding:10px 14px;border-bottom:1px solid #e5e7eb;">
<b style="font-size:14px;">{{ __('Factură') }} WO #{{ $wo->number }}</b>
<button type="button" @click="pdfOpen = false" style="background:none;border:none;font-size:20px;cursor:pointer;color:#6b7280;"></button>
</div>
<iframe x-bind:src="pdfOpen ? '{{ url('/app/work-orders/' . $wo->id . '/pdf') }}#toolbar=1&navpanes=0' : ''"
style="flex:1;width:100%;border:none;" title="PDF"></iframe>
</div>
</div>
@php $signed = $wo->getMedia('signed_documents'); @endphp
@if ($signed->isNotEmpty())
<div style="font-size:11px;color:var(--wd-text-3);text-transform:uppercase;margin-top:12px;">{{ __('Documente semnate') }}</div>
@foreach ($signed as $doc)
<a class="wd-btn" href="{{ $doc->getFullUrl() }}" target="_blank">
📎 {{ $doc->name }} <span style="color:var(--wd-text-3);">({{ round($doc->size / 1024) }} KB)</span>
</a>
@endforeach
@else
<div class="wd-empty" style="padding:12px 0;font-size:11px;">{{ __('Niciun document semnat încă. Poți încărca documente din formularul de editare al fișei.') }}</div>
@endif
</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>
{{-- TIMELINE --}}
<div class="wd-card">
<div class="wd-card-hd">
<h3>{{ __('Timeline') }}</h3>
</div>
@if (! empty($timeline))
<div class="wd-timeline">
@foreach ($timeline as $ev)
<div class="wd-tl-item">
<div class="wd-tl-icon">{{ $ev['icon'] }}</div>
<div class="wd-tl-body">
<div class="lbl">{{ $ev['label'] }}</div>
<div class="meta">
{{ $ev['at']->format('d.m.Y H:i') }}
@if ($ev['meta']) · {{ $ev['meta'] }} @endif
</div>
</div>
</div>
@endforeach
</div>
@else
<div class="wd-empty" style="padding:16px 0;">{{ __('Fără evenimente') }}</div>
@endif
</div>
{{-- CHAT CLIENT --}}
@php
$tenantSettings = (array) (app(\App\Tenancy\TenantManager::class)->current()?->settings ?? []);
$integrations = (array) ($tenantSettings['integrations'] ?? []);
$telegramEnabled = (bool) data_get($integrations, 'telegram_bot.enabled')
&& filled(data_get($integrations, 'telegram_bot.fields.bot_token'));
$whatsappEnabled = (bool) data_get($integrations, 'whatsapp_business.enabled')
&& filled(data_get($integrations, 'whatsapp_business.fields.token'));
$messagingConfigured = $telegramEnabled || $whatsappEnabled;
@endphp
<div class="wd-card">
<div class="wd-card-hd">
<h3>{{ __('Chat client') }}</h3>
@if ($telegramEnabled && $wo->client?->telegram_chat_id)
<span style="font-size:10px;color:var(--wd-blue);"> Telegram</span>
@elseif ($whatsappEnabled && $wo->client?->phone)
<span style="font-size:10px;color:var(--wd-text-3);"> WhatsApp</span>
@endif
</div>
@if (! $messagingConfigured)
<div class="wd-empty" style="padding:16px 0;">
{{ __('Mesageria nu este configurată.') }}
<div class="wd-empty-add" style="margin-top:8px;">
<a class="wd-btn" href="{{ url('/app/integrations') }}">⚙️ {{ __('Configurează integrări') }}</a>
</div>
</div>
@elseif (! $wo->client)
<div class="wd-empty" style="padding:16px 0;">{{ __('Fișa nu are client asociat.') }}</div>
@else
<div class="wd-chat">
<div class="wd-chat-list">
@forelse ($messages as $m)
<div class="wd-msg {{ $m->status === 'failed' ? 'failed' : '' }}">
<div class="wd-msg-meta">
<span>{{ strtoupper($m->channel) }}</span>
<span>{{ $m->sent_at?->format('d.m H:i') }}</span>
@if ($m->status === 'failed')<span style="color:var(--wd-red);"> {{ __('Eșuat') }}</span>@endif
</div>
<div class="wd-msg-body">{{ $m->message_text }}</div>
</div>
@empty
<div class="wd-empty" style="padding:16px 0;">{{ __('Nicio conversație') }}</div>
@endforelse
</div>
<form class="wd-chat-input" wire:submit.prevent="sendMessage">
<input type="text" placeholder="{{ __('Scrie mesajul tău...') }}" wire:model="newMessage" maxlength="1000">
<button type="submit" class="wd-btn wd-btn-primary"></button>
</form>
</div>
@endif
</div>
</div>
</div>
{{-- ── BOTTOM ACTION BAR ── --}}
<div class="wd-bottom">
<div class="wd-bottom-nav">
@if ($prev)
<a class="wd-btn" href="{{ url('/app/work-orders/' . $prev->id . '/dashboard') }}">
{{ __('Precedent') }} <span style="color:var(--wd-text-3);">({{ $prev->number }})</span>
</a>
@endif
@if ($next)
<a class="wd-btn" href="{{ url('/app/work-orders/' . $next->id . '/dashboard') }}">
{{ __('Următor') }} <span style="color:var(--wd-text-3);">({{ $next->number }})</span>
</a>
@endif
</div>
<div class="wd-bottom-actions">
<a class="wd-btn" href="{{ route('filament.tenant.resources.work-orders.create') }}?client_id={{ $wo->client_id }}&vehicle_id={{ $wo->vehicle_id }}">
📄 {{ __('Comandă repetată') }}
</a>
@if ($wo->status !== 'done')
<button type="button" class="wd-btn wd-btn-danger" wire:click="closeWorkOrder" wire:confirm="{{ __('Închide fișa?') }}">
🔒 {{ __('Închide fișă') }}
</button>
@endif
</div>
</div>
</div>
</x-filament-panels::page>