Files
kreios/backoffice/app/Filament/Resources/OpzioneResource.php
T
carmine.savino 06617a985a feat: add previous configuration handling for user selections in quotes
- Implemented session storage management for the last configuration choices made by the user.
- Added functionality to prompt users when changing models to retain their previous selections.
- Introduced methods to apply previous choices to the newly loaded model, including finishes, handles, glass types, and options.
- Enhanced user experience by ensuring selections are automatically applied where applicable.
2026-10-19 19:49:44 +02:00

268 lines
13 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.
<?php
namespace App\Filament\Resources;
use App\Filament\Resources\OpzioneResource\Pages;
use App\Filament\Support\CampiConfiguratore;
use App\Models\GruppoOpzioni;
use App\Models\Modello;
use App\Models\Opzione;
use Filament\Forms;
use Filament\Forms\Form;
use Filament\Forms\Get;
use Filament\Forms\Set;
use Filament\Resources\Resource;
use Filament\Tables;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Collection;
use Illuminate\Validation\Rules\Unique;
/** Opzioni e supplementi del sistema (le personalizzazioni per modello sono nella scheda del modello). */
class OpzioneResource extends Resource
{
/** Voce "Altro" del valore nelle opzioni personalizzate: valore scritto a mano. */
public const ALTRO = '__altro';
protected static ?string $model = Opzione::class;
protected static ?string $navigationIcon = 'heroicon-o-adjustments-horizontal';
protected static ?string $slug = 'opzioni';
protected static ?string $navigationGroup = 'Configuratore';
protected static ?string $navigationLabel = 'Opzioni e supplementi';
protected static ?string $modelLabel = 'opzione';
protected static ?string $pluralModelLabel = 'opzioni';
protected static ?int $navigationSort = 23;
public static function getEloquentQuery(): Builder
{
return parent::getEloquentQuery()->whereNull('modello_id');
}
public static function form(Form $form): Form
{
return $form->schema(self::campi(conSistema: true));
}
/**
* Campi dell'opzione; senza sistema (relation manager del modello) l'unicità è sul modello
* e il valore si sceglie tra quelli del gruppo sul sistema, o "Altro" per scriverlo a mano.
*
* @param \Closure(): Modello|null $modello
*/
public static function campi(bool $conSistema, ?\Closure $modello = null): array
{
$unicita = fn (Unique $rule, Get $get) => self::unicita($rule, $get, $modello);
$finitura = fn (Get $get) => $get('gruppo') === 'tipo_finitura';
return [
Forms\Components\Section::make()
->columns(2)
->schema(array_values(array_filter([
$conSistema ? CampiConfiguratore::sistema() : null,
Forms\Components\Select::make('gruppo')
->label('Gruppo')
->options(fn () => GruppoOpzioni::etichette())
->required()
->live()
->afterStateUpdated(function (Set $set) {
$set('valore', null);
$set('valore_altro', null);
}),
$modello ? null : Forms\Components\Select::make('valore')
->label('Valore')
->options(Opzione::TIPI_FINITURA)
->helperText('Il tipo finitura è calcolato dalle finiture interna ed esterna scelte.')
->visible($finitura)
->required()
->unique(ignoreRecord: true, modifyRuleUsing: $unicita),
$modello ? null : Forms\Components\TextInput::make('valore')
->label('Valore')
->helperText('Identificativo inviato dal configuratore (es. dx, telaio_l).')
->hidden($finitura)
->required()
->alphaDash()
->maxLength(100)
->unique(ignoreRecord: true, modifyRuleUsing: $unicita),
$modello ? Forms\Components\Select::make('valore')
->label('Valore')
->helperText(fn (Get $get) => $finitura($get)
? 'Il tipo finitura è calcolato dalle finiture interna ed esterna scelte.'
: 'Valori del gruppo definiti sul sistema: sceglierne uno ne precompila i dati. "Altro" per un valore nuovo.')
// Un solo campo per tutti i gruppi: con due campi "valore" Filament aggiorna solo il primo.
->options(fn (Get $get) => $finitura($get)
? Opzione::TIPI_FINITURA
: self::catalogo($modello(), $get('gruppo'))
->mapWithKeys(fn (Opzione $o) => [$o->valore => "{$o->etichetta} ({$o->valore})"])
->put(self::ALTRO, 'Altro…')
->all())
->required()
->live()
->unique(ignoreRecord: true, modifyRuleUsing: $unicita)
// Valore non presente sul sistema: in modifica compare come "Altro".
->afterStateHydrated(function (Forms\Components\Select $component, ?string $state, Get $get, Set $set) use ($modello) {
if (filled($state) && $get('gruppo') !== 'tipo_finitura'
&& ! self::catalogo($modello(), $get('gruppo'))->contains('valore', $state)) {
$component->state(self::ALTRO);
$set('valore_altro', $state);
}
})
->afterStateUpdated(fn (?string $state, Get $get, Set $set) => self::precompila($modello(), $get('gruppo'), $state, $set))
->dehydrateStateUsing(fn (?string $state, Get $get) => $state === self::ALTRO ? $get('valore_altro') : $state) : null,
$modello ? Forms\Components\TextInput::make('valore_altro')
->label('Nuovo valore')
->helperText('Identificativo inviato dal configuratore (es. dx, telaio_l).')
->visible(fn (Get $get) => $get('valore') === self::ALTRO)
->required()
->alphaDash()
->maxLength(100)
->notIn([self::ALTRO])
->unique(table: Opzione::class, column: 'valore', ignoreRecord: true, modifyRuleUsing: $unicita)
->dehydrated(false) : null,
Forms\Components\TextInput::make('etichetta')->label('Etichetta')->required()->maxLength(255),
Forms\Components\TextInput::make('alternativa')
->label('Alternativa a')
->helperText('Le opzioni del gruppo con lo stesso valore si escludono a vicenda (es. "coprifili": piatti o scatolati).')
->maxLength(50)
->visible(fn (Get $get) => GruppoOpzioni::where('codice', $get('gruppo'))->value('ui') === 'multipla'),
Forms\Components\Toggle::make('attivo')->label('Attiva')->default(true)->inline(false),
]))),
Forms\Components\Section::make('Supplemento')
->columns(2)
->schema([
Forms\Components\Select::make('supplemento_tipo')
->label('Tipo')
->options(Opzione::SUPPLEMENTI)
->placeholder('Nessun supplemento')
->live(),
Forms\Components\TextInput::make('supplemento')
->label('Valore')
->numeric()
->step(0.01)
->default(0)
->required()
->prefix(fn (Get $get) => in_array($get('supplemento_tipo'), ['fisso', 'metro_lineare'], true) ? '€' : null)
->suffix(fn (Get $get) => match ($get('supplemento_tipo')) {
'percentuale' => '%',
'metro_lineare' => '/ml',
default => null,
})
->visible(fn (Get $get) => filled($get('supplemento_tipo'))),
Forms\Components\Fieldset::make('Metri lineari')
->columns(3)
->visible(fn (Get $get) => $get('supplemento_tipo') === 'metro_lineare')
->schema([
Forms\Components\Placeholder::make('formula_ml')
->hiddenLabel()
->content('ml = (L × moltiplicatore L + H × moltiplicatore H + aggiunta) ÷ 1000, con L e H del serramento in mm. Es. "H + 100 mm, 2 pezzi": moltiplicatore H 2, aggiunta 200.')
->columnSpanFull(),
Forms\Components\TextInput::make('ml_per_larghezza')->label('Moltiplicatore L')->numeric()->step(0.001)->default(0)->required(),
Forms\Components\TextInput::make('ml_per_altezza')->label('Moltiplicatore H')->numeric()->step(0.001)->default(0)->required(),
Forms\Components\TextInput::make('ml_aggiunta_mm')->label('Aggiunta')->numeric()->step(1)->suffix('mm')->default(0)->required(),
]),
]),
Forms\Components\Section::make('Configuratore')
->columns(2)
->collapsible()
->hidden(fn (Get $get) => $get('gruppo') === 'tipo_finitura')
->schema([
CampiConfiguratore::immagine('icona', 'Icona', 'opzioni'),
Forms\Components\KeyValue::make('parametri')
->label('Parametri')
->helperText('Letti dal configuratore: scale_x (1 / -1) per l\'apertura, func e categoria per le select.')
->keyLabel('Nome')
->valueLabel('Valore'),
]),
];
}
public static function table(Table $table): Table
{
return self::tabella($table)
->filters([
Tables\Filters\SelectFilter::make('sistema')->label('Sistema')->relationship('sistema', 'nome'),
Tables\Filters\SelectFilter::make('gruppo')->label('Gruppo')->options(fn () => GruppoOpzioni::etichette()),
]);
}
/** Tabella condivisa con le opzioni personalizzate del modello. */
public static function tabella(Table $table): Table
{
return $table
->defaultSort('ordine')
->reorderable('ordine')
->defaultGroup('gruppo')
->groups([
Tables\Grouping\Group::make('gruppo')
->label('Gruppo')
->getTitleFromRecordUsing(fn (Opzione $record) => $record->definizione?->etichetta ?? $record->gruppo),
])
->columns([
Tables\Columns\ImageColumn::make('icona')->label('Icona')->disk('public')->height(40),
Tables\Columns\TextColumn::make('etichetta')->label('Etichetta')->searchable(),
Tables\Columns\TextColumn::make('valore')->label('Valore')->searchable()->color('gray'),
Tables\Columns\TextColumn::make('alternativa')->label('Alternativa')->badge()->color('gray')->placeholder('—')->toggleable(),
Tables\Columns\TextColumn::make('supplemento')
->label('Supplemento')
->state(fn (Opzione $record) => $record->descriviSupplemento()),
Tables\Columns\ToggleColumn::make('attivo')->label('Attiva'),
])
->actions([
Tables\Actions\EditAction::make()->iconButton(),
Tables\Actions\DeleteAction::make()->iconButton(),
])
->bulkActions([
Tables\Actions\BulkActionGroup::make([
Tables\Actions\DeleteBulkAction::make(),
]),
]);
}
public static function getPages(): array
{
return [
'index' => Pages\ManageOpzioni::route('/'),
];
}
private static function unicita(Unique $rule, Get $get, ?\Closure $modello): Unique
{
$rule->where('gruppo', $get('gruppo'));
return $modello
? $rule->where('modello_id', $modello()->getKey())
: $rule->where('sistema_id', $get('sistema_id'))->whereNull('modello_id');
}
/** Opzioni del gruppo definite sul sistema del modello. */
public static function catalogo(Modello $modello, ?string $gruppo): Collection
{
return Opzione::query()
->where('sistema_id', $modello->sistema_id)
->whereNull('modello_id')
->where('gruppo', $gruppo)
->orderBy('ordine')
->get();
}
/** Copia nel form i dati dell'opzione del sistema scelta come valore. */
private static function precompila(Modello $modello, ?string $gruppo, ?string $valore, Set $set): void
{
$set('valore_altro', null);
$origine = self::catalogo($modello, $gruppo)->firstWhere('valore', $valore);
if (! $origine) {
return;
}
foreach (['etichetta', 'alternativa', 'supplemento_tipo', 'supplemento', 'ml_per_larghezza', 'ml_per_altezza', 'ml_aggiunta_mm', 'parametri'] as $campo) {
$set($campo, $origine->{$campo});
}
}
}