fix(ui): fix subscription access and mobile menu positioning

Add the subscription link for cloud team admins, anchor the mobile account menu below its trigger, and protect the subscription modal from Livewire morphing.
This commit is contained in:
Andras Bacsai 2026-08-16 08:02:23 +02:00
parent 0a35909ddf
commit bb45668a80
6 changed files with 64 additions and 2 deletions

View file

@ -1959,6 +1959,19 @@ @media (max-width: 767px) {
transform: translate(-50%, -50%) !important;
z-index: 9999 !important;
}
/* The mobile top bar's backdrop filter makes it the containing block for
fixed descendants. Keep this menu anchored below its trigger instead
of centering it within the short top bar. */
.listbox-panel.top-user-menu-panel {
position: absolute !important;
top: calc(100% + 0.25rem) !important;
right: 0 !important;
left: auto !important;
max-height: calc(100dvh - 4.5rem) !important;
overflow-y: auto !important;
transform: none !important;
}
}
.listbox-option {

View file

@ -167,6 +167,16 @@ class="{{ request()->is('security*') ? 'menu-item-active menu-item' : 'menu-item
<span class="menu-item-label" :class="collapsed && 'lg:hidden'">Keys & Tokens</span>
</a>
</li>
@if (isCloud() && auth()->user()->isAdmin())
<li>
<a title="Subscription" {{ wireNavigate() }}
class="{{ request()->is('subscription*') ? 'menu-item-active menu-item' : 'menu-item' }}"
:class="collapsed && 'lg:justify-center lg:px-0'" href="{{ route('subscription.show') }}">
<x-reicon name="subscription" class="menu-item-icon" />
<span class="menu-item-label" :class="collapsed && 'lg:hidden'">Subscription</span>
</a>
</li>
@endif
<li>
<a title="Tags" {{ wireNavigate() }}
class="{{ request()->is('tags*') ? 'menu-item-active menu-item' : 'menu-item' }}"

View file

@ -82,7 +82,7 @@ class="flex size-5 shrink-0 items-center justify-center rounded-full bg-neutral-
</button>
<div x-show="open" x-cloak @class([
'listbox-panel z-[90]! max-h-none! w-52! min-w-0! overflow-visible! animate-in fade-in zoom-in-95 duration-150',
'top-user-menu-panel listbox-panel z-[90]! max-h-none! w-52! min-w-0! overflow-visible! animate-in fade-in zoom-in-95 duration-150',
'right-0! left-auto!' => ! $sidebar,
'bottom-full! left-0! right-auto! top-auto! mb-1!' => $sidebar,
'origin-bottom-left' => $sidebar,

View file

@ -84,7 +84,7 @@
@endif
{{-- Adjust Server Limit Modal --}}
<template x-teleport="body">
<template x-teleport="body" wire:ignore>
<div x-show="showModal"
class="fixed top-0 left-0 z-99 flex items-center justify-center w-screen h-screen p-4" x-cloak>
<div x-show="showModal" class="absolute inset-0 w-full h-full bg-black/20 backdrop-blur-xs"

View file

@ -62,6 +62,33 @@
$response->assertDontSee('>Search</span>', false);
});
test('subscribed cloud sidebar shows subscription link for team admins', function () {
Subscription::create([
'team_id' => $this->team->id,
'stripe_subscription_id' => 'sub_active',
'stripe_customer_id' => 'cus_active',
'stripe_invoice_paid' => true,
'stripe_plan_id' => 'price_active',
'stripe_cancel_at_period_end' => false,
'stripe_past_due' => false,
]);
Once::flush();
session(['currentTeam' => $this->team->fresh()]);
$html = view('components.navbar')->render();
expect($html)
->toContain('title="Subscription"')
->toContain(route('subscription.show'));
});
test('subscription adjustment modal is protected from livewire morphing', function () {
$view = file_get_contents(resource_path('views/livewire/subscription/actions.blade.php'));
expect($view)->toContain('<template x-teleport="body" wire:ignore>');
});
test('subscription pricing page does not render a single-item pricing tab strip', function () {
$html = view('components.dashboard.navbar', [
'section' => 'subscription',

View file

@ -31,6 +31,18 @@
expect($stylesheet)->toContain('@import "tw-animate-css";');
});
it('positions the account menu below the mobile trigger', function () {
$menu = file_get_contents(resource_path('views/components/top-user-menu.blade.php'));
$stylesheet = file_get_contents(resource_path('css/app.css'));
expect($menu)->toContain('top-user-menu-panel');
expect($stylesheet)
->toContain('.listbox-panel.top-user-menu-panel')
->toContain('top: calc(100% + 0.25rem) !important;')
->toContain('transform: none !important;')
->toContain('max-height: calc(100dvh - 4.5rem) !important;');
});
it('changes appearance from a submenu instead of navigating to a separate page', function () {
$menu = file_get_contents(resource_path('views/components/top-user-menu.blade.php'));