Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
@if ($shouldShowDropdown())
<div class="flex items-center gap-x-2">
<a @if ($hasSpaMode()) wire:navigate @endif href="{{ $getDashboardUrl() }}" class="flex-1">
<div class="fi-logo flex text-xl font-bold leading-5 tracking-tight text-gray-950 dark:text-white">
{{ $getAppName() }}
@if ($getCurrentTenant() && $getCurrentTenantName() !== $getAppName())
<span class="text-gray-500 dark:text-gray-400 text-sm font-normal ml-2">
- {{ $getCurrentTenantName() }}
</span>
@endif
</div>
</a>

<x-filament::dropdown teleport>
<x-slot name="trigger">
<button type="button"
class="fi-tenant-menu-trigger group flex items-center justify-center rounded-lg p-1.5 text-sm font-medium outline-none transition duration-75 hover:bg-gray-100 focus-visible:bg-gray-100 dark:hover:bg-white/5 dark:focus-visible:bg-white/5">
<x-filament::icon icon="heroicon-m-chevron-down" alias="panels::brand-tenant-menu.toggle-button"
class="h-4 w-4 text-gray-400 transition duration-75 group-hover:text-gray-500 group-focus-visible:text-gray-500 dark:text-gray-500 dark:group-hover:text-gray-400 dark:group-focus-visible:text-gray-400" />
</button>
</x-slot>

<x-filament::dropdown.list>
@if ($canSwitchTenants())
@foreach ($getTenants() as $tenant)
<x-filament::dropdown.list.item :href="route('filament.admin.pages.dashboard', ['tenant' => $tenant])" :image="filament()->getTenantAvatarUrl($tenant)" tag="a">
{{ filament()->getTenantName($tenant) }}
</x-filament::dropdown.list.item>
@endforeach
@endif


@if ($hasFrontend())
@if ($canSwitchTenants())
<x-filament::dropdown.list.item tag="div"
class="border-t border-gray-200 dark:border-gray-700 my-1"></x-filament::dropdown.list.item>
@endif

<x-filament::dropdown.list.item :href="$getFrontendUrl()" tag="a" target="_blank"
class="font-medium">
<div class="flex items-center gap-2">
<x-filament::icon icon="heroicon-s-globe-alt"
class="h-4 w-4 text-gray-500 dark:text-gray-400" />
Frontend
<x-filament::icon icon="heroicon-s-arrow-top-right-on-square"
class="h-3 w-3 text-gray-400 dark:text-gray-500 ml-auto" />
</div>
</x-filament::dropdown.list.item>
@endif
</x-filament::dropdown.list>
</x-filament::dropdown>
</div>
@else
<div class="fi-logo flex text-xl font-bold leading-5 tracking-tight text-gray-950 dark:text-white">
{{ $getAppName() }}
@if ($getCurrentTenant() && $getCurrentTenantName() !== $getAppName())
<span class="text-gray-500 dark:text-gray-400 text-sm font-normal ml-2">
- {{ $getCurrentTenantName() }}
</span>
@endif
</div>
@endif
5 changes: 5 additions & 0 deletions src/EclipseServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
use Eclipse\Core\Providers\HorizonServiceProvider;
use Eclipse\Core\Providers\TelescopeServiceProvider;
use Eclipse\Core\Services\Registry;
use Eclipse\Frontend\Providers\FrontendPanelProvider;
use Filament\Facades\Filament;
use Filament\Resources\Resource;
use Filament\Support\Facades\FilamentAsset;
Expand Down Expand Up @@ -103,6 +104,10 @@ public function register(): self
$this->app->register(AdminPanelProvider::class);
}

if (class_exists(FrontendPanelProvider::class)) {
$this->app->register(FrontendPanelProvider::class);
}

if ($this->app->environment('local')) {
$this->app->register(\Laravel\Telescope\TelescopeServiceProvider::class);
$this->app->register(TelescopeServiceProvider::class);
Expand Down
27 changes: 2 additions & 25 deletions src/Providers/AdminPanelProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
use BezhanSalleh\FilamentShield\Facades\FilamentShield;
use BezhanSalleh\FilamentShield\FilamentShieldPlugin;
use BezhanSalleh\FilamentShield\Middleware\SyncShieldTenant;
use BezhanSalleh\PanelSwitch\PanelSwitch;
use DutchCodingCompany\FilamentDeveloperLogins\FilamentDeveloperLoginsPlugin;
use Eclipse\Common\CommonPlugin;
use Eclipse\Common\Providers\GlobalSearchProvider;
Expand All @@ -16,6 +15,7 @@
use Eclipse\Core\Models\Site;
use Eclipse\Core\Models\User;
use Eclipse\Core\Services\Registry;
use Eclipse\Core\View\Components\BrandWithTenantSwitcher;
use Eclipse\World\EclipseWorld;
use Filament\Http\Middleware\Authenticate;
use Filament\Http\Middleware\DisableBladeIconComponents;
Expand All @@ -41,7 +41,6 @@
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\Facades\Schema;
use Illuminate\View\Middleware\ShareErrorsFromSession;
use Illuminate\View\View;
use LaraZeus\SpatieTranslatable\SpatieTranslatablePlugin;
use pxlrbt\FilamentEnvironmentIndicator\EnvironmentIndicatorPlugin;
use pxlrbt\FilamentSpotlight\SpotlightPlugin;
Expand Down Expand Up @@ -77,7 +76,7 @@ public function panel(Panel $panel): Panel
])
->topNavigation()
->brandLogo(
fn (): View => view('eclipse::filament.components.brand')
fn () => app(BrandWithTenantSwitcher::class)
)
->discoverResources(in: app_path('Filament/Resources'), for: 'App\\Filament\\Resources')
->discoverResources(in: $package_src.'Filament/Resources', for: 'Eclipse\\Core\\Filament\\Resources')
Expand Down Expand Up @@ -169,13 +168,6 @@ public function panel(Panel $panel): Panel
fn () => view('eclipse::filament.components.my-settings')
);

if ($hasTenantMenu) {
$panel->renderHook(
PanelsRenderHook::GLOBAL_SEARCH_END,
fn () => view('eclipse::filament.components.tenant-menu')
);
}

// If the Pro version of the Spotlight plugin is installed, use that, otherwise use the free version
if (class_exists(\pxlrbt\FilamentSpotlightPro\SpotlightPlugin::class)) {
/** @noinspection PhpFullyQualifiedNameUsageInspection */
Expand Down Expand Up @@ -240,20 +232,5 @@ public function boot(): void

// Load customized translations for Filament Shield
$this->loadTranslationsFrom(__DIR__.'/../../resources/lang/vendor/filament-shield', 'filament-shield');

// Configure Panel Switch
PanelSwitch::configureUsing(function (PanelSwitch $panelSwitch) {
$panelSwitch
->simple()
->icons([
'admin' => 'heroicon-s-cog-6-tooth',
'frontend' => 'heroicon-s-globe-alt',
])
->labels([
'admin' => 'Admin Panel',
'frontend' => 'Frontend',
])
->visible(fn (): bool => auth()->check());
});
}
}
93 changes: 93 additions & 0 deletions src/View/Components/BrandWithTenantSwitcher.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
<?php

namespace Eclipse\Core\View\Components;

use Eclipse\Core\Services\Registry;
use Exception;
use Filament\Facades\Filament;
use Filament\Support\Components\ViewComponent;
use Illuminate\Database\Eloquent\Model;

class BrandWithTenantSwitcher extends ViewComponent
{
protected string $view = 'eclipse::filament.components.brand-with-tenant-switcher';

public function getAppName(): string
{
return Registry::getSite()->name ?? config('app.name');
}

public function hasSpaMode(): bool
{
return Filament::getCurrentPanel()->hasSpaMode();
}

public function getDashboardUrl(): string
{
return '/'.trim(Filament::getCurrentPanel()->getPath(), '/');
}

public function getCurrentTenant(): ?Model
{
return filament()->getTenant();
}

public function getCurrentTenantName(): ?string
{
$currentTenant = $this->getCurrentTenant();

return $currentTenant ? filament()->getTenantName($currentTenant) : null;
}

public function getTenants(): array
{
if (! $this->isMultiSiteEnabled() || ! filament()->auth()->check()) {
return [];
}

$currentTenant = $this->getCurrentTenant();

return array_filter(
filament()->getUserTenants(filament()->auth()->user()),
fn (Model $tenant): bool => ! $tenant->is($currentTenant),
);
}

public function canSwitchTenants(): bool
{
return count($this->getTenants()) > 0;
}

public function hasFrontend(): bool
{
return collect(Filament::getPanels())->has('frontend');
}

public function getFrontendUrl(): string
{
if (! $this->hasFrontend()) {
return config('app.url');
}

try {
$currentTenant = $this->getCurrentTenant();
if ($currentTenant?->domain) {
return "https://{$currentTenant->domain}";
} else {
return config('app.url');
}
} catch (Exception $e) {
return config('app.url');
}
}

public function shouldShowDropdown(): bool
{
return $this->canSwitchTenants() || $this->hasFrontend();
}

private function isMultiSiteEnabled(): bool
{
return config('eclipse.multi_site', false);
}
}
62 changes: 62 additions & 0 deletions tests/Feature/Filament/BrandWithTenantSwitcherTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<?php

use Eclipse\Core\Models\Site;
use Eclipse\Core\Models\User;
use Eclipse\Core\View\Components\BrandWithTenantSwitcher;

test('brand component renders without tenants', function () {
$response = $this->get('/admin/login');

$response->assertStatus(200);
$response->assertSee(config('app.name'));
});

test('brand component renders with multi-site enabled', function () {
config(['eclipse.multi_site' => true]);

$response = $this->get('/admin/login');
$response->assertStatus(200);
$response->assertSee(config('app.name'));
});

test('brand component dropdown behavior depends on frontend and tenant availability', function () {
$component = new BrandWithTenantSwitcher;

$hasFrontend = $component->hasFrontend();
$canSwitchTenants = $component->canSwitchTenants();
$shouldShowDropdown = $component->shouldShowDropdown();

expect($shouldShowDropdown)->toBe($hasFrontend || $canSwitchTenants);

expect($hasFrontend)->toBeBool();
expect($canSwitchTenants)->toBeBool();
expect($shouldShowDropdown)->toBeBool();
});

test('tenant switcher logic handles multiple and single tenant scenarios', function () {
config(['eclipse.multi_site' => true]);

$user = User::factory()->create();
$site1 = Site::factory()->create(['name' => 'Primary Site']);
$site2 = Site::factory()->create(['name' => 'Secondary Site']);

$user->sites()->attach([$site1->id, $site2->id]);

expect($user->sites)->toHaveCount(2);
expect($user->sites->pluck('name'))->toContain('Primary Site', 'Secondary Site');

$singleSiteUser = User::factory()->create();
$singleSite = Site::factory()->create(['name' => 'Only Site']);
$singleSiteUser->sites()->attach($singleSite->id);

expect($singleSiteUser->sites)->toHaveCount(1);
expect($singleSiteUser->sites->first()->name)->toBe('Only Site');
});

test('brand component renders app name correctly', function () {
$component = new BrandWithTenantSwitcher;

$appName = $component->getAppName();

expect($appName)->toBe(config('app.name'));
});
15 changes: 15 additions & 0 deletions workbench/app/Providers/WorkbenchServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@

namespace Workbench\App\Providers;

use Eclipse\Common\CommonServiceProvider;
use Eclipse\Core\Providers\AdminPanelProvider;
use Eclipse\Frontend\Providers\FrontendPanelProvider;
use Illuminate\Support\ServiceProvider;
use Nben\FilamentRecordNav\FilamentRecordNavServiceProvider;

class WorkbenchServiceProvider extends ServiceProvider
{
Expand All @@ -12,7 +15,19 @@ class WorkbenchServiceProvider extends ServiceProvider
*/
public function register(): void
{
if (class_exists(CommonServiceProvider::class)) {
$this->app->register(CommonServiceProvider::class);
}

if (class_exists(FilamentRecordNavServiceProvider::class)) {
$this->app->register(FilamentRecordNavServiceProvider::class);
}

$this->app->register(AdminPanelProvider::class);

if (class_exists(FrontendPanelProvider::class)) {
$this->app->register(FrontendPanelProvider::class);
}
}

/**
Expand Down
Loading