Files
autocrm/resources/views/filament/tenant/pages/work-order-dashboard.blade.php
T
Vasyka 3c92ea5475 feat(hints): scale coverage — 11 new registry entries + inject across pages
Registry grows from 14 → 25 hints. New entries:
- wo.dashboard.topbar_actions / meta / tabs / history / finance /
  timeline / bottom
- wo.create.overview
- crm.clients.overview / vehicles.overview
- finante.dashboard.stats / expenses.overview

"?" icons injected into:
- WO dashboard: top-bar actions, meta header, tabs bar, history card,
  finance card, timeline card, bottom action bar (in addition to the
  existing dashboard title, PDF preview, chat)
- CalendarBoard: title
- PipelineBoard: title
- Excel import wizard: step 1
- Main tenant dashboard (via PanelsRenderHook::PAGE_START scoped to
  Dashboard::class): small banner with a "?" and a shortcut to the
  FAQ page for users who don't see the icons

Users now see hint icons on the primary daily-use screens; more can
be added by dropping <x-hint key="..." /> anywhere.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-09-01 09:57:54 +00:00

885 lines
42 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 }}<x-hint key="wo.dashboard.overview" /></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">
<x-hint key="wo.dashboard.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') }}<x-hint key="wo.dashboard.meta" /></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') }}<x-hint key="wo.dashboard.history" /></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">
<x-hint key="wo.dashboard.tabs" />
@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>
<span style="display:inline-block;"><x-hint key="wo.dashboard.pdf" /></span>
<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') }}<x-hint key="wo.dashboard.finance" /></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') }}<x-hint key="wo.dashboard.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') }}<x-hint key="wo.dashboard.chat" /></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">
<x-hint key="wo.dashboard.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>