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:
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Tenant\Pages;
|
||||
|
||||
use App\Models\Tenant\WorkOrder;
|
||||
use Filament\Pages\Page;
|
||||
|
||||
/**
|
||||
* Mitchell1-style 3-column dashboard view for a single WorkOrder.
|
||||
* MVP Phase 1: layout + tabs (Works/Parts/Diagnostic/Photos/Docs/Notes).
|
||||
* Editing still goes through /app/work-orders/{id}/edit for now.
|
||||
*/
|
||||
class WorkOrderDashboard extends Page
|
||||
{
|
||||
protected static string|\BackedEnum|null $navigationIcon = null;
|
||||
|
||||
protected static bool $shouldRegisterNavigation = false;
|
||||
|
||||
protected string $view = 'filament.tenant.pages.work-order-dashboard';
|
||||
|
||||
public WorkOrder $record;
|
||||
|
||||
public string $activeTab = 'works';
|
||||
|
||||
public static function getSlug(?\Filament\Panel $panel = null): string
|
||||
{
|
||||
return 'work-orders/{record}/dashboard';
|
||||
}
|
||||
|
||||
public static function getRoutePath(?\Filament\Panel $panel = null): string
|
||||
{
|
||||
return 'work-orders/{record}/dashboard';
|
||||
}
|
||||
|
||||
public function mount(int|string $record): void
|
||||
{
|
||||
$this->record = WorkOrder::with([
|
||||
'client', 'vehicle', 'master',
|
||||
'works.labor', 'works.master',
|
||||
'parts.part', 'parts.batch',
|
||||
'payments.user',
|
||||
'subcontractJobs.subcontractor',
|
||||
])->findOrFail($record);
|
||||
}
|
||||
|
||||
public function setTab(string $tab): void
|
||||
{
|
||||
$this->activeTab = $tab;
|
||||
}
|
||||
|
||||
public function getTitle(): string|\Illuminate\Contracts\Support\Htmlable
|
||||
{
|
||||
return __('Fișă') . ' ' . ($this->record->number ?? '');
|
||||
}
|
||||
|
||||
public function getHeading(): string|\Illuminate\Contracts\Support\Htmlable
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
/** Latest 5 WOs for the vehicle (repair history sidebar). */
|
||||
public function getRepairHistory(): \Illuminate\Support\Collection
|
||||
{
|
||||
if (! $this->record->vehicle_id) return collect();
|
||||
return WorkOrder::where('vehicle_id', $this->record->vehicle_id)
|
||||
->where('id', '!=', $this->record->id)
|
||||
->orderByDesc('opened_at')
|
||||
->limit(5)
|
||||
->get(['id', 'number', 'opened_at', 'total', 'status']);
|
||||
}
|
||||
|
||||
/** Aggregated finance data — used by right panel in future phases. */
|
||||
public function getFinance(): array
|
||||
{
|
||||
$r = $this->record;
|
||||
$worksSum = (float) $r->works->sum('total');
|
||||
$partsSum = (float) $r->parts->sum('total');
|
||||
$subtotal = $worksSum + $partsSum;
|
||||
$discount = (float) $r->discount;
|
||||
$total = max(0, $subtotal - $discount);
|
||||
$paid = $r->paidAmount();
|
||||
return [
|
||||
'works_sum' => $worksSum,
|
||||
'parts_sum' => $partsSum,
|
||||
'subtotal' => $subtotal,
|
||||
'discount' => $discount,
|
||||
'total' => $total,
|
||||
'paid' => $paid,
|
||||
'balance' => max(0, $total - $paid),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,11 @@ class EditWorkOrder extends EditRecord
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
Actions\Action::make('dashboard')
|
||||
->label(__('Vizualizare dashboard'))
|
||||
->icon('heroicon-m-squares-2x2')
|
||||
->color('info')
|
||||
->url(fn () => route('filament.tenant.pages.work-order-dashboard', ['record' => $this->record->id])),
|
||||
Actions\Action::make('apply_template')
|
||||
->label(__('Aplică șablon'))
|
||||
->icon('heroicon-m-clipboard-document-list')
|
||||
|
||||
Reference in New Issue
Block a user