[], 'Client Routes' => [], 'API Routes' => [], 'Other Routes' => [], ]; foreach ($routeCollection as $route) { $name = $route->getName(); if ($name && !str_starts_with($name, 'admin.') && !str_starts_with($name, 'filament.') && !str_starts_with($name, '_debugbar') && !str_starts_with($name, 'telescope') && !str_starts_with($name, 'clockwork')) { $methods = implode('|', $route->methods()); $uri = $route->uri(); $label = "{$name} ({$methods} /{$uri})"; // Group routes by type if (in_array($name, ['home', 'dashboard', 'services', 'invoices', 'profile', 'shop'])) { $grouped['Common Routes'][$name] = $label; } elseif (str_starts_with($name, 'client.') || str_starts_with($name, 'account.')) { $grouped['Client Routes'][$name] = $label; } elseif (str_starts_with($name, 'api.')) { $grouped['API Routes'][$name] = $label; } else { $grouped['Other Routes'][$name] = $label; } } } // Remove empty groups and flatten for Filament foreach ($grouped as $group => $groupRoutes) { if (!empty($groupRoutes)) { asort($groupRoutes); $routes = array_merge($routes, $groupRoutes); } } } catch (\Exception $e) { // Fallback routes if there's an error $routes = [ 'home' => 'home (GET /)', 'dashboard' => 'dashboard (GET dashboard)', 'services' => 'services (GET services)', 'invoices' => 'invoices (GET invoices)', 'profile' => 'profile (GET profile)', 'shop' => 'shop (GET shop)', ]; } return $routes; } public static function getAvailableProducts(): array { $products = []; try { $productModels = \App\Models\Product::where('hidden', false)->get(); foreach ($productModels as $product) { $label = $product->name; // Add pricing info if available if ($product->price > 0) { $label .= " (\${$product->price})"; } // Add description if available if ($product->description) { $description = \Str::limit($product->description, 50); $label .= " - {$description}"; } $products[$product->id] = $label; } } catch (\Exception $e) { // Fallback if there's an error $products = []; } return $products; } public static function getAvailableCategories(): array { $categories = []; try { $categoryModels = \App\Models\Category::all(); foreach ($categoryModels as $category) { $label = $category->name; // Add parent category if it's a subcategory if ($category->parent) { $label = $category->parent->name . ' > ' . $label; } // Add product count if available $productCount = $category->products()->where('hidden', false)->count(); if ($productCount > 0) { $label .= " ({$productCount} products)"; } $categories[$category->id] = $label; } } catch (\Exception $e) { // Fallback if there's an error $categories = []; } return $categories; } public static function form(Form $form): Form { return $form ->schema([ Forms\Components\TextInput::make('name') ->label('Link Name') ->required() ->maxLength(255), Forms\Components\Select::make('type') ->label('Navigation Type') ->options([ 'primary' => 'Primary Navigation', 'account' => 'Account Navigation', 'dashboard' => 'Dashboard Navigation', ]) ->required() ->default('primary'), Forms\Components\Select::make('parent_id') ->label('Parent Link (for dropdown)') ->relationship('parent', 'name') ->searchable() ->preload() ->placeholder('Select parent (optional)'), Forms\Components\Section::make('Link Destination') ->description('Configure the link destination') ->schema([ Forms\Components\Select::make('link_type') ->label('Link Type') ->required() ->live() ->options([ 'route' => 'Laravel Route', 'path' => 'Custom Path', 'url' => 'External URL', 'product' => 'Product Path', 'category' => 'Category Path', ]) ->default('route') ->helperText('Select the type of link you want to create'), Forms\Components\Select::make('route_name') ->label('Route Name') ->required(fn (Get $get): bool => $get('link_type') === 'route') ->options(self::getAvailableRoutes()) ->searchable() ->placeholder('Select a Laravel route') ->helperText('Choose from available Laravel routes') ->visible(fn (Get $get): bool => $get('link_type') === 'route'), Forms\Components\Repeater::make('route_params') ->label('Route Parameters') ->schema([ Forms\Components\TextInput::make('key') ->label('Parameter') ->required(), Forms\Components\TextInput::make('value') ->label('Value') ->required(), ]) ->columns(2) ->helperText('Parameters to pass to the route (optional)') ->visible(fn (Get $get): bool => $get('link_type') === 'route'), Forms\Components\TextInput::make('custom_path') ->label('Custom Path') ->required(fn (Get $get): bool => $get('link_type') === 'path') ->placeholder('e.g., /admin/custom-page') ->helperText('Internal path on your website') ->visible(fn (Get $get): bool => $get('link_type') === 'path'), Forms\Components\TextInput::make('custom_url') ->label('External URL') ->required(fn (Get $get): bool => $get('link_type') === 'url') ->url() ->placeholder('e.g., https://example.com') ->helperText('Full external URL') ->visible(fn (Get $get): bool => $get('link_type') === 'url'), Forms\Components\Select::make('product_id') ->label('Select Product') ->required(fn (Get $get): bool => $get('link_type') === 'product') ->options(self::getAvailableProducts()) ->searchable() ->placeholder('Choose a product') ->helperText('Select which product this link should point to') ->visible(fn (Get $get): bool => $get('link_type') === 'product'), Forms\Components\Select::make('category_id') ->label('Select Category') ->required(fn (Get $get): bool => $get('link_type') === 'category') ->options(self::getAvailableCategories()) ->searchable() ->placeholder('Choose a category') ->helperText('Select which category this link should point to') ->visible(fn (Get $get): bool => $get('link_type') === 'category'), ]) ->columns(2), Forms\Components\TextInput::make('icon') ->label('Icon Class') ->placeholder('ri-home-line or home-line') ->helperText('Use any Remix Icon (see https://github.com/Remix-Design/RemixIcon/blob/master/tags.json). Prefix is optional—we will auto-add `ri-`.') ->regex('/^(?:ri-)?[a-z0-9-]+$/i') ->maxLength(80) ->datalist(array_values(RemixIconCatalog::all())) ->dehydrateStateUsing(fn ($state) => self::normalizeIcon($state)), Forms\Components\Select::make('condition') ->label('Visibility Condition') ->options([ 'always_visible' => 'Always Show', 'admin_only' => 'Admin Only', 'clients_only' => 'Client Only', 'guests_only' => 'Guest Only', 'always_hidden' => 'Always Hidden', ]) ->required() ->default('always_visible'), Forms\Components\Toggle::make('is_active') ->label('Active') ->default(true), Forms\Components\TextInput::make('sort_order') ->label('Sort Order') ->numeric() ->default(0) ->helperText('Lower numbers appear first'), ]); } public static function table(Table $table): Table { return $table ->columns([ Tables\Columns\TextColumn::make('name') ->label('Name') ->searchable() ->sortable(), Tables\Columns\TextColumn::make('type') ->label('Type') ->badge() ->color(fn (string $state): string => match ($state) { 'primary' => 'primary', 'account' => 'success', 'dashboard' => 'warning', default => 'gray', }), Tables\Columns\TextColumn::make('link_type') ->label('Link Type') ->badge() ->color(fn (string $state): string => match ($state) { 'route' => 'primary', 'path' => 'warning', 'url' => 'danger', 'product' => 'success', 'category' => 'info', default => 'gray', }) ->formatStateUsing(fn (string $state): string => match ($state) { 'route' => 'Laravel Route', 'path' => 'Custom Path', 'url' => 'External URL', 'product' => 'Product Path', 'category' => 'Category Path', default => $state, }), Tables\Columns\TextColumn::make('parent.name') ->label('Parent') ->placeholder('—'), Tables\Columns\TextColumn::make('route_name') ->label('Route') ->placeholder('—') ->limit(30), Tables\Columns\TextColumn::make('custom_path') ->label('Path') ->placeholder('—') ->limit(30), Tables\Columns\TextColumn::make('custom_url') ->label('URL') ->placeholder('—') ->limit(30), Tables\Columns\TextColumn::make('product.name') ->label('Product') ->placeholder('—') ->limit(25), Tables\Columns\TextColumn::make('category.name') ->label('Category') ->placeholder('—') ->limit(25), Tables\Columns\IconColumn::make('is_active') ->label('Active') ->boolean(), Tables\Columns\TextColumn::make('condition') ->label('Condition') ->badge() ->color(fn (string $state): string => match ($state) { 'always_visible' => 'success', 'admin_only' => 'danger', 'clients_only' => 'primary', 'guests_only' => 'warning', 'always_hidden' => 'gray', default => 'gray', }), Tables\Columns\TextColumn::make('sort_order') ->label('Order') ->sortable(), ]) ->filters([ Tables\Filters\SelectFilter::make('type') ->options([ 'primary' => 'Primary Navigation', 'account' => 'Account Navigation', 'dashboard' => 'Dashboard Navigation', ]), Tables\Filters\SelectFilter::make('condition') ->options([ 'always_visible' => 'Always Show', 'admin_only' => 'Admin Only', 'clients_only' => 'Client Only', 'guests_only' => 'Guest Only', 'always_hidden' => 'Always Hidden', ]), Tables\Filters\TernaryFilter::make('is_active') ->label('Active Status'), ]) ->actions([ Tables\Actions\EditAction::make(), ]) ->bulkActions([ Tables\Actions\BulkActionGroup::make([ Tables\Actions\DeleteBulkAction::make(), ]), ]) ->defaultSort('sort_order'); } public static function getRelations(): array { return [ // ]; } public static function getPages(): array { return [ 'index' => Pages\ListNavbarLinks::route('/'), 'create' => Pages\CreateNavbarLink::route('/create'), 'edit' => Pages\EditNavbarLink::route('/{record}/edit'), ]; } }