diff --git a/app/Filament/Resources/Manage/EggCollections/EggCollectionResource.php b/app/Filament/Resources/Manage/EggCollections/EggCollectionResource.php index 65debe2..1e227ac 100644 --- a/app/Filament/Resources/Manage/EggCollections/EggCollectionResource.php +++ b/app/Filament/Resources/Manage/EggCollections/EggCollectionResource.php @@ -14,8 +14,11 @@ use BackedEnum; use Filament\Actions\BulkActionGroup; use Filament\Forms\Components\Hidden; +use Filament\Forms\Components\Repeater; +use Filament\Forms\Components\Select; use Filament\Forms\Components\Textarea; use Filament\Forms\Components\TextInput; +use Filament\Forms\Components\Toggle; use Filament\Resources\Resource; use Filament\Schemas\Schema; use Filament\Support\Enums\Width; @@ -48,21 +51,33 @@ public static function form(Schema $schema): Schema Hidden::make('production_date') ->default(now()), - TextInput::make('total_eggs') - ->label('Jumlah Telur') - ->placeholder('0') - ->autocomplete(false) - ->required() - ->currencyMask(thousandSeparator: '.', decimalSeparator: ',', precision: 0) - ->dehydrateStateUsing(fn ($state) => (int) str()->replace(',', '', (string) ($state ?? 0))), + Repeater::make('items') + ->label('Rincian Produksi') + ->relationship('items') + ->schema([ + Select::make('unit_id') + ->label('Satuan') + ->relationship('unit', 'name') + ->required() + ->searchable() + ->preload(), - TextInput::make('total_broken_eggs') - ->label('Jumlah Telur Pecah') - ->placeholder('0') - ->autocomplete(false) - ->required() - ->currencyMask(thousandSeparator: '.', decimalSeparator: ',', precision: 0) - ->dehydrateStateUsing(fn ($state) => (int) str()->replace(',', '', (string) ($state ?? 0))), + TextInput::make('quantity') + ->label('Jumlah') + ->placeholder('0') + ->autocomplete(false) + ->required() + ->currencyMask(thousandSeparator: '.', decimalSeparator: ',', precision: 0) + ->dehydrateStateUsing(fn ($state) => (float) str()->replace(',', '.', str()->replace('.', '', (string) ($state ?? 0)))), + + Toggle::make('is_broken') + ->label('Pecah?') + ->default(false), + ]) + ->columns(3) + ->defaultItems(1) + ->addActionLabel('Tambah Satuan') + ->columnSpanFull(), Textarea::make('notes') ->label('Catatan') @@ -82,23 +97,36 @@ public static function table(Table $table): Table ->searchable() ->sortable(), - TextColumn::make('total_eggs') - ->label('Total Telur') - ->numeric() - ->searchable() - ->sortable(), + TextColumn::make('production_summary') + ->label('Hasil Produksi') + ->getStateUsing(function (EggCollection $record) { + return $record->items() + ->where('is_broken', false) + ->with('unit') + ->get() + ->map(fn ($item) => (float) $item->quantity." {$item->unit?->alias}") + ->join(', ') ?: '-'; + }) + ->badge() + ->color('success'), - TextColumn::make('total_broken_eggs') - ->label('Total Telur Pecah') - ->numeric() - ->searchable() - ->sortable(), + TextColumn::make('broken_summary') + ->label('Telur Pecah') + ->getStateUsing(function (EggCollection $record) { + return $record->items() + ->where('is_broken', true) + ->with('unit') + ->get() + ->map(fn ($item) => (float) $item->quantity." {$item->unit?->alias}") + ->join(', ') ?: '-'; + }) + ->badge() + ->color('danger'), TextColumn::make('notes') ->label('Catatan') ->searchable() - ->wrap() - ->toggleable(), + ->wrap(), ...TimestampColumns::make(), ]) @@ -110,7 +138,7 @@ public static function table(Table $table): Table ->recordActions([ EditAction::make() ->label('Ubah') - ->modalWidth(Width::Large) + ->modalWidth(Width::FourExtraLarge) ->modalHeading(fn (EggCollection $record): string => 'Ubah '.$record->production_date->translatedFormat('l, d F Y H:i')), DeleteAction::make(), diff --git a/app/Filament/Resources/Manage/EggCollections/Pages/ManageEggCollections.php b/app/Filament/Resources/Manage/EggCollections/Pages/ManageEggCollections.php index abee8e9..d041a32 100644 --- a/app/Filament/Resources/Manage/EggCollections/Pages/ManageEggCollections.php +++ b/app/Filament/Resources/Manage/EggCollections/Pages/ManageEggCollections.php @@ -25,7 +25,7 @@ protected function getHeaderActions(): array $action->makeModalSubmitAction('createAnother', arguments: ['another' => true]) ->label('Simpan dan Tambah Lagi'), ]) - ->modalWidth(Width::Large), + ->modalWidth(Width::FourExtraLarge), ]; } } diff --git a/app/Filament/Widgets/StatsOverview.php b/app/Filament/Widgets/StatsOverview.php index 3a7eaf0..40e2124 100644 --- a/app/Filament/Widgets/StatsOverview.php +++ b/app/Filament/Widgets/StatsOverview.php @@ -21,8 +21,8 @@ class StatsOverview extends BaseWidget protected function getStats(): array { return [ - Stat::make('Produksi Telur', number_format(EggCollection::sum('total_eggs'), 0, ',', '.').' Butir') - ->description('Total telur yang terkumpul.') + Stat::make('Produksi Telur', number_format(EggCollection::sum('total_eggs'), 0, ',', '.')) + ->description('Total akumulasi produksi telur (berbagai satuan).') ->descriptionIcon('heroicon-m-circle-stack') ->color('warning'), diff --git a/app/Models/EggCollection.php b/app/Models/EggCollection.php index 32240c0..467cf9f 100644 --- a/app/Models/EggCollection.php +++ b/app/Models/EggCollection.php @@ -4,6 +4,7 @@ use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\SoftDeletes; class EggCollection extends Model @@ -12,6 +13,18 @@ class EggCollection extends Model protected $guarded = ['id']; + public function items(): HasMany + { + return $this->hasMany(EggCollectionItem::class); + } + + public function syncTotals() + { + $this->total_eggs = $this->items()->where('is_broken', false)->sum('quantity'); + $this->total_broken_eggs = $this->items()->where('is_broken', true)->sum('quantity'); + $this->saveQuietly(); + } + protected function casts(): array { return [ diff --git a/app/Models/EggCollectionItem.php b/app/Models/EggCollectionItem.php new file mode 100644 index 0000000..57e77cb --- /dev/null +++ b/app/Models/EggCollectionItem.php @@ -0,0 +1,32 @@ + 'boolean', + 'quantity' => 'decimal:2', + ]; + + public function eggCollection(): BelongsTo + { + return $this->belongsTo(EggCollection::class); + } + + public function unit(): BelongsTo + { + return $this->belongsTo(Unit::class); + } +} diff --git a/app/Observers/EggCollectionItemObserver.php b/app/Observers/EggCollectionItemObserver.php new file mode 100644 index 0000000..a1b7adb --- /dev/null +++ b/app/Observers/EggCollectionItemObserver.php @@ -0,0 +1,24 @@ +eggCollection?->syncTotals(); + } + + /** + * Handle the EggCollectionItem "deleted" event. + */ + public function deleted(EggCollectionItem $eggCollectionItem): void + { + $eggCollectionItem->eggCollection?->syncTotals(); + } +} diff --git a/database/migrations/2026_03_06_235443_create_egg_collection_items_table.php b/database/migrations/2026_03_06_235443_create_egg_collection_items_table.php new file mode 100644 index 0000000..da0e37a --- /dev/null +++ b/database/migrations/2026_03_06_235443_create_egg_collection_items_table.php @@ -0,0 +1,25 @@ +id(); + $table->foreignId('egg_collection_id')->constrained()->cascadeOnDelete(); + $table->foreignId('unit_id')->constrained()->cascadeOnDelete(); + $table->decimal('quantity', 12, 2); + $table->boolean('is_broken')->default(false); + $table->timestamps(); + }); + } + + public function down(): void + { + Schema::dropIfExists('egg_collection_items'); + } +};