user()?->canDo(Permissions::INJECTOR_PROTOCOLS_VIEW) ?? false; } public static function canCreate(): bool { return auth()->user()?->canDo(Permissions::INJECTOR_PROTOCOLS_CONCLUDE) ?? false; } public static function canEdit($record): bool { return auth()->user()?->canDo(Permissions::INJECTOR_PROTOCOLS_CONCLUDE) ?? false; } public static function form(Schema $schema): Schema { return $schema->components([ Schemas\Components\Section::make(__('Date auto & client')) ->columns(3) ->schema([ Forms\Components\TextInput::make('protocol_number') ->label(__('Nr. protocol')) ->disabled() ->dehydrated(false) ->placeholder(__('Generat automat')), Forms\Components\Select::make('source_type') ->label(__('Sursă')) ->options(\App\Support\I18n::opts(InjectorProtocol::SOURCE_TYPES)) ->default('client') ->required(), Forms\Components\TextInput::make('source_name') ->label(__('Autoservis / Client')) ->required() ->maxLength(160), Forms\Components\Select::make('client_id') ->label(__('Client CRM')) ->options(fn () => Client::pluck('name', 'id')) ->searchable() ->live() ->afterStateUpdated(function ($state, Set $set) { if ($state && $c = Client::find($state)) { $set('source_name', $c->name); $set('phone', $c->phone); } }), Forms\Components\Select::make('vehicle_id') ->label(__('Auto CRM')) ->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() : Vehicle::get()->mapWithKeys(fn ($v) => [$v->id => "{$v->make} {$v->model} {$v->plate}"])->toArray()) ->searchable() ->live() ->afterStateUpdated(function ($state, Set $set) { if ($state && $v = Vehicle::find($state)) { $set('car_model', trim(($v->make ?? '') . ' ' . ($v->model ?? ''))); $set('plate_number', $v->plate); $set('vin', $v->vin); $set('year', $v->year); $set('mileage', $v->mileage); } }), Forms\Components\Select::make('work_order_id') ->label(__('Fișă lucru (opțional)')) ->options(fn () => WorkOrder::orderBy('id', 'desc')->limit(50) ->get()->mapWithKeys(fn ($w) => [$w->id => "{$w->number} — " . ($w->client?->name ?? '?')])->toArray()) ->searchable(), Forms\Components\TextInput::make('phone')->label(__('Telefon'))->tel()->maxLength(40), Forms\Components\TextInput::make('plate_number')->label(__('Nr. înmatriculare'))->maxLength(32), Forms\Components\TextInput::make('vin')->label(__('VIN'))->maxLength(17), Forms\Components\TextInput::make('car_model')->label(__('Model auto'))->maxLength(120), Forms\Components\TextInput::make('year')->label(__('An'))->numeric()->minValue(1950)->maxValue(2100), Forms\Components\TextInput::make('mileage')->label(__('Kilometraj'))->numeric()->suffix(__('km')), Forms\Components\TextInput::make('injector_brand')->label(__('Marca injectoarelor'))->maxLength(60), Forms\Components\TextInput::make('injector_count')->label(__('Nr. injectoare')) ->numeric()->minValue(1)->maxValue(8)->default(8)->required(), ]), Schemas\Components\Section::make(__('Cauza adresării')) ->columns(3) ->schema([ Forms\Components\Checkbox::make('reason_new_injectors')->label(__('Injectoare noi')), Forms\Components\Checkbox::make('reason_engine_overhaul')->label(__('Reparație capitală motor')), Forms\Components\Checkbox::make('reason_check')->label(__('Verificare / diagnostic')), ]), Schemas\Components\Section::make(__('Măsurători mecanica injectoarelor')) ->description(__('8 forsunki × 6 măsurători (Înainte / După). Introducerea se face pe rând.')) ->schema([ Forms\Components\Repeater::make('rows') ->label('') ->relationship('rows') ->schema([ Forms\Components\TextInput::make('position') ->label(__('№')) ->numeric()->minValue(1)->maxValue(8)->required() ->disabled()->dehydrated(true), Forms\Components\TextInput::make('resistance_before')->label(__('Rezistență ÎNAINTE'))->numeric()->step(0.01)->suffix('Ω'), Forms\Components\TextInput::make('resistance_after')->label(__('DUPĂ'))->numeric()->step(0.01)->suffix('Ω'), Forms\Components\TextInput::make('open_time_before')->label(__('Deschidere ÎNAINTE'))->numeric()->step(0.01)->suffix('ms'), Forms\Components\TextInput::make('open_time_after')->label(__('DUPĂ'))->numeric()->step(0.01)->suffix('ms'), Forms\Components\TextInput::make('close_time_before')->label(__('Închidere ÎNAINTE'))->numeric()->step(0.01)->suffix('ms'), Forms\Components\TextInput::make('close_time_after')->label(__('DUPĂ'))->numeric()->step(0.01)->suffix('ms'), Forms\Components\TextInput::make('close_time_kz_before')->label(__('Închidere KZ ÎNAINTE'))->numeric()->step(0.01)->suffix('ms'), Forms\Components\TextInput::make('close_time_kz_after')->label(__('DUPĂ'))->numeric()->step(0.01)->suffix('ms'), Forms\Components\TextInput::make('flow_before')->label(__('Debit ÎNAINTE'))->numeric()->step(0.01)->suffix(__('ml')), Forms\Components\TextInput::make('flow_after')->label(__('DUPĂ'))->numeric()->step(0.01)->suffix(__('ml')), Forms\Components\TextInput::make('tightness_before')->label(__('Etanșeitate ÎNAINTE'))->maxLength(20), Forms\Components\TextInput::make('tightness_after')->label(__('DUPĂ'))->maxLength(20), ]) ->columns(2) ->itemLabel(fn (array $state) => __('Forsunka') . ' #' . ($state['position'] ?? '?')) ->addable(false) ->deletable(false) ->reorderable(false) ->collapsed(fn ($record) => (bool) $record), ]), Schemas\Components\Section::make(__('Заключение')) ->columns(3) ->schema([ Forms\Components\Checkbox::make('conclusion_ok') ->label(__('Injectoarele în normă, apte pentru exploatare')), Forms\Components\Checkbox::make('conclusion_needs_cleaning') ->label(__('Necesită curățare repetată')), Forms\Components\Checkbox::make('conclusion_needs_replacement') ->label(__('Recomandată înlocuire')), Forms\Components\Textarea::make('master_comment') ->label(__('Comentariu / recomandări maistru')) ->rows(3) ->columnSpanFull(), Forms\Components\Select::make('master_id') ->label(__('Maistru')) ->options(fn () => User::whereIn('role', ['mechanic', 'admin', 'owner']) ->pluck('name', 'id')) ->searchable(), Forms\Components\TextInput::make('master_name_signed') ->label(__('Semnat de (manual)')) ->maxLength(120) ->helperText(__('Doar dacă maistrul nu e în listă.')), Forms\Components\DatePicker::make('issue_date') ->label(__('Data emiterii')) ->default(today()), Forms\Components\Checkbox::make('client_acknowledged') ->label(__('Client informat / a luat cunoștință')) ->columnSpanFull(), ]), ]); } public static function table(Table $table): Table { return $table ->columns([ Tables\Columns\TextColumn::make('protocol_number') ->label(__('Nr. protocol')) ->searchable() ->sortable() ->copyable() ->weight('bold'), Tables\Columns\TextColumn::make('issue_date') ->label(__('Data')) ->date('d.m.Y') ->sortable() ->placeholder('—'), Tables\Columns\TextColumn::make('source_name') ->label(__('Autoservis / Client')) ->searchable() ->wrap(), Tables\Columns\TextColumn::make('plate_number') ->label(__('Nr. auto')) ->searchable() ->placeholder('—'), Tables\Columns\TextColumn::make('injector_brand') ->label(__('Marca')) ->placeholder('—') ->toggleable(), Tables\Columns\TextColumn::make('injector_count') ->label(__('Injectoare')) ->alignCenter(), Tables\Columns\IconColumn::make('conclusion_ok') ->label(__('OK')) ->boolean() ->toggleable(), Tables\Columns\IconColumn::make('conclusion_needs_cleaning') ->label(__('Curățare')) ->boolean() ->toggleable(), Tables\Columns\IconColumn::make('conclusion_needs_replacement') ->label(__('Înlocuire')) ->boolean() ->toggleable(), Tables\Columns\TextColumn::make('master.name') ->label(__('Maistru')) ->placeholder('—') ->toggleable(), Tables\Columns\TextColumn::make('created_at') ->label(__('Creat')) ->dateTime('d.m.Y H:i') ->toggleable(isToggledHiddenByDefault: true), ]) ->filters([ Tables\Filters\SelectFilter::make('source_type') ->label(__('Sursă')) ->options(\App\Support\I18n::opts(InjectorProtocol::SOURCE_TYPES)), Tables\Filters\Filter::make('needs_action') ->label(__('Necesită acțiune')) ->query(fn ($q) => $q->where(fn ($q) => $q ->where('conclusion_needs_cleaning', true) ->orWhere('conclusion_needs_replacement', true))), ]) ->actions([ Actions\Action::make('pdf') ->label(__('PDF')) ->icon('heroicon-m-document-magnifying-glass') ->color('gray') ->modalHeading(fn (InjectorProtocol $r) => __('Protocol') . ' ' . $r->protocol_number) ->modalSubmitAction(false) ->modalCancelActionLabel(__('Închide')) ->modalWidth('7xl') ->modalContent(fn (InjectorProtocol $r) => view('filament.tenant.pdf-preview', [ 'pdfUrl' => route('tenant.injector-protocols.pdf', ['record' => $r->id]), 'downloadUrl' => route('tenant.injector-protocols.pdf', ['record' => $r->id, 'download' => 1]), ])), Actions\EditAction::make(), Actions\DeleteAction::make(), ]) ->emptyStateHeading(__('Niciun protocol')) ->emptyStateDescription(__('Creează primul protocol de diagnostic forsunki. Numărul se generează automat (PS-{tenant}-{an}-{seq}).')) ->emptyStateIcon('heroicon-o-beaker') ->defaultSort('created_at', 'desc'); } public static function getPages(): array { return [ 'index' => Pages\ListInjectorProtocols::route('/'), 'create' => Pages\CreateInjectorProtocol::route('/create'), 'edit' => Pages\EditInjectorProtocol::route('/{record}/edit'), ]; } }