schema([ Forms\Components\Section::make('Anagrafica') ->columns(2) ->schema([ Forms\Components\Select::make('sistema_id') ->label('Sistema') ->relationship('sistema', 'nome') ->required() ->preload() ->live(), Forms\Components\TextInput::make('codice') ->label('Codice interno') ->helperText('Usato dal configuratore nel parametro ?type=.') ->required() ->maxLength(100) ->alphaDash() ->unique( ignoreRecord: true, modifyRuleUsing: fn (Unique $rule, Get $get) => $rule->where('sistema_id', $get('sistema_id')), ), Forms\Components\TextInput::make('nome') ->label('Nome') ->helperText('Etichetta mostrata nella lista modelli del configuratore.') ->required() ->maxLength(255), Forms\Components\TextInput::make('descrizione') ->label('Descrizione') ->maxLength(255), Forms\Components\TextInput::make('titolo') ->label('Titolo nel configuratore') ->helperText('Intestazione del menu e nome nel preventivo; se vuoto si usa la descrizione.') ->maxLength(255) ->columnSpanFull(), Forms\Components\FileUpload::make('icona') ->label('Icona') ->image() ->disk('public') ->directory('modelli/icone') ->maxSize(2048), Forms\Components\Grid::make(1) ->columnSpan(1) ->schema([ Forms\Components\TextInput::make('ordine') ->label('Ordine') ->numeric() ->minValue(0) ->default(0), Forms\Components\Toggle::make('attivo') ->label('Visibile nel configuratore') ->helperText('Richiede la configurazione 3D.') ->default(true) ->live(), ]), ]), Forms\Components\Section::make('Configurazione 3D') ->description('Asset del configuratore usati per questo modello.') ->columns(2) ->collapsible() ->schema([ Forms\Components\TextInput::make('cartella') ->label('Cartella dati') ->helperText('src/assets/data/modelli///') ->required(fn (Get $get) => (bool) $get('attivo')), Forms\Components\TextInput::make('file_modello') ->label('File modello 3D') ->helperText('src/assets/models//.glb') ->required(fn (Get $get) => (bool) $get('attivo')), Forms\Components\Select::make('categoria') ->label('Categoria') ->options(Modello::CATEGORIE) ->required(fn (Get $get) => (bool) $get('attivo')), Forms\Components\TextInput::make('cartella_texture') ->label('Cartella texture') ->helperText('Se vuota si usa la cartella dati.'), Forms\Components\CheckboxList::make('moduli') ->label('Moduli') ->helperText('Sezioni del menu del configuratore e voci che entrano nel prezzo.') ->options(fn () => Modello::moduliDisponibili()) ->columns(3) ->columnSpanFull(), Forms\Components\Select::make('regola_vetro_id') ->label('Regola vetro') ->helperText('Geometria delle lastre per la variazione di prezzo del vetro. Vuota: nessuna variazione.') ->relationship( 'regolaVetro', 'descrizione', modifyQueryUsing: fn (Builder $query, Get $get) => $query->where('sistema_id', $get('sistema_id'))->orderBy('codice'), ) ->getOptionLabelFromRecordUsing(fn ($record) => "{$record->codice} — {$record->descrizione}") ->preload(), ]), ]); } public static function table(Table $table): Table { return $table // Ordine del catalogo (quello del configuratore), con i disattivi in fondo. ->defaultSort(fn (Builder $query) => $query->orderByDesc('attivo')->orderBy('ordine')) ->reorderable('ordine') ->reorderRecordsTriggerAction(fn (Tables\Actions\Action $action, bool $isReordering) => $action ->button() ->icon($isReordering ? 'heroicon-m-check' : 'heroicon-m-arrows-up-down') ->label($isReordering ? 'Fine riordino' : 'Riordina')) ->defaultGroup('sistema.nome') ->columns([ Tables\Columns\ImageColumn::make('icona')->label('Icona')->disk('public')->height(48), // Nome a capo su due righe e codice sotto, nella stessa cella: evita lo // scroll orizzontale (i codici, senza spazi, non possono andare a capo da soli). Tables\Columns\TextColumn::make('nome') ->label('Nome / codice') ->description(fn (Modello $record) => $record->codice) ->searchable(['nome', 'codice']) ->wrap() ->extraAttributes(['style' => 'min-width: 11rem; max-width: 14rem; overflow-wrap: anywhere']), Tables\Columns\TextColumn::make('descrizione')->label('Descrizione')->searchable()->wrap()->placeholder('—'), Tables\Columns\TextColumn::make('categoria') ->label('Categoria') ->badge() ->formatStateUsing(fn (?string $state) => Modello::CATEGORIE[$state] ?? $state) ->placeholder('—'), Tables\Columns\TextColumn::make('listino_count') ->label('Listino') ->counts('listino') ->formatStateUsing(fn (int $state) => $state ? "{$state} prezzi" : 'Nessuno') // Senza listino il modello non compare nel configuratore. ->badge(fn (int $state) => ! $state) ->color(fn (int $state) => $state ? null : 'danger') ->icon(fn (int $state) => $state ? null : 'heroicon-m-exclamation-triangle') ->tooltip(fn (int $state) => $state ? null : 'Senza listino: non compare nel configuratore') ->description(fn (Modello $record) => $record->listino_aggiornato_at?->format('d/m/Y')) ->sortable(), Tables\Columns\ToggleColumn::make('attivo') ->label('Attivo') ->disabled(fn (Modello $record) => ! $record->attivo && (! $record->cartella || ! $record->file_modello)), ]) ->filters([ Tables\Filters\SelectFilter::make('sistema')->label('Sistema')->relationship('sistema', 'nome'), Tables\Filters\SelectFilter::make('categoria')->label('Categoria')->options(Modello::CATEGORIE), Tables\Filters\TernaryFilter::make('attivo')->label('Attivo'), Tables\Filters\TernaryFilter::make('listino') ->label('Listino') ->trueLabel('Con listino') ->falseLabel('Senza listino (non nel configuratore)') ->queries( true: fn (Builder $query) => $query->whereHas('listino'), false: fn (Builder $query) => $query->whereDoesntHave('listino'), ), ]) // Solo icone (con tooltip): le etichette allargherebbero la tabella. ->actions([ Tables\Actions\EditAction::make()->iconButton(), Tables\Actions\DeleteAction::make()->iconButton(), ]) ->bulkActions([ Tables\Actions\BulkActionGroup::make([ Tables\Actions\DeleteBulkAction::make(), ]), ]); } public static function getRelations(): array { return [ ListinoRelationManager::class, FinitureRelationManager::class, OpzioniRelationManager::class, ]; } public static function getPages(): array { return [ 'index' => Pages\ListModelli::route('/'), 'create' => Pages\CreateModello::route('/create'), 'edit' => Pages\EditModello::route('/{record}/edit'), ]; } }