number ?? $record->id) . ' · ' . ($record->vehicle?->plate ?? '?'); } public static function getGlobalSearchResultDetails(\Illuminate\Database\Eloquent\Model $record): array { return [ 'Client' => $record->client?->name ?? '—', 'Status' => $record->status, 'Total' => number_format((float) $record->total, 2), ]; } public static function form(Schema $schema): Schema { return $schema->components([ Schemas\Components\Section::make('Antet') ->columns(3) ->schema([ Forms\Components\TextInput::make('number') ->label('Nr.') ->disabled() ->dehydrated(false) ->placeholder('Generat automat'), Forms\Components\DatePicker::make('opened_at') ->label('Deschis') ->default(today()) ->required(), Forms\Components\Select::make('status') ->options(WorkOrder::STATUSES) ->default('new') ->required(), Forms\Components\Select::make('client_id') ->label('Client') ->options(fn () => Client::pluck('name', 'id')) ->searchable() ->live() ->required(), Forms\Components\Select::make('vehicle_id') ->label('Auto') ->options(fn (Get $get) => $get('client_id') ? Vehicle::where('client_id', $get('client_id')) ->get() ->mapWithKeys(fn ($v) => [$v->id => "{$v->make} {$v->model} {$v->plate}"]) ->toArray() : []) ->searchable(), Forms\Components\Select::make('master_id') ->label('Maistru') ->options(fn () => User::where('status', 'active')->pluck('name', 'id')) ->searchable(), Forms\Components\TextInput::make('mileage_in')->label('Km la intrare')->numeric(), Forms\Components\TextInput::make('mileage_out')->label('Km la ieșire')->numeric(), ]), Schemas\Components\Section::make('Diagnostic') ->collapsible() ->schema([ Forms\Components\Textarea::make('complaint')->label('Plângere client')->rows(2)->columnSpanFull(), Forms\Components\Textarea::make('diagnosis')->label('Diagnostic')->rows(3)->columnSpanFull(), Forms\Components\Textarea::make('recommendations')->label('Recomandări')->rows(2)->columnSpanFull(), ]), Schemas\Components\Section::make('Foto') ->collapsible() ->schema([ \Filament\Forms\Components\SpatieMediaLibraryFileUpload::make('photos') ->label('Fotografii') ->collection('photos') ->multiple() ->reorderable() ->image() ->imageEditor() ->maxFiles(20) ->columnSpanFull(), ]), Schemas\Components\Section::make('Tracking & ETA') ->columns(3) ->collapsible() ->schema([ Forms\Components\DateTimePicker::make('eta_at') ->label('Gata estimat (ETA)') ->seconds(false), Forms\Components\TextInput::make('tracking_token') ->label('Token public') ->disabled() ->dehydrated(false) ->columnSpan(2) ->helperText(fn (?WorkOrder $record) => $record?->tracking_token ? 'Link client: ' . $record->trackingUrl() : 'Se generează la salvare'), ]), Schemas\Components\Section::make('Plată & total') ->columns(3) ->schema([ Forms\Components\Select::make('pay_status') ->options(WorkOrder::PAY_STATUSES) ->default('unpaid') ->required(), Forms\Components\TextInput::make('discount_pct')->label('Discount %')->numeric()->default(0), Forms\Components\TextInput::make('total')->label('Total')->numeric()->disabled()->dehydrated(false), Forms\Components\Toggle::make('approved')->label('Aprobat de client'), Forms\Components\DatePicker::make('closed_at')->label('Închis'), ]), ]); } public static function table(Table $table): Table { return $table ->columns([ Tables\Columns\TextColumn::make('number')->label('Nr.')->searchable()->sortable(), Tables\Columns\TextColumn::make('opened_at')->label('Deschis')->date('d.m.Y')->sortable(), Tables\Columns\TextColumn::make('client.name')->label('Client')->searchable(), Tables\Columns\TextColumn::make('vehicle.plate')->label('Auto')->placeholder('—'), Tables\Columns\TextColumn::make('master.name')->label('Maistru')->placeholder('—'), Tables\Columns\TextColumn::make('status') ->formatStateUsing(fn ($state) => WorkOrder::STATUSES[$state] ?? $state) ->badge() ->colors([ 'gray' => ['new'], 'info' => ['diagnosis', 'agreement', 'approved'], 'warning' => ['in_work', 'awaiting_parts'], 'success' => ['ready', 'done'], 'danger' => ['cancelled'], ]), Tables\Columns\TextColumn::make('pay_status') ->formatStateUsing(fn ($state) => WorkOrder::PAY_STATUSES[$state] ?? $state) ->badge() ->colors([ 'danger' => ['unpaid'], 'warning' => ['partial'], 'success' => ['paid'], ]), Tables\Columns\TextColumn::make('total')->money('MDL')->alignRight()->sortable(), ]) ->filters([ Tables\Filters\SelectFilter::make('status')->options(WorkOrder::STATUSES), Tables\Filters\SelectFilter::make('pay_status')->options(WorkOrder::PAY_STATUSES), Tables\Filters\SelectFilter::make('master_id') ->label('Maistru') ->options(fn () => User::pluck('name', 'id')), ]) ->actions([ Actions\Action::make('pdf') ->label('PDF') ->icon('heroicon-m-document-arrow-down') ->color('gray') ->action(function (WorkOrder $r) { $svc = app(\App\Services\WorkOrderPdfService::class); $pdf = $svc->generate($r); $filename = $svc->filename($r); return response()->streamDownload( fn () => print($pdf->output()), $filename ); }), Actions\EditAction::make(), Actions\DeleteAction::make(), ]) ->emptyStateHeading('Nicio fișă de lucru') ->emptyStateDescription('Crează prima fișă pentru o mașină existentă. Adaugă manopere, piese, plăți — totalul se calculează automat.') ->emptyStateIcon('heroicon-o-wrench-screwdriver') ->defaultSort('opened_at', 'desc'); } public static function getRelations(): array { return [ RelationManagers\WorksRelationManager::class, RelationManagers\PartsRelationManager::class, RelationManagers\PaymentsRelationManager::class, ]; } public static function getPages(): array { return [ 'index' => Pages\ListWorkOrders::route('/'), 'create' => Pages\CreateWorkOrder::route('/create'), 'edit' => Pages\EditWorkOrder::route('/{record}/edit'), ]; } }