Skip to content

Commit ca912b9

Browse files
thapacodes4uankitcodes4u
andauthored
feat: add new tenant menu
* feat: adding brand tenant switcher menu * fix: reverting some of the changes * fix: failing tests * fix: refactor tenant switcher component and fix frontend panel detection * fix: fixing the issue of frontend link not showing * fix: resolving conflict * refactor: use Filament ViewComponent pattern for BrandWithTenantSwitcher --------- Co-authored-by: ankitcodes4u <[email protected]>
1 parent 0b49be9 commit ca912b9

File tree

6 files changed

+239
-25
lines changed

6 files changed

+239
-25
lines changed
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
@if ($shouldShowDropdown())
2+
<div class="flex items-center gap-x-2">
3+
<a @if ($hasSpaMode()) wire:navigate @endif href="{{ $getDashboardUrl() }}" class="flex-1">
4+
<div class="fi-logo flex text-xl font-bold leading-5 tracking-tight text-gray-950 dark:text-white">
5+
{{ $getAppName() }}
6+
@if ($getCurrentTenant() && $getCurrentTenantName() !== $getAppName())
7+
<span class="text-gray-500 dark:text-gray-400 text-sm font-normal ml-2">
8+
- {{ $getCurrentTenantName() }}
9+
</span>
10+
@endif
11+
</div>
12+
</a>
13+
14+
<x-filament::dropdown teleport>
15+
<x-slot name="trigger">
16+
<button type="button"
17+
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">
18+
<x-filament::icon icon="heroicon-m-chevron-down" alias="panels::brand-tenant-menu.toggle-button"
19+
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" />
20+
</button>
21+
</x-slot>
22+
23+
<x-filament::dropdown.list>
24+
@if ($canSwitchTenants())
25+
@foreach ($getTenants() as $tenant)
26+
<x-filament::dropdown.list.item :href="route('filament.admin.pages.dashboard', ['tenant' => $tenant])" :image="filament()->getTenantAvatarUrl($tenant)" tag="a">
27+
{{ filament()->getTenantName($tenant) }}
28+
</x-filament::dropdown.list.item>
29+
@endforeach
30+
@endif
31+
32+
33+
@if ($hasFrontend())
34+
@if ($canSwitchTenants())
35+
<x-filament::dropdown.list.item tag="div"
36+
class="border-t border-gray-200 dark:border-gray-700 my-1"></x-filament::dropdown.list.item>
37+
@endif
38+
39+
<x-filament::dropdown.list.item :href="$getFrontendUrl()" tag="a" target="_blank"
40+
class="font-medium">
41+
<div class="flex items-center gap-2">
42+
<x-filament::icon icon="heroicon-s-globe-alt"
43+
class="h-4 w-4 text-gray-500 dark:text-gray-400" />
44+
Frontend
45+
<x-filament::icon icon="heroicon-s-arrow-top-right-on-square"
46+
class="h-3 w-3 text-gray-400 dark:text-gray-500 ml-auto" />
47+
</div>
48+
</x-filament::dropdown.list.item>
49+
@endif
50+
</x-filament::dropdown.list>
51+
</x-filament::dropdown>
52+
</div>
53+
@else
54+
<div class="fi-logo flex text-xl font-bold leading-5 tracking-tight text-gray-950 dark:text-white">
55+
{{ $getAppName() }}
56+
@if ($getCurrentTenant() && $getCurrentTenantName() !== $getAppName())
57+
<span class="text-gray-500 dark:text-gray-400 text-sm font-normal ml-2">
58+
- {{ $getCurrentTenantName() }}
59+
</span>
60+
@endif
61+
</div>
62+
@endif

src/EclipseServiceProvider.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
use Eclipse\Core\Providers\HorizonServiceProvider;
2828
use Eclipse\Core\Providers\TelescopeServiceProvider;
2929
use Eclipse\Core\Services\Registry;
30+
use Eclipse\Frontend\Providers\FrontendPanelProvider;
3031
use Filament\Facades\Filament;
3132
use Filament\Resources\Resource;
3233
use Filament\Support\Facades\FilamentAsset;
@@ -103,6 +104,10 @@ public function register(): self
103104
$this->app->register(AdminPanelProvider::class);
104105
}
105106

107+
if (class_exists(FrontendPanelProvider::class)) {
108+
$this->app->register(FrontendPanelProvider::class);
109+
}
110+
106111
if ($this->app->environment('local')) {
107112
$this->app->register(\Laravel\Telescope\TelescopeServiceProvider::class);
108113
$this->app->register(TelescopeServiceProvider::class);

src/Providers/AdminPanelProvider.php

Lines changed: 2 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
use BezhanSalleh\FilamentShield\Facades\FilamentShield;
66
use BezhanSalleh\FilamentShield\FilamentShieldPlugin;
77
use BezhanSalleh\FilamentShield\Middleware\SyncShieldTenant;
8-
use BezhanSalleh\PanelSwitch\PanelSwitch;
98
use DutchCodingCompany\FilamentDeveloperLogins\FilamentDeveloperLoginsPlugin;
109
use Eclipse\Common\CommonPlugin;
1110
use Eclipse\Common\Providers\GlobalSearchProvider;
@@ -16,6 +15,7 @@
1615
use Eclipse\Core\Models\Site;
1716
use Eclipse\Core\Models\User;
1817
use Eclipse\Core\Services\Registry;
18+
use Eclipse\Core\View\Components\BrandWithTenantSwitcher;
1919
use Eclipse\World\EclipseWorld;
2020
use Filament\Http\Middleware\Authenticate;
2121
use Filament\Http\Middleware\DisableBladeIconComponents;
@@ -41,7 +41,6 @@
4141
use Illuminate\Support\Facades\Blade;
4242
use Illuminate\Support\Facades\Schema;
4343
use Illuminate\View\Middleware\ShareErrorsFromSession;
44-
use Illuminate\View\View;
4544
use LaraZeus\SpatieTranslatable\SpatieTranslatablePlugin;
4645
use pxlrbt\FilamentEnvironmentIndicator\EnvironmentIndicatorPlugin;
4746
use pxlrbt\FilamentSpotlight\SpotlightPlugin;
@@ -77,7 +76,7 @@ public function panel(Panel $panel): Panel
7776
])
7877
->topNavigation()
7978
->brandLogo(
80-
fn (): View => view('eclipse::filament.components.brand')
79+
fn () => app(BrandWithTenantSwitcher::class)
8180
)
8281
->discoverResources(in: app_path('Filament/Resources'), for: 'App\\Filament\\Resources')
8382
->discoverResources(in: $package_src.'Filament/Resources', for: 'Eclipse\\Core\\Filament\\Resources')
@@ -170,13 +169,6 @@ public function panel(Panel $panel): Panel
170169
)
171170
->viteTheme('resources/css/filament/admin/theme.css');
172171

173-
if ($hasTenantMenu) {
174-
$panel->renderHook(
175-
PanelsRenderHook::GLOBAL_SEARCH_END,
176-
fn () => view('eclipse::filament.components.tenant-menu')
177-
);
178-
}
179-
180172
// If the Pro version of the Spotlight plugin is installed, use that, otherwise use the free version
181173
if (class_exists(\pxlrbt\FilamentSpotlightPro\SpotlightPlugin::class)) {
182174
/** @noinspection PhpFullyQualifiedNameUsageInspection */
@@ -241,20 +233,5 @@ public function boot(): void
241233

242234
// Load customized translations for Filament Shield
243235
$this->loadTranslationsFrom(__DIR__.'/../../resources/lang/vendor/filament-shield', 'filament-shield');
244-
245-
// Configure Panel Switch
246-
PanelSwitch::configureUsing(function (PanelSwitch $panelSwitch) {
247-
$panelSwitch
248-
->simple()
249-
->icons([
250-
'admin' => 'heroicon-s-cog-6-tooth',
251-
'frontend' => 'heroicon-s-globe-alt',
252-
])
253-
->labels([
254-
'admin' => 'Admin Panel',
255-
'frontend' => 'Frontend',
256-
])
257-
->visible(fn (): bool => auth()->check());
258-
});
259236
}
260237
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
<?php
2+
3+
namespace Eclipse\Core\View\Components;
4+
5+
use Eclipse\Core\Services\Registry;
6+
use Exception;
7+
use Filament\Facades\Filament;
8+
use Filament\Support\Components\ViewComponent;
9+
use Illuminate\Database\Eloquent\Model;
10+
11+
class BrandWithTenantSwitcher extends ViewComponent
12+
{
13+
protected string $view = 'eclipse::filament.components.brand-with-tenant-switcher';
14+
15+
public function getAppName(): string
16+
{
17+
return Registry::getSite()->name ?? config('app.name');
18+
}
19+
20+
public function hasSpaMode(): bool
21+
{
22+
return Filament::getCurrentPanel()->hasSpaMode();
23+
}
24+
25+
public function getDashboardUrl(): string
26+
{
27+
return '/'.trim(Filament::getCurrentPanel()->getPath(), '/');
28+
}
29+
30+
public function getCurrentTenant(): ?Model
31+
{
32+
return filament()->getTenant();
33+
}
34+
35+
public function getCurrentTenantName(): ?string
36+
{
37+
$currentTenant = $this->getCurrentTenant();
38+
39+
return $currentTenant ? filament()->getTenantName($currentTenant) : null;
40+
}
41+
42+
public function getTenants(): array
43+
{
44+
if (! $this->isMultiSiteEnabled() || ! filament()->auth()->check()) {
45+
return [];
46+
}
47+
48+
$currentTenant = $this->getCurrentTenant();
49+
50+
return array_filter(
51+
filament()->getUserTenants(filament()->auth()->user()),
52+
fn (Model $tenant): bool => ! $tenant->is($currentTenant),
53+
);
54+
}
55+
56+
public function canSwitchTenants(): bool
57+
{
58+
return count($this->getTenants()) > 0;
59+
}
60+
61+
public function hasFrontend(): bool
62+
{
63+
return collect(Filament::getPanels())->has('frontend');
64+
}
65+
66+
public function getFrontendUrl(): string
67+
{
68+
if (! $this->hasFrontend()) {
69+
return config('app.url');
70+
}
71+
72+
try {
73+
$currentTenant = $this->getCurrentTenant();
74+
if ($currentTenant?->domain) {
75+
return "https://{$currentTenant->domain}";
76+
} else {
77+
return config('app.url');
78+
}
79+
} catch (Exception $e) {
80+
return config('app.url');
81+
}
82+
}
83+
84+
public function shouldShowDropdown(): bool
85+
{
86+
return $this->canSwitchTenants() || $this->hasFrontend();
87+
}
88+
89+
private function isMultiSiteEnabled(): bool
90+
{
91+
return config('eclipse.multi_site', false);
92+
}
93+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<?php
2+
3+
use Eclipse\Core\Models\Site;
4+
use Eclipse\Core\Models\User;
5+
use Eclipse\Core\View\Components\BrandWithTenantSwitcher;
6+
7+
test('brand component renders without tenants', function () {
8+
$response = $this->get('/admin/login');
9+
10+
$response->assertStatus(200);
11+
$response->assertSee(config('app.name'));
12+
});
13+
14+
test('brand component renders with multi-site enabled', function () {
15+
config(['eclipse.multi_site' => true]);
16+
17+
$response = $this->get('/admin/login');
18+
$response->assertStatus(200);
19+
$response->assertSee(config('app.name'));
20+
});
21+
22+
test('brand component dropdown behavior depends on frontend and tenant availability', function () {
23+
$component = new BrandWithTenantSwitcher;
24+
25+
$hasFrontend = $component->hasFrontend();
26+
$canSwitchTenants = $component->canSwitchTenants();
27+
$shouldShowDropdown = $component->shouldShowDropdown();
28+
29+
expect($shouldShowDropdown)->toBe($hasFrontend || $canSwitchTenants);
30+
31+
expect($hasFrontend)->toBeBool();
32+
expect($canSwitchTenants)->toBeBool();
33+
expect($shouldShowDropdown)->toBeBool();
34+
});
35+
36+
test('tenant switcher logic handles multiple and single tenant scenarios', function () {
37+
config(['eclipse.multi_site' => true]);
38+
39+
$user = User::factory()->create();
40+
$site1 = Site::factory()->create(['name' => 'Primary Site']);
41+
$site2 = Site::factory()->create(['name' => 'Secondary Site']);
42+
43+
$user->sites()->attach([$site1->id, $site2->id]);
44+
45+
expect($user->sites)->toHaveCount(2);
46+
expect($user->sites->pluck('name'))->toContain('Primary Site', 'Secondary Site');
47+
48+
$singleSiteUser = User::factory()->create();
49+
$singleSite = Site::factory()->create(['name' => 'Only Site']);
50+
$singleSiteUser->sites()->attach($singleSite->id);
51+
52+
expect($singleSiteUser->sites)->toHaveCount(1);
53+
expect($singleSiteUser->sites->first()->name)->toBe('Only Site');
54+
});
55+
56+
test('brand component renders app name correctly', function () {
57+
$component = new BrandWithTenantSwitcher;
58+
59+
$appName = $component->getAppName();
60+
61+
expect($appName)->toBe(config('app.name'));
62+
});

workbench/app/Providers/WorkbenchServiceProvider.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@
22

33
namespace Workbench\App\Providers;
44

5+
use Eclipse\Common\CommonServiceProvider;
56
use Eclipse\Core\Providers\AdminPanelProvider;
7+
use Eclipse\Frontend\Providers\FrontendPanelProvider;
68
use Illuminate\Support\ServiceProvider;
9+
use Nben\FilamentRecordNav\FilamentRecordNavServiceProvider;
710

811
class WorkbenchServiceProvider extends ServiceProvider
912
{
@@ -12,7 +15,19 @@ class WorkbenchServiceProvider extends ServiceProvider
1215
*/
1316
public function register(): void
1417
{
18+
if (class_exists(CommonServiceProvider::class)) {
19+
$this->app->register(CommonServiceProvider::class);
20+
}
21+
22+
if (class_exists(FilamentRecordNavServiceProvider::class)) {
23+
$this->app->register(FilamentRecordNavServiceProvider::class);
24+
}
25+
1526
$this->app->register(AdminPanelProvider::class);
27+
28+
if (class_exists(FrontendPanelProvider::class)) {
29+
$this->app->register(FrontendPanelProvider::class);
30+
}
1631
}
1732

1833
/**

0 commit comments

Comments
 (0)