schema([
Forms\Components\TextInput::make('lot_suffix')
->label('LOT-Nummer')
->prefix(fn ($livewire): ?string => $livewire->getOwnerRecord()->lot_prefix
? $livewire->getOwnerRecord()->lot_prefix . ' -'
: null
)
->placeholder('JJMMTT')
->maxLength(50)
->required()
->dehydrated(false)
->live()
->afterStateHydrated(function ($component, $record) {
if (! $record?->lot_number) return;
$pos = strrpos($record->lot_number, '-');
$component->state($pos !== false
? substr($record->lot_number, $pos + 1)
: $record->lot_number
);
})
->afterStateUpdated(function ($state, $set, $livewire) {
$prefix = $livewire->getOwnerRecord()->lot_prefix;
$set('lot_number', $prefix ? "{$prefix}-{$state}" : $state);
}),
Forms\Components\Hidden::make('lot_number'),
Forms\Components\Select::make('size_id')
->label('Variante (Verpackung)')
->options(fn () => ProductSize::with('packing')
->where('product_id', $this->getOwnerRecord()->id)
->get()
->mapWithKeys(fn ($s) => [$s->id => $s->packing->name . ' ' . $s->packing->size . 'g'])
)
->required()
->live(),
Forms\Components\Select::make('recipe_id')
->label('Rezeptur')
->options(fn () => Recipe::where('product_id', $this->getOwnerRecord()->id)
->orderByDesc('version')
->get()
->mapWithKeys(fn ($r) => [$r->id => 'v' . $r->version . ' · ' . $r->created_at->format('d.m.Y')])
)
->default(fn () => $this->getOwnerRecord()->recipes_id)
->required()
->live()
->afterStateUpdated(function ($state, $set, $get) {
if (! $state) {
$set('origins', []);
return;
}
$recipe = Recipe::with('spices')->find($state);
if (! $recipe) {
return;
}
$existing = collect($get('origins') ?? [])->keyBy('spice_id');
$set('origins', $recipe->spices->map(fn ($s) => $existing->has($s->id)
? $existing[$s->id]
: ['spice_id' => $s->id, 'entries' => [['supplier_id' => null, 'origin_country' => null, 'farmer_id' => null, 'notes' => null]]]
)->toArray());
}),
Forms\Components\TextInput::make('units_produced')
->label('Anzahl Einheiten')
->numeric()
->required()
->live(debounce: 400),
Forms\Components\Placeholder::make('quantity_g_preview')
->label('Gesamtgewicht (berechnet)')
->content(function (Get $get): HtmlString {
$units = (int) $get('units_produced');
$sizeId = $get('size_id');
if (! $units || ! $sizeId) {
return new HtmlString('—');
}
$size = ProductSize::with('packing')->find($sizeId);
$totalG = $units * ($size?->packing?->size ?? 0);
return new HtmlString("{$totalG} g ({$units} × {$size?->packing?->size}g)");
}),
Forms\Components\DatePicker::make('produced_at')
->label('Produktionsdatum'),
Forms\Components\DatePicker::make('expires_at')
->label('Mindesthaltbarkeitsdatum'),
Forms\Components\Section::make('Herkunft der Rohstoffe')->schema([
Forms\Components\Repeater::make('origins')
->label('')
->relationship()
->afterStateHydrated(function ($component, $record, $livewire) {
if ($record?->id) {
return;
}
$recipeId = $livewire->getOwnerRecord()->recipes_id;
if (! $recipeId) {
return;
}
$recipe = Recipe::with('spices')->find($recipeId);
if (! $recipe) {
return;
}
$component->state($recipe->spices->map(fn ($s) => [
'spice_id' => $s->id,
'entries' => [['supplier_id' => null, 'origin_country' => null, 'farmer_id' => null, 'notes' => null]],
])->toArray());
})
->schema([
Forms\Components\Hidden::make('spice_id'),
Forms\Components\Placeholder::make('spice_name')
->label('Rohstoff')
->content(fn (Get $get): string =>
Spice::find($get('spice_id'))?->name ?? '—'
)
->columnSpanFull(),
Forms\Components\Repeater::make('entries')
->label('Herkunftsangaben')
->relationship()
->schema([
Forms\Components\Select::make('supplier_id')
->label('Lieferant')
->options(fn () => \App\Models\Supplier::orderBy('name')->pluck('name', 'id'))
->searchable()
->createOptionForm([
Forms\Components\TextInput::make('name')->label('Name')->required(),
])
->createOptionUsing(fn ($data) => \App\Models\Supplier::create($data)->id)
->nullable(),
Forms\Components\TextInput::make('origin_country')
->label('Herkunftsland')
->maxLength(100),
Forms\Components\Select::make('farmer_id')
->label('Landwirt')
->options(fn () => Farmer::orderBy('name')->pluck('name', 'id'))
->searchable()
->createOptionForm([
Forms\Components\TextInput::make('name')->label('Name')->required(),
])
->createOptionUsing(fn ($data) => Farmer::create($data)->id)
->nullable(),
Forms\Components\Textarea::make('notes')
->label('Notizen')
->rows(2)
->columnSpanFull(),
])
->columns(3)
->addActionLabel('Eintrag hinzufügen')
->reorderable(false)
->columnSpanFull(),
])
->reorderable(false),
]),
]);
}
public function table(Table $table): Table
{
return $table
->recordTitleAttribute('lot_number')
->columns([
Tables\Columns\TextColumn::make('lot_number')
->label('LOT-Nummer')
->searchable()
->sortable(),
Tables\Columns\TextColumn::make('units_produced')
->label('Einheiten')
->suffix(fn ($record) => $record->quantity_g
? ' (' . number_format($record->quantity_g, 0, ',', '.') . 'g)'
: ''),
Tables\Columns\TextColumn::make('recipe.version')
->label('Rezeptur')
->formatStateUsing(fn ($state) => $state ? 'v' . $state : '—'),
Tables\Columns\TextColumn::make('produced_at')
->label('Produktion')
->date('d.m.Y')
->sortable(),
Tables\Columns\TextColumn::make('expires_at')
->label('MHD')
->date('d.m.Y')
->sortable(),
])
->defaultSort('produced_at', 'desc')
->actions([
Tables\Actions\EditAction::make(),
Tables\Actions\DeleteAction::make(),
])
->headerActions([
Tables\Actions\CreateAction::make(),
]);
}
}