Unify logged-in profile dropdown with platform account menu.
Deploy Ladill Transfer / deploy (push) Successful in 42s
Deploy Ladill Transfer / deploy (push) Successful in 42s
Wire UserProfileMenu and shared Blade partial into topbar, mobile nav, and layouts so menu items match the account portal (Ladill Mail excluded). Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
namespace App\Support;
|
||||
|
||||
use Illuminate\Contracts\Auth\Authenticatable;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
class UserProfileMenu
|
||||
{
|
||||
/** @return list<array<string, mixed>> */
|
||||
public static function items(?Authenticatable $user = null): array
|
||||
{
|
||||
$user ??= auth()->user();
|
||||
|
||||
if (! $user) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$items = [];
|
||||
|
||||
foreach ([
|
||||
['label' => 'Home', 'path' => '', 'host' => 'home'],
|
||||
['label' => 'Profile', 'path' => 'profile'],
|
||||
['label' => 'Account Settings', 'path' => 'account-settings'],
|
||||
['label' => 'Dashboard', 'path' => 'dashboard'],
|
||||
['label' => 'Billing', 'path' => 'billing'],
|
||||
] as $link) {
|
||||
$items[] = [
|
||||
'type' => 'link',
|
||||
'label' => $link['label'],
|
||||
'href' => self::platformUrl($link['host'] ?? 'account', $link['path']),
|
||||
];
|
||||
}
|
||||
|
||||
if (self::userIsAdmin($user)) {
|
||||
$items[] = [
|
||||
'type' => 'link',
|
||||
'label' => 'Admin',
|
||||
'href' => self::platformUrl('account', 'admin'),
|
||||
];
|
||||
}
|
||||
|
||||
if (Route::has('logout')) {
|
||||
$items[] = [
|
||||
'type' => 'logout',
|
||||
'label' => 'Logout',
|
||||
'action' => route('logout'),
|
||||
];
|
||||
}
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
private static function platformUrl(string $host, string $path = ''): string
|
||||
{
|
||||
if ($host === 'home') {
|
||||
if (function_exists('ladill_home_url')) {
|
||||
return ladill_home_url($path);
|
||||
}
|
||||
|
||||
$domain = config('app.home_domain');
|
||||
if (! $domain) {
|
||||
$platform = (string) config('app.platform_domain', parse_url((string) config('app.url', ''), PHP_URL_HOST) ?: '');
|
||||
$domain = $platform !== '' ? 'home.'.$platform : (string) config('app.account_domain');
|
||||
}
|
||||
|
||||
return self::absoluteUrl((string) $domain, $path);
|
||||
}
|
||||
|
||||
if (function_exists('ladill_account_url')) {
|
||||
return ladill_account_url($path);
|
||||
}
|
||||
|
||||
return self::absoluteUrl((string) config('app.account_domain'), $path);
|
||||
}
|
||||
|
||||
private static function absoluteUrl(string $domain, string $path = ''): string
|
||||
{
|
||||
$base = 'https://'.trim($domain, '/');
|
||||
|
||||
if ($path === '') {
|
||||
return $base;
|
||||
}
|
||||
|
||||
return $base.'/'.ltrim($path, '/');
|
||||
}
|
||||
|
||||
private static function userIsAdmin(Authenticatable $user): bool
|
||||
{
|
||||
if (method_exists($user, 'isAdmin')) {
|
||||
return $user->isAdmin();
|
||||
}
|
||||
|
||||
return (bool) ($user->is_admin ?? false);
|
||||
}
|
||||
}
|
||||
@@ -45,8 +45,7 @@
|
||||
'profileActive' => false,
|
||||
'profileName' => $navUser?->name ?? '',
|
||||
'profileSubtitle' => $navUser?->email ?? '',
|
||||
'profileMenuItems' => [
|
||||
['type' => 'link', 'label' => 'Profile', 'href' => $navAcct.'/profile'],
|
||||
'profileMenuItems' => \App\Support\UserProfileMenu::items($navUser),
|
||||
['type' => 'link', 'label' => 'Account Settings', 'href' => $navAcct.'/account-settings'],
|
||||
['type' => 'logout', 'label' => 'Logout', 'action' => route('logout')],
|
||||
],
|
||||
|
||||
@@ -73,8 +73,7 @@
|
||||
'profileActive' => request()->routeIs('account.settings'),
|
||||
'profileName' => $navUser?->name ?? '',
|
||||
'profileSubtitle' => $navUser?->email ?? '',
|
||||
'profileMenuItems' => [
|
||||
['type' => 'link', 'label' => 'Settings', 'href' => route('account.settings')],
|
||||
'profileMenuItems' => \App\Support\UserProfileMenu::items($navUser),
|
||||
['type' => 'link', 'label' => 'Account Settings', 'href' => ladill_account_url('account-settings')],
|
||||
['type' => 'link', 'label' => 'Overview', 'href' => route('transfer.dashboard')],
|
||||
['type' => 'logout', 'label' => 'Logout', 'action' => route('logout')],
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
if ($profileMenuItems === [] && $profileUrl !== '#') {
|
||||
$profileMenuItems = [['type' => 'link', 'label' => 'Profile', 'href' => $profileUrl]];
|
||||
}
|
||||
$searchLabel = $searchLabel ?? 'Search';
|
||||
$navActive = fn (bool $active) => $active ? 'text-indigo-600' : 'text-slate-600';
|
||||
@endphp
|
||||
<div x-data="{ profileOpen: false }" class="lg:hidden">
|
||||
@@ -27,7 +26,7 @@
|
||||
<a href="{{ $searchUrl }}"
|
||||
class="flex flex-col items-center justify-center gap-1 px-2 py-2 transition hover:text-slate-900 {{ $navActive($searchActive ?? false) }}">
|
||||
<svg class="h-6 w-6" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607Z"/></svg>
|
||||
<span class="text-[10px] font-medium">{{ $searchLabel }}</span>
|
||||
<span class="text-[10px] font-medium">Search</span>
|
||||
</a>
|
||||
|
||||
@if (!empty($centerCompose))
|
||||
@@ -114,25 +113,11 @@
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="space-y-1 p-2 pb-4">
|
||||
@foreach ($profileMenuItems as $item)
|
||||
@if (($item['type'] ?? 'link') === 'link')
|
||||
<a href="{{ $item['href'] }}"
|
||||
@click="profileOpen = false"
|
||||
class="block rounded-xl px-4 py-3 text-sm font-medium text-slate-700 transition hover:bg-slate-100">
|
||||
{{ $item['label'] }}
|
||||
</a>
|
||||
@elseif (($item['type'] ?? '') === 'logout')
|
||||
<form method="POST" action="{{ $item['action'] }}" class="border-t border-slate-100 pt-2">
|
||||
@csrf
|
||||
<button type="submit"
|
||||
class="w-full rounded-xl px-4 py-3 text-left text-sm font-medium text-rose-600 transition hover:bg-rose-50">
|
||||
{{ $item['label'] }}
|
||||
</button>
|
||||
</form>
|
||||
@endif
|
||||
@endforeach
|
||||
</div>
|
||||
@include('partials.user-profile-menu', [
|
||||
'items' => $profileMenuItems,
|
||||
'variant' => 'sheet',
|
||||
'onNavigate' => 'profileOpen = false',
|
||||
])
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -122,15 +122,11 @@
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<div x-show="profileOpen" x-cloak x-transition @click.outside="profileOpen = false"
|
||||
class="absolute right-0 z-20 mt-2 w-48 rounded-xl border border-slate-200 bg-white p-1 shadow-lg">
|
||||
<a href="{{ route('account.settings') }}" class="block rounded-lg px-3 py-2 text-sm text-slate-700 hover:bg-slate-100">Settings</a>
|
||||
<a href="{{ ladill_account_url('account-settings') }}" class="block rounded-lg px-3 py-2 text-sm text-slate-700 hover:bg-slate-100">Account Settings</a>
|
||||
<a href="{{ route('transfer.dashboard') }}" class="block rounded-lg px-3 py-2 text-sm text-slate-700 hover:bg-slate-100">Dashboard</a>
|
||||
<form method="POST" action="{{ route('logout') }}">
|
||||
@csrf
|
||||
<button type="submit" class="w-full rounded-lg px-3 py-2 text-left text-sm text-rose-600 hover:bg-rose-50">Logout</button>
|
||||
</form>
|
||||
<div x-show="profileOpen" x-cloak x-transition
|
||||
class="absolute right-0 z-50 mt-2 w-56 rounded-xl border border-slate-200 bg-white shadow-lg">
|
||||
@include('partials.user-profile-menu', [
|
||||
'items' => \App\Support\UserProfileMenu::items($user),
|
||||
])
|
||||
</div>
|
||||
</div>
|
||||
@auth
|
||||
|
||||
@@ -238,7 +238,7 @@
|
||||
@endif
|
||||
</a>
|
||||
|
||||
<div class="relative hidden lg:block">
|
||||
<div class="relative hidden lg:block" x-data="{ profileOpen: false }" @click.outside="profileOpen = false">
|
||||
<button type="button"
|
||||
@click="profileOpen = !profileOpen"
|
||||
class="inline-flex items-center gap-2 rounded-full border border-slate-200 px-2 py-1.5 text-slate-700 hover:bg-slate-50">
|
||||
@@ -255,16 +255,12 @@
|
||||
</button>
|
||||
|
||||
<div x-show="profileOpen" x-cloak x-transition @click.outside="profileOpen = false"
|
||||
class="absolute right-0 z-20 mt-2 w-48 rounded-xl border border-slate-200 bg-white p-1 shadow-lg">
|
||||
<a href="{{ route('profile.edit') }}" class="block rounded-lg px-3 py-2 text-sm text-slate-700 hover:bg-slate-100">Profile</a>
|
||||
<a href="{{ route('account.settings') }}" class="block rounded-lg px-3 py-2 text-sm text-slate-700 hover:bg-slate-100">Account Settings</a>
|
||||
<a href="{{ route('transfer.dashboard') }}" class="block rounded-lg px-3 py-2 text-sm text-slate-700 hover:bg-slate-100">Dashboard</a>
|
||||
<form method="POST" action="{{ route('logout') }}">
|
||||
@csrf
|
||||
<button type="submit" class="w-full rounded-lg px-3 py-2 text-left text-sm text-rose-600 hover:bg-rose-50">Logout</button>
|
||||
</form>
|
||||
</div>
|
||||
class="absolute right-0 z-50 mt-2 w-56 rounded-xl border border-slate-200 bg-white shadow-lg">
|
||||
@include('partials.user-profile-menu', [
|
||||
'items' => \App\Support\UserProfileMenu::items($user),
|
||||
])
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@include('partials.mobile-header-cart', [
|
||||
'cartUrl' => $cartRoute,
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
@props([
|
||||
'items' => [],
|
||||
'user' => null,
|
||||
'showUser' => false,
|
||||
'variant' => 'dropdown',
|
||||
'onNavigate' => null,
|
||||
])
|
||||
|
||||
@php
|
||||
$linkClass = match ($variant) {
|
||||
'dark' => 'block rounded-md px-3 py-2 text-sm text-slate-200 hover:bg-slate-800',
|
||||
'sheet' => 'block rounded-xl px-4 py-3 text-sm font-medium text-slate-700 transition hover:bg-slate-100',
|
||||
default => 'block rounded-lg px-3 py-2 text-sm text-slate-700 hover:bg-slate-100',
|
||||
};
|
||||
|
||||
$logoutClass = match ($variant) {
|
||||
'dark' => 'w-full rounded-md px-3 py-2 text-left text-sm text-rose-300 hover:bg-rose-950/40',
|
||||
'sheet' => 'w-full rounded-xl px-4 py-3 text-left text-sm font-medium text-rose-600 transition hover:bg-rose-50',
|
||||
default => 'w-full rounded-lg px-3 py-2 text-left text-sm text-rose-600 hover:bg-rose-50',
|
||||
};
|
||||
|
||||
$logoutWrapperClass = match ($variant) {
|
||||
'sheet' => 'border-t border-slate-100 pt-2',
|
||||
'dark' => '',
|
||||
default => '',
|
||||
};
|
||||
|
||||
$dividerClass = match ($variant) {
|
||||
'dark' => 'my-1 border-t border-slate-800',
|
||||
default => 'my-1 border-t border-slate-100',
|
||||
};
|
||||
|
||||
$containerClass = match ($variant) {
|
||||
'dark' => 'mt-2 rounded-lg border border-slate-800 bg-slate-900 p-1',
|
||||
'sheet' => 'space-y-1 p-2 pb-4',
|
||||
default => 'p-1',
|
||||
};
|
||||
@endphp
|
||||
|
||||
<div {{ $attributes->merge(['class' => $containerClass]) }}>
|
||||
@if ($showUser && $user)
|
||||
<div class="px-3 py-2">
|
||||
<p class="truncate text-sm font-semibold text-slate-900">{{ $user->name ?? 'Your account' }}</p>
|
||||
<p class="truncate text-xs text-slate-400">{{ $user->email }}</p>
|
||||
</div>
|
||||
<div class="{{ $dividerClass }}"></div>
|
||||
@endif
|
||||
|
||||
@foreach ($items as $item)
|
||||
@if (($item['type'] ?? 'link') === 'link')
|
||||
<a href="{{ $item['href'] }}"
|
||||
class="{{ $linkClass }}"
|
||||
@if ($onNavigate) @click="{{ $onNavigate }}" @endif>
|
||||
{{ $item['label'] }}
|
||||
</a>
|
||||
@elseif (($item['type'] ?? '') === 'logout')
|
||||
@if ($variant !== 'sheet')
|
||||
<div class="{{ $dividerClass }}"></div>
|
||||
@endif
|
||||
<form method="POST" action="{{ $item['action'] }}" class="{{ $logoutWrapperClass }}">
|
||||
@csrf
|
||||
<button type="submit" class="{{ $logoutClass }}">
|
||||
{{ $item['label'] }}
|
||||
</button>
|
||||
</form>
|
||||
@endif
|
||||
@endforeach
|
||||
</div>
|
||||
Reference in New Issue
Block a user