diff --git a/app/Enums/FeedStatus.php b/app/Enums/FeedStatus.php new file mode 100644 index 0000000..c34f521 --- /dev/null +++ b/app/Enums/FeedStatus.php @@ -0,0 +1,42 @@ + 'Tersedia', + self::OUT_OF_STOCK => 'Habis', + self::DISCONTINUED => 'Tidak Digunakan', + }; + } + + public function getColor(): string|array|null + { + return match ($this) { + self::AVAILABLE => 'success', + self::OUT_OF_STOCK => 'danger', + self::DISCONTINUED => 'gray', + }; + } + + public static function options(): array + { + return collect(self::cases()) + ->mapWithKeys(fn ($case) => [$case->value => $case->getLabel()]) + ->toArray(); + } +} diff --git a/app/Filament/Resources/Master/Feeds/FeedResource.php b/app/Filament/Resources/Master/Feeds/FeedResource.php new file mode 100644 index 0000000..9cb9041 --- /dev/null +++ b/app/Filament/Resources/Master/Feeds/FeedResource.php @@ -0,0 +1,142 @@ +components([ + TextInput::make('name') + ->label('Nama Pakan') + ->placeholder('Pelet') + ->autocomplete(false) + ->autofocus() + ->required() + ->maxLength(50), + + Select::make('unit_id') + ->label('Satuan') + ->relationship('unit', 'name') + ->required() + ->native(false) + ->searchable() + ->preload(), + + Select::make('status') + ->label('Status') + ->options(FeedStatus::class) + ->required() + ->native(false) + ->default(FeedStatus::AVAILABLE), + ]) + ->columns(1); + } + + public static function table(Table $table): Table + { + return $table + ->columns([ + TextColumn::make('name') + ->label('Nama Pakan') + ->searchable() + ->sortable(), + + TextColumn::make('stock') + ->label('Stok') + ->numeric(decimalPlaces: 2) + ->suffix(fn (Feed $record) => ' '.$record->unit->alias) + ->sortable(), + + TextColumn::make('status') + ->label('Status') + ->badge() + ->sortable(), + + ...TimestampColumns::make(), + ]) + ->filters([ + SelectFilter::make('status') + ->label('Status') + ->options(FeedStatus::class) + ->native(false), + + TrashedFilter::make() + ->native(false), + ]) + ->recordActions([ + EditAction::make() + ->modalWidth(Width::Large), + + DeleteAction::make(), + + ForceDeleteAction::make(), + + RestoreAction::make(), + ]) + ->toolbarActions([ + BulkActionGroup::make([ + ...DefaultBulkActions::make('Pakan'), + ]), + ]) + ->emptyStateIcon(Heroicon::CircleStack) + ->emptyStateDescription('Setelah Anda membuat data pertama, maka akan muncul disini.') + ->defaultSort('created_at', 'desc') + ->deferFilters(false); + } + + public static function getPages(): array + { + return [ + 'index' => ManageFeeds::route('/'), + ]; + } + + public static function getRecordRouteBindingEloquentQuery(): Builder + { + return parent::getRecordRouteBindingEloquentQuery() + ->withoutGlobalScopes([ + SoftDeletingScope::class, + ]); + } +} diff --git a/app/Filament/Resources/Master/Feeds/Pages/ManageFeeds.php b/app/Filament/Resources/Master/Feeds/Pages/ManageFeeds.php new file mode 100644 index 0000000..215ae64 --- /dev/null +++ b/app/Filament/Resources/Master/Feeds/Pages/ManageFeeds.php @@ -0,0 +1,31 @@ +label('Tambah') + ->modalHeading('Tambah Pakan') + ->modalSubmitActionLabel('Simpan') + ->modalCancelActionLabel('Batal') + ->extraModalFooterActions(fn (CreateAction $action): array => [ + $action->makeModalSubmitAction('createAnother', arguments: ['another' => true]) + ->label('Simpan dan Tambah Lagi'), + ]) + ->modalWidth(Width::Large), + ]; + } +} diff --git a/app/Models/Feed.php b/app/Models/Feed.php new file mode 100644 index 0000000..7be5b05 --- /dev/null +++ b/app/Models/Feed.php @@ -0,0 +1,34 @@ + 'decimal:2', + 'status' => FeedStatus::class, + ]; + } + + public function unit(): BelongsTo + { + return $this->belongsTo(Unit::class); + } + + public function purchases(): HasMany + { + return $this->hasMany(FeedPurchase::class); + } +} diff --git a/database/migrations/2026_02_09_024536_create_feeds_table.php b/database/migrations/2026_02_09_024536_create_feeds_table.php new file mode 100644 index 0000000..07978bf --- /dev/null +++ b/database/migrations/2026_02_09_024536_create_feeds_table.php @@ -0,0 +1,34 @@ +id(); + $table->string('name', 50); + $table->foreignId('unit_id')->constrained()->cascadeOnDelete(); + $table->decimal('stock', 15, 2)->default(0); + $table->enum('status', FeedStatus::cases())->default(FeedStatus::AVAILABLE->value)->comment(FeedStatus::comment()); + $table->timestamp('created_at')->useCurrent(); + $table->timestamp('updated_at')->nullable()->useCurrentOnUpdate(); + $table->softDeletes(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('feeds'); + } +};