Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
1 change: 0 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@
"require": {
"php": "^8.3",
"bezhansalleh/filament-language-switch": "^3.1",
"bezhansalleh/filament-panel-switch": "^1.1",
"bezhansalleh/filament-shield": "^3.3",
"datalinx/php-utils": "^2.5",
"dutchcodingcompany/filament-developer-logins": "^1.6",
Expand Down
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 @@ -22,6 +22,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 @@ -99,6 +100,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
26 changes: 2 additions & 24 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 Down Expand Up @@ -77,7 +77,7 @@ public function panel(Panel $panel): Panel
])
->topNavigation()
->brandLogo(
fn (): View => view('eclipse::filament.components.brand')
fn (): View => app(BrandWithTenantSwitcher::class)->render()
)
->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 @@ -171,13 +171,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 @@ -242,20 +235,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());
});
}
}
107 changes: 107 additions & 0 deletions src/View/Components/BrandWithTenantSwitcher.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
<?php

namespace Eclipse\Core\View\Components;

use Eclipse\Core\Services\Registry;
use Exception;
use Filament\Facades\Filament;
use Illuminate\Database\Eloquent\Model;
use Illuminate\View\Component;

class BrandWithTenantSwitcher extends Component
{
public function render()
{
return view('eclipse::filament.components.brand-with-tenant-switcher', [
'shouldShowDropdown' => $this->shouldShowDropdown(),
'getAppName' => $this->getAppName(),
'hasSpaMode' => $this->hasSpaMode(),
'getDashboardUrl' => $this->getDashboardUrl(),
'getCurrentTenant' => $this->getCurrentTenant(),
'getCurrentTenantName' => $this->getCurrentTenantName(),
'canSwitchTenants' => $this->canSwitchTenants(),
'getTenants' => $this->getTenants(),
'hasFrontend' => $this->hasFrontend(),
'getFrontendUrl' => $this->getFrontendUrl(),
]);
}

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'));
});
18 changes: 17 additions & 1 deletion workbench/app/Providers/WorkbenchServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +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 @@ -11,7 +15,19 @@ class WorkbenchServiceProvider extends ServiceProvider
*/
public function register(): void
{
$this->app->register(\Eclipse\Core\Providers\AdminPanelProvider::class);
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