schema([ Grid::make(2)->schema([ Forms\Components\TextInput::make('name') ->label('Feature Name') ->required() ->maxLength(255) ->helperText('Internal name used in code (e.g., email_forwarding)'), Forms\Components\TextInput::make('display_name') ->label('Display Name') ->required() ->maxLength(255) ->helperText('User-friendly name shown in UI'), ]), Grid::make(2)->schema([ Forms\Components\Select::make('category') ->label('Category') ->options([ 'core' => 'Core', 'advanced' => 'Advanced', 'premium' => 'Premium', ]) ->required() ->default('core'), Forms\Components\Select::make('type') ->label('Type') ->options([ 'boolean' => 'Boolean (On/Off)', 'numeric' => 'Numeric (With Limits)', 'toggle' => 'Toggle (Switch)', ]) ->required() ->default('boolean') ->reactive() ->afterStateUpdated(function ($state, callable $set) { if ($state === 'numeric') { $set('show_limit_info', true); } else { $set('show_limit_info', false); } }), ]), Forms\Components\Textarea::make('description') ->label('Description') ->rows(3) ->maxLength(500) ->helperText('Describe what this feature does'), Grid::make(2)->schema([ Forms\Components\Toggle::make('is_active') ->label('Active') ->default(true) ->helperText('Feature is available for use in plans'), Forms\Components\TextInput::make('sort_order') ->label('Sort Order') ->numeric() ->default(0) ->helperText('Display order in lists'), ]), TextEntry::make('limit_info') ->label('Numeric Feature Info') ->state('For numeric features, you can set limits in plan configurations (e.g., 100 emails per month).') ->visible(fn (callable $get) => $get('show_limit_info') ?? false), ]); } public static function table(Table $table): Table { return $table ->columns([ Tables\Columns\TextColumn::make('display_name') ->label('Display Name') ->searchable() ->sortable(), Tables\Columns\TextColumn::make('name') ->label('Internal Name') ->searchable() ->sortable() ->badge() ->color('gray'), Tables\Columns\TextColumn::make('category') ->label('Category') ->badge() ->color(fn (string $state): string => match ($state) { 'core' => 'primary', 'advanced' => 'warning', 'premium' => 'danger', default => 'gray', }), Tables\Columns\TextColumn::make('type') ->label('Type') ->badge() ->color(fn (string $state): string => match ($state) { 'boolean' => 'success', 'numeric' => 'info', 'toggle' => 'primary', default => 'gray', }), Tables\Columns\IconColumn::make('is_active') ->label('Active') ->boolean() ->trueColor('success') ->falseColor('danger'), Tables\Columns\TextColumn::make('sort_order') ->label('Order') ->sortable() ->alignCenter(), ]) ->filters([ Tables\Filters\SelectFilter::make('category') ->label('Category') ->options([ 'core' => 'Core', 'advanced' => 'Advanced', 'premium' => 'Premium', ]), Tables\Filters\SelectFilter::make('type') ->label('Type') ->options([ 'boolean' => 'Boolean', 'numeric' => 'Numeric', 'toggle' => 'Toggle', ]), Tables\Filters\TernaryFilter::make('is_active') ->label('Active Status') ->placeholder('All features') ->trueLabel('Active only') ->falseLabel('Inactive only'), ]) ->recordActions([ EditAction::make(), DeleteAction::make() ->before(function (PlanFeature $record) { // Prevent deletion if feature is used in plans if ($record->planFeatureLimits()->exists()) { Log::error('Cannot delete feature that is used in plans'); } }), ]) ->toolbarActions([ BulkActionGroup::make([ DeleteBulkAction::make() ->before(function ($records) { foreach ($records as $record) { if ($record->planFeatureLimits()->exists()) { Log::error('Cannot delete features that are used in planss'); } } }), ]), ]) ->emptyStateActions([ CreateAction::make(), ]) ->defaultSort('sort_order', 'asc') ->groups([ Tables\Grouping\Group::make('category') ->label('Category') ->collapsible(), ]); } public static function getRelations(): array { return [ // ]; } public static function getPages(): array { return [ 'index' => ListPlanFeatures::route('/'), 'create' => CreatePlanFeature::route('/create'), 'edit' => EditPlanFeature::route('/{record}/edit'), ]; } public static function getNavigationBadge(): ?string { return static::getModel()::active()->count(); } public static function getNavigationBadgeColor(): ?string { return 'warning'; } }