Add mobile topbar titles and show Afia on all mobile headers.
Deploy Ladill QR Plus / deploy (push) Successful in 33s

Route-based subtitle and page titles replace plain app labels in mobile topbars, and the Afia AI button is visible on mobile across topbars and mobile-page-header screens.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
isaacclad
2026-06-07 16:30:16 +00:00
co-authored by Cursor
parent 5fb2de31c8
commit 40981be252
7 changed files with 90 additions and 9 deletions
+6
View File
@@ -6,6 +6,8 @@ use App\Models\QrCode;
use App\Policies\QrCodePolicy; use App\Policies\QrCodePolicy;
use Illuminate\Support\Facades\Gate; use Illuminate\Support\Facades\Gate;
use Illuminate\Support\ServiceProvider; use Illuminate\Support\ServiceProvider;
use App\Support\MobileTopbar;
use Illuminate\Support\Facades\View;
class AppServiceProvider extends ServiceProvider class AppServiceProvider extends ServiceProvider
{ {
@@ -17,5 +19,9 @@ class AppServiceProvider extends ServiceProvider
public function boot(): void public function boot(): void
{ {
Gate::policy(QrCode::class, QrCodePolicy::class); Gate::policy(QrCode::class, QrCodePolicy::class);
View::composer(['partials.topbar', 'partials.topbar-qr'], function ($view) {
$view->with(MobileTopbar::resolve());
});
} }
} }
+25
View File
@@ -0,0 +1,25 @@
<?php
namespace App\Support;
class MobileTopbar
{
public static function resolve(): array
{
$config = config('mobile-topbar', []);
$subtitle = $config['app_name'] ?? 'Ladill';
$title = $config['default_title'] ?? 'Overview';
foreach ($config['patterns'] ?? [] as $pattern => $patternTitle) {
if (request()->routeIs($pattern)) {
$title = is_callable($patternTitle) ? $patternTitle() : $patternTitle;
break;
}
}
return [
'mobileTopbarSubtitle' => $subtitle,
'mobileTopbarTitle' => $title,
];
}
}
+19
View File
@@ -0,0 +1,19 @@
<?php
return [
'app_name' => 'QR Plus',
'default_title' => 'Overview',
'patterns' => [
'qr.dashboard' => 'Overview',
'qr.search' => 'Search',
'user.qr-codes.index' => 'QR codes',
'user.qr-codes.create' => 'Create QR code',
'user.qr-codes.show' => 'QR code',
'account.wallet' => 'Wallet',
'account.billing' => 'Billing',
'account.team*' => 'Team',
'account.settings' => 'Settings',
'account.developers*' => 'Developers',
'notifications.*' => 'Notifications',
],
];
@@ -3,6 +3,7 @@
'subtitle' => null, 'subtitle' => null,
'backUrl' => null, 'backUrl' => null,
'badge' => null, 'badge' => null,
'hideAfia' => false,
]) ])
<div class="sticky top-0 z-20 border-b border-slate-200 bg-white/95 backdrop-blur lg:hidden"> <div class="sticky top-0 z-20 border-b border-slate-200 bg-white/95 backdrop-blur lg:hidden">
@@ -22,6 +23,9 @@
@if ($badge) @if ($badge)
<span class="rounded-full bg-slate-100 px-3 py-1 text-xs font-medium text-slate-600">{{ $badge }}</span> <span class="rounded-full bg-slate-100 px-3 py-1 text-xs font-medium text-slate-600">{{ $badge }}</span>
@endif @endif
@unless ($hideAfia)
@include('partials.afia-button', ['compact' => true])
@endunless
{{ $actions ?? '' }} {{ $actions ?? '' }}
</div> </div>
{{ $slot }} {{ $slot }}
@@ -0,0 +1,24 @@
@props([
'compact' => false,
'handler' => 'dispatch',
])
@auth
<button type="button"
@if ($handler === 'openAfia')
@click="openAfia()"
@else
@click="$dispatch('afia-open')"
@endif
@class([
'inline-flex shrink-0 items-center justify-center gap-2 rounded-xl bg-gradient-to-r from-indigo-700 via-blue-600 to-emerald-400 text-sm font-semibold text-white shadow-sm transition hover:opacity-95',
'h-9 w-9 px-0 lg:h-auto lg:w-auto lg:px-4 lg:py-2' => $compact,
'px-3 py-2 lg:px-4' => ! $compact,
])
aria-label="Open Afia AI assistant">
<svg class="h-4 w-4" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
<path d="M12 2.25c.414 0 .75.336.75.75a5.25 5.25 0 0 0 5.25 5.25.75.75 0 0 1 0 1.5A5.25 5.25 0 0 0 12.75 15a.75.75 0 0 1-1.5 0A5.25 5.25 0 0 0 6 9.75a.75.75 0 0 1 0-1.5A5.25 5.25 0 0 0 11.25 3a.75.75 0 0 1 .75-.75Zm6.75 11.25a.75.75 0 0 1 .75.75A2.25 2.25 0 0 0 21.75 16.5a.75.75 0 0 1 0 1.5A2.25 2.25 0 0 0 19.5 20.25a.75.75 0 0 1-1.5 0A2.25 2.25 0 0 0 15.75 18a.75.75 0 0 1 0-1.5A2.25 2.25 0 0 0 18 14.25a.75.75 0 0 1 .75-.75ZM5.25 14.25a.75.75 0 0 1 .75.75 3 3 0 0 0 3 3 .75.75 0 0 1 0 1.5 3 3 0 0 0-3 3 .75.75 0 0 1-1.5 0 3 3 0 0 0-3-3 .75.75 0 0 1 0-1.5 3 3 0 0 0 3-3 .75.75 0 0 1 .75-.75Z"/>
</svg>
<span @class(['sr-only lg:not-sr-only lg:inline' => $compact, 'hidden sm:inline' => ! $compact])>AI</span>
</button>
@endauth
@@ -0,0 +1,10 @@
@php
$subtitle = $mobileTopbarSubtitle ?? null;
$title = $mobileTopbarTitle ?? 'Overview';
@endphp
<div class="min-w-0 flex-1 lg:hidden">
@if ($subtitle)
<p class="text-xs font-semibold uppercase tracking-[0.18em] text-slate-400">{{ $subtitle }}</p>
@endif
<h1 class="truncate text-base font-semibold text-slate-900">{{ $title }}</h1>
</div>
+2 -9
View File
@@ -12,7 +12,7 @@
<button @click="sidebarOpen = !sidebarOpen" class="shrink-0 text-slate-500 hover:text-slate-700 lg:hidden"> <button @click="sidebarOpen = !sidebarOpen" class="shrink-0 text-slate-500 hover:text-slate-700 lg:hidden">
<svg class="h-6 w-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"/></svg> <svg class="h-6 w-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"/></svg>
</button> </button>
<p class="truncate text-sm font-medium text-slate-700 lg:hidden">Ladill QR Plus</p> @include('partials.mobile-topbar-title')
<div class="relative hidden min-w-0 flex-1 max-w-md lg:block" <div class="relative hidden min-w-0 flex-1 max-w-md lg:block"
x-data="topbarSearch({ searchUrl: @js(route('qr.search')) })" x-data="topbarSearch({ searchUrl: @js(route('qr.search')) })"
@click.outside="open = false" @click.outside="open = false"
@@ -179,15 +179,8 @@
</form> </form>
</div> </div>
</div> </div>
@auth @auth
<button type="button" @click="$dispatch('afia-open')" @include('partials.afia-button', ['compact' => true])
class="hidden lg:inline-flex items-center gap-2 rounded-xl bg-gradient-to-r from-indigo-700 via-blue-600 to-emerald-400 px-4 py-2 text-sm font-semibold text-white shadow-sm transition hover:opacity-95">
<svg class="h-4 w-4" viewBox="0 0 24 24" fill="currentColor">
<path d="M12 2.25c.414 0 .75.336.75.75a5.25 5.25 0 0 0 5.25 5.25.75.75 0 0 1 0 1.5A5.25 5.25 0 0 0 12.75 15a.75.75 0 0 1-1.5 0A5.25 5.25 0 0 0 6 9.75a.75.75 0 0 1 0-1.5A5.25 5.25 0 0 0 11.25 3a.75.75 0 0 1 .75-.75Zm6.75 11.25a.75.75 0 0 1 .75.75A2.25 2.25 0 0 0 21.75 16.5a.75.75 0 0 1 0 1.5A2.25 2.25 0 0 0 19.5 20.25a.75.75 0 0 1-1.5 0A2.25 2.25 0 0 0 15.75 18a.75.75 0 0 1 0-1.5A2.25 2.25 0 0 0 18 14.25a.75.75 0 0 1 .75-.75ZM5.25 14.25a.75.75 0 0 1 .75.75 3 3 0 0 0 3 3 .75.75 0 0 1 0 1.5 3 3 0 0 0-3 3 .75.75 0 0 1-1.5 0 3 3 0 0 0-3-3 .75.75 0 0 1 0-1.5 3 3 0 0 0 3-3 .75.75 0 0 1 .75-.75Z"/>
</svg>
<span>AI</span>
</button>
@endauth @endauth
@include('partials.launcher') @include('partials.launcher')