'Notice', 'type' => 'placeholder', 'label' => new HtmlString('You can use this extension to manage custom navigation links. To manage navbar links, go to Navbar Manager.'), ], ]; } catch (\Exception $e) { return [ [ 'name' => 'Notice', 'type' => 'placeholder', 'label' => new HtmlString('You can use this extension to manage custom navigation links. You\'ll need to enable this extension above to get started.'), ], ]; } } public function enabled() { // This method is called only when the extension is being enabled // Run migrations or initial setup here try { Artisan::call('migrate', [ '--path' => 'extensions/Others/NavbarManager/database/migrations', '--force' => true ]); } catch (\Exception $e) { \Log::error('NavbarManager: Migration failed', ['error' => $e->getMessage()]); } } public function boot() { // Register view service provider app()->register(\Paymenter\Extensions\Others\NavbarManager\Providers\ViewServiceProvider::class); // Register custom routes for navbar links require __DIR__ . '/routes/web.php'; // Register event listeners for navigation - dodaće naše linkove uz postojeće Event::listen('navigation', function () { return $this->getNavigationLinks('primary'); }); Event::listen('navigation.account-dropdown', function () { return $this->getNavigationLinks('account'); }); Event::listen('navigation.dashboard', function () { return $this->getNavigationLinks('dashboard'); }); // Register the Filament resource try { \Filament\Facades\Filament::serving(function () { \Filament\Facades\Filament::getCurrentPanel() ->resources([ NavbarLinkResource::class, ]); }); } catch (\Exception $e) { // Resource not yet available } } private function getNavigationLinks($type) { try { $links = NavbarLink::where('type', $type) ->where('is_active', true) ->whereNull('parent_id') // Only get top-level links ->orderBy('sort_order') ->with('children') ->get(); $navLinks = []; foreach ($links as $link) { if ($this->shouldShowLink($link)) { $formattedLink = $this->formatLink($link); if ($formattedLink) { $navLinks[] = $formattedLink; } } } return $navLinks; } catch (\Exception $e) { // Log error but don't break navigation \Log::error('NavbarManager: Error loading navigation links', [ 'type' => $type, 'error' => $e->getMessage(), 'trace' => $e->getTraceAsString() ]); return []; } } private function shouldShowLink($link) { try { switch ($link->condition) { case 'always_visible': return true; case 'always_hidden': return false; case 'clients_only': return auth()->check() && auth()->user()->role === null; case 'admin_only': return auth()->check() && auth()->user()->role !== null; case 'guests_only': return !auth()->check(); default: return true; } } catch (\Exception $e) { // If there's an error checking conditions, default to visible return true; } } private function formatLink($link) { try { $formatted = [ 'name' => $link->name, 'priority' => $link->sort_order, ]; // Add icon with validation and strip -line/-fill suffix if ($link->icon && $this->isValidIcon($link->icon)) { // Remove -line or -fill suffix since Navigation::markActiveRoute will add it $baseIcon = preg_replace('/-(?:line|fill)$/', '', $link->icon); $formatted['icon'] = $baseIcon; } // Handle different link types switch ($link->link_type) { case 'route': if ($link->route_name) { $formatted['route'] = $link->route_name; $formatted['spa'] = true; // Enable SPA for route links if ($link->route_params) { // Get raw attribute to avoid accessor transformation $rawParams = $link->getAttributes()['route_params'] ?? null; $params = is_string($rawParams) ? json_decode($rawParams, true) : $rawParams; if ($params && is_array($params)) { $formatted['params'] = $params; } } } else { return null; } break; case 'path': case 'url': // Use navbar redirect route $formatted['route'] = 'navbar.redirect'; $formatted['params'] = ['linkId' => $link->id]; $formatted['spa'] = false; // Disable SPA for external links break; case 'product': if ($link->product_id) { try { $product = \App\Models\Product::with('category')->find($link->product_id); if ($product && $product->category) { // Use products show route (requires both category and product slugs) $formatted['route'] = 'products.show'; $formatted['params'] = [ 'category' => $product->category->slug ?? $product->category->id, 'product' => $product->slug ?? $product->id ]; $formatted['spa'] = true; } else { return null; // Product or category not found } } catch (\Exception $e) { return null; } } else { return null; } break; case 'category': if ($link->category_id) { try { $category = \App\Models\Category::find($link->category_id); if ($category) { // Use category show route $formatted['route'] = 'category.show'; $formatted['params'] = ['category' => $category->slug ?? $category->id]; $formatted['spa'] = true; } else { return null; // Category not found } } catch (\Exception $e) { return null; } } else { return null; } break; } // Handle children (dropdown items) if ($link->children && $link->children->count() > 0) { $children = []; foreach ($link->children->where('is_active', true) as $child) { if ($this->shouldShowLink($child)) { $childFormatted = $this->formatLink($child); if ($childFormatted) { $children[] = $childFormatted; } } } if (!empty($children)) { $formatted['children'] = $children; } } return $formatted; } catch (\Exception $e) { \Log::error('NavbarManager: Error formatting link', [ 'link_id' => $link->id ?? 'unknown', 'error' => $e->getMessage() ]); return null; } } private function isValidIcon($icon) { // Basic validation for RemixIcon format if (!preg_match('/^ri-[\w-]+$/', $icon)) { return false; } // List of common valid RemixIcon names to avoid errors (without -line/-fill suffix) $commonIcons = [ 'ri-home', 'ri-user', 'ri-settings', 'ri-dashboard', 'ri-menu', 'ri-link', 'ri-external-link', 'ri-folder', 'ri-file', 'ri-edit', 'ri-delete-bin', 'ri-add', 'ri-navigation', 'ri-admin', 'ri-function', 'ri-archive-stack', 'ri-receipt', 'ri-customer-service', 'ri-settings-3', 'ri-megaphone', 'ri-shopping-bag', 'ri-check', 'ri-close', 'ri-arrow-down-s' ]; // Remove -line or -fill suffix if present to get base icon name $baseIcon = preg_replace('/-(?:line|fill)$/', '', $icon); // If it's in our common list, it's definitely valid if (in_array($baseIcon, $commonIcons)) { return true; } // For other icons, just check the format (basic validation) // In a production environment, you might want to check if the SVG file actually exists return true; } }