diff --git a/app/Filament/Resources/Manage/EggCollections/EggCollectionResource.php b/app/Filament/Resources/Manage/EggCollections/EggCollectionResource.php new file mode 100644 index 0000000..cf2052e --- /dev/null +++ b/app/Filament/Resources/Manage/EggCollections/EggCollectionResource.php @@ -0,0 +1,52 @@ + ListEggCollections::route('/'), + 'create' => CreateEggCollection::route('/create'), + 'edit' => EditEggCollection::route('/{record}/edit'), + ]; + } +} diff --git a/app/Filament/Resources/Manage/EggCollections/Pages/CreateEggCollection.php b/app/Filament/Resources/Manage/EggCollections/Pages/CreateEggCollection.php new file mode 100644 index 0000000..3d6050a --- /dev/null +++ b/app/Filament/Resources/Manage/EggCollections/Pages/CreateEggCollection.php @@ -0,0 +1,58 @@ +url(ListEggCollections::getUrl()), + ]; + } + + protected function getRedirectUrl(): string + { + return $this->getResource()::getUrl('index'); + } + + protected function mutateFormDataBeforeCreate(array $data): array + { + $data['total_eggs'] = array_sum($this->eggs ?? []); + $data['production_date'] = now(); + + return $data; + } + + protected function afterCreate(): void + { + foreach ($this->eggs as $chickenId => $eggsCount) { + if ($eggsCount <= 0) { + continue; + } + + EggCollectionItem::create([ + 'egg_collection_id' => $this->record->id, + 'chicken_id' => $chickenId, + 'eggs_count' => $eggsCount, + ]); + } + + $this->eggs = []; + } +} diff --git a/app/Filament/Resources/Manage/EggCollections/Pages/EditEggCollection.php b/app/Filament/Resources/Manage/EggCollections/Pages/EditEggCollection.php new file mode 100644 index 0000000..4c9842c --- /dev/null +++ b/app/Filament/Resources/Manage/EggCollections/Pages/EditEggCollection.php @@ -0,0 +1,67 @@ +eggs = $this->record->items() + ->pluck('eggs_count', 'chicken_id') + ->map(fn ($count) => (int) $count) + ->toArray(); + } + + protected function getHeaderActions(): array + { + return [ + BackAction::make() + ->url(ListEggCollections::getUrl()), + ]; + } + + protected function getRedirectUrl(): string + { + return $this->getResource()::getUrl('index'); + } + + protected function mutateFormDataBeforeSave(array $data): array + { + $data['total_eggs'] = array_sum($this->eggs ?? []); + + return $data; + } + + protected function afterSave(): void + { + EggCollectionItem::where('egg_collection_id', $this->record->id)->delete(); + + foreach ($this->eggs as $chickenId => $eggsCount) { + if ($eggsCount <= 0) { + continue; + } + + EggCollectionItem::create([ + 'egg_collection_id' => $this->record->id, + 'chicken_id' => $chickenId, + 'eggs_count' => $eggsCount, + ]); + } + } +} diff --git a/app/Filament/Resources/Manage/EggCollections/Pages/ListEggCollections.php b/app/Filament/Resources/Manage/EggCollections/Pages/ListEggCollections.php new file mode 100644 index 0000000..0da0409 --- /dev/null +++ b/app/Filament/Resources/Manage/EggCollections/Pages/ListEggCollections.php @@ -0,0 +1,22 @@ +label('Tambah'), + ]; + } +} diff --git a/app/Filament/Resources/Manage/EggCollections/Schemas/EggCollectionForm.php b/app/Filament/Resources/Manage/EggCollections/Schemas/EggCollectionForm.php new file mode 100644 index 0000000..3f5666a --- /dev/null +++ b/app/Filament/Resources/Manage/EggCollections/Schemas/EggCollectionForm.php @@ -0,0 +1,63 @@ +remember( + 'active_chickens_for_egg_collection_form', + now()->addMinutes(10), + function () { + return Chicken::query() + ->where('status', ChickenStatus::ACTIVE) + ->orderBy('tag_code') + ->get(); + } + ); + + return $schema + ->components([ + Section::make('Informasi Produksi') + ->schema([ + Grid::make(3) + ->schema([ + Textarea::make('notes') + ->label('Catatan') + ->placeholder('...') + ->columnSpanFull(), + ]), + ]) + ->collapsible(), + + Grid::make([ + 'default' => 1, + 'lg' => 3, + ]) + ->schema([ + Section::make('Daftar Ayam') + ->schema([ + View::make('filament.resources.manage.egg-collections.chicken-eggs-grid') + ->viewData([ + 'chickens' => $chickens, + ]), + ]) + ->columnSpan([ + 'default' => 1, + 'lg' => 3, + ]), + ]), + ]) + ->columns(1); + } +} diff --git a/app/Filament/Resources/Manage/EggCollections/Tables/EggCollectionsTable.php b/app/Filament/Resources/Manage/EggCollections/Tables/EggCollectionsTable.php new file mode 100644 index 0000000..2a530f0 --- /dev/null +++ b/app/Filament/Resources/Manage/EggCollections/Tables/EggCollectionsTable.php @@ -0,0 +1,105 @@ +columns([ + TextColumn::make('production_date') + ->label('Tanggal Produksi') + ->dateTime('l, d F Y H:i') + ->sortable(), + + TextColumn::make('total_eggs') + ->label('Total Telur') + ->numeric() + ->sortable(), + + TextColumn::make('notes') + ->label('Catatan') + ->wrap() + ->toggleable(), + + TextColumn::make('created_at') + ->label('Dibuat') + ->dateTime('d/m/Y H:i') + ->toggleable(isToggledHiddenByDefault: true), + ]) + ->filters([ + TrashedFilter::make() + ->native(false), + ]) + ->recordActions([ + EditAction::make() + ->label('Ubah'), + + ViewAction::make() + ->label('Detail') + ->color('gray') + ->modalHeading('Detail Produksi Telur') + ->modalWidth(Width::Large) + ->schema([ + Section::make('Ringkasan') + ->schema([ + TextEntry::make('production_date') + ->label('Waktu Produksi') + ->dateTime('l, d F Y H:i'), + + TextEntry::make('total_eggs') + ->label('Total Telur'), + + TextEntry::make('notes') + ->label('Catatan') + ->placeholder('-') + ->columnSpanFull(), + + RepeatableEntry::make('items') + ->label('Daftar Ayam') + ->schema([ + TextEntry::make('chicken.tag_code') + ->label('Kode Ayam') + ->placeholder('-'), + + TextEntry::make('eggs_count') + ->label('Jumlah Telur'), + ]) + ->columns(2), + ]), + ]), + + DeleteAction::make(), + + ForceDeleteAction::make(), + + RestoreAction::make(), + ]) + ->toolbarActions([ + BulkActionGroup::make([ + ...DefaultBulkActions::make('Produksi Telur'), + ]), + ]) + ->emptyStateIcon(Heroicon::RectangleGroup) + ->emptyStateDescription('Setelah Anda membuat data pertama, maka akan muncul disini.') + ->defaultSort('created_at', 'desc') + ->deferFilters(false); + } +} diff --git a/app/Filament/Resources/Manage/EggCollections/Traits/HasEggs.php b/app/Filament/Resources/Manage/EggCollections/Traits/HasEggs.php new file mode 100644 index 0000000..9c2a46d --- /dev/null +++ b/app/Filament/Resources/Manage/EggCollections/Traits/HasEggs.php @@ -0,0 +1,46 @@ + eggs_count] + */ + public array $eggs = []; + + public function addEgg(int $chickenId): void + { + $current = $this->eggs[$chickenId] ?? 0; + $this->eggs[$chickenId] = $current + 1; + + $this->syncTotalEggsFromState(); + } + + public function decreaseEgg(int $chickenId): void + { + $current = $this->eggs[$chickenId] ?? 0; + if ($current <= 0) { + return; + } + + $new = $current - 1; + if ($new > 0) { + $this->eggs[$chickenId] = $new; + } else { + unset($this->eggs[$chickenId]); + } + + $this->syncTotalEggsFromState(); + } + + protected function syncTotalEggsFromState(): void + { + $total = array_sum($this->eggs); + + $this->form->fill([ + ...$this->form->getState(), + 'total_eggs' => $total, + ]); + } +} diff --git a/app/Models/EggCollection.php b/app/Models/EggCollection.php new file mode 100644 index 0000000..8a5ffcc --- /dev/null +++ b/app/Models/EggCollection.php @@ -0,0 +1,28 @@ + 'datetime', + 'total_eggs' => 'integer', + ]; + } + + public function items(): HasMany + { + return $this->hasMany(EggCollectionItem::class); + } +} diff --git a/app/Models/EggCollectionItem.php b/app/Models/EggCollectionItem.php new file mode 100644 index 0000000..29ee9dd --- /dev/null +++ b/app/Models/EggCollectionItem.php @@ -0,0 +1,31 @@ + 'integer', + ]; + } + + public function collection(): BelongsTo + { + return $this->belongsTo(EggCollection::class, 'egg_collection_id'); + } + + public function chicken(): BelongsTo + { + return $this->belongsTo(Chicken::class); + } +} diff --git a/database/factories/EggCollectionFactory.php b/database/factories/EggCollectionFactory.php new file mode 100644 index 0000000..1f0bdeb --- /dev/null +++ b/database/factories/EggCollectionFactory.php @@ -0,0 +1,64 @@ + + */ +class EggCollectionFactory extends Factory +{ + protected $model = EggCollection::class; + + public function definition(): array + { + $dateTime = $this->faker->dateTimeBetween('-1 month', 'now'); + + return [ + 'production_date' => $dateTime, + 'total_eggs' => 0, + 'notes' => $this->faker->optional(0.4)->sentence(), + ]; + } + + public function configure() + { + return $this->afterCreating(function (EggCollection $collection) { + $chickens = Chicken::where('status', 'ACTIVE') + ->inRandomOrder() + ->take(20) + ->get(); + + if ($chickens->isEmpty()) { + $chickens = Chicken::factory()->count(20)->create(); + } + + $itemCount = $this->faker->numberBetween(5, min(20, $chickens->count())); + $selectedChickens = $chickens->shuffle()->take($itemCount); + + $totalEggs = 0; + + foreach ($selectedChickens as $chicken) { + $eggsCount = $this->faker->numberBetween(0, 2); + + if ($eggsCount <= 0) { + continue; + } + + EggCollectionItem::create([ + 'egg_collection_id' => $collection->id, + 'chicken_id' => $chicken->id, + 'eggs_count' => $eggsCount, + ]); + + $totalEggs += $eggsCount; + } + + $collection->update(['total_eggs' => $totalEggs]); + }); + } +} diff --git a/database/migrations/2026_02_17_000001_create_egg_collections_table.php b/database/migrations/2026_02_17_000001_create_egg_collections_table.php new file mode 100644 index 0000000..3b78f52 --- /dev/null +++ b/database/migrations/2026_02_17_000001_create_egg_collections_table.php @@ -0,0 +1,26 @@ +id(); + $table->dateTime('production_date'); + $table->unsignedInteger('total_eggs')->default(0); + $table->text('notes')->nullable(); + $table->timestamp('created_at')->useCurrent(); + $table->timestamp('updated_at')->nullable()->useCurrentOnUpdate(); + $table->softDeletes(); + }); + } + + public function down(): void + { + Schema::dropIfExists('egg_collections'); + } +}; diff --git a/database/migrations/2026_02_17_000002_create_egg_collection_items_table.php b/database/migrations/2026_02_17_000002_create_egg_collection_items_table.php new file mode 100644 index 0000000..ab1f23e --- /dev/null +++ b/database/migrations/2026_02_17_000002_create_egg_collection_items_table.php @@ -0,0 +1,26 @@ +id(); + $table->foreignId('egg_collection_id')->constrained()->cascadeOnDelete(); + $table->foreignId('chicken_id')->constrained()->cascadeOnDelete(); + $table->unsignedInteger('eggs_count'); + $table->timestamp('created_at')->useCurrent(); + $table->timestamp('updated_at')->nullable()->useCurrentOnUpdate(); + $table->softDeletes(); + }); + } + + public function down(): void + { + Schema::dropIfExists('egg_collection_items'); + } +}; diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index 722b8f0..ab20efe 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -21,6 +21,7 @@ public function run(): void ChickenSeeder::class, FeedSeeder::class, FeedPurchaseSeeder::class, + EggCollectionSeeder::class, ]); } } diff --git a/database/seeders/EggCollectionSeeder.php b/database/seeders/EggCollectionSeeder.php new file mode 100644 index 0000000..3692548 --- /dev/null +++ b/database/seeders/EggCollectionSeeder.php @@ -0,0 +1,17 @@ +count(30)->create(); + } +} diff --git a/resources/views/filament/resources/manage/egg-collections/chicken-eggs-grid.blade.php b/resources/views/filament/resources/manage/egg-collections/chicken-eggs-grid.blade.php new file mode 100644 index 0000000..69be9a4 --- /dev/null +++ b/resources/views/filament/resources/manage/egg-collections/chicken-eggs-grid.blade.php @@ -0,0 +1,29 @@ +
+ + @foreach ($chickens as $chicken) +
+
+
+ {{ $chicken->tag_code }} +
+ +
+ + - + + + + {{ $this->eggs[$chicken->id] ?? 0 }} + + + + + + +
+
+
+ @endforeach + +