438 lines
18 KiB
PHP
438 lines
18 KiB
PHP
<?php
|
|
|
|
namespace Paymenter\Extensions\Others\NavbarManager\Admin\Resources;
|
|
|
|
use Paymenter\Extensions\Others\NavbarManager\Models\NavbarLink;
|
|
use Paymenter\Extensions\Others\NavbarManager\Admin\Resources\NavbarLinkResource\Pages;
|
|
use Paymenter\Extensions\Others\NavbarManager\Support\RemixIconCatalog;
|
|
use Filament\Forms;
|
|
use Filament\Forms\Form;
|
|
use Filament\Forms\Get;
|
|
use Filament\Resources\Resource;
|
|
use Filament\Tables;
|
|
use Filament\Tables\Table;
|
|
use Illuminate\Database\Eloquent\Builder;
|
|
|
|
class NavbarLinkResource extends Resource
|
|
{
|
|
protected static function normalizeIcon(?string $icon): ?string
|
|
{
|
|
if (empty($icon)) {
|
|
return null;
|
|
}
|
|
|
|
$icon = strtolower(trim($icon));
|
|
if ($icon === '') {
|
|
return null;
|
|
}
|
|
|
|
if (!str_starts_with($icon, 'ri-')) {
|
|
$icon = 'ri-' . ltrim($icon, '-');
|
|
}
|
|
|
|
return $icon;
|
|
}
|
|
protected static ?string $model = NavbarLink::class;
|
|
|
|
protected static ?string $navigationIcon = 'heroicon-o-bars-3';
|
|
|
|
protected static ?string $navigationLabel = 'Navbar Manager';
|
|
|
|
protected static ?string $navigationGroup = 'Extensions';
|
|
|
|
public static function getAvailableRoutes(): array
|
|
{
|
|
$routes = [];
|
|
|
|
try {
|
|
$routeCollection = \Route::getRoutes();
|
|
$grouped = [
|
|
'Common Routes' => [],
|
|
'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'),
|
|
];
|
|
}
|
|
}
|