Add mobile topbar titles and show Afia on all mobile headers.
Deploy Ladill Hosting / deploy (push) Successful in 26s
Deploy Ladill Hosting / deploy (push) Successful in 26s
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:
@@ -6,6 +6,8 @@ use App\Models\HostingAccount;
|
||||
use App\Policies\HostingAccountPolicy;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use App\Support\MobileTopbar;
|
||||
use Illuminate\Support\Facades\View;
|
||||
|
||||
class AppServiceProvider extends ServiceProvider
|
||||
{
|
||||
@@ -17,5 +19,9 @@ class AppServiceProvider extends ServiceProvider
|
||||
public function boot(): void
|
||||
{
|
||||
Gate::policy(HostingAccount::class, HostingAccountPolicy::class);
|
||||
View::composer(['partials.topbar'], function ($view) {
|
||||
$view->with(MobileTopbar::resolve());
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'app_name' => 'Hosting',
|
||||
'default_title' => 'Dashboard',
|
||||
'patterns' => [
|
||||
'hosting.dashboard' => 'Dashboard',
|
||||
'hosting.index' => 'Dashboard',
|
||||
'hosting.search' => 'Search',
|
||||
'hosting.single-domain' => 'Single Domain',
|
||||
'hosting.multi-domain' => 'Multi Domain',
|
||||
'hosting.wordpress' => 'WordPress',
|
||||
'hosting.accounts.*' => 'Accounts',
|
||||
'hosting.orders.*' => 'Orders',
|
||||
'hosting.panel.*' => 'Control panel',
|
||||
'account.wallet' => 'Wallet',
|
||||
'account.billing' => 'Billing',
|
||||
'account.team*' => 'Team',
|
||||
'account.settings' => 'Settings',
|
||||
'notifications.*' => 'Notifications',
|
||||
],
|
||||
];
|
||||
@@ -3,6 +3,7 @@
|
||||
'subtitle' => null,
|
||||
'backUrl' => null,
|
||||
'badge' => null,
|
||||
'hideAfia' => false,
|
||||
])
|
||||
|
||||
<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)
|
||||
<span class="rounded-full bg-slate-100 px-3 py-1 text-xs font-medium text-slate-600">{{ $badge }}</span>
|
||||
@endif
|
||||
@unless ($hideAfia)
|
||||
@include('partials.afia-button', ['compact' => true])
|
||||
@endunless
|
||||
{{ $actions ?? '' }}
|
||||
</div>
|
||||
{{ $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>
|
||||
@@ -4,6 +4,7 @@
|
||||
<button @click="sidebarOpen = !sidebarOpen" class="lg:hidden shrink-0 text-slate-500 hover:text-slate-700">
|
||||
<svg class="w-6 h-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>
|
||||
@include('partials.mobile-topbar-title')
|
||||
{{-- Search hosting accounts, domains, orders --}}
|
||||
<div class="relative hidden w-full max-w-md lg:block" x-data="topbarSearch({ searchUrl: @js(route('hosting.search')) })" @click.outside="open = false" @keydown.escape.window="open = false">
|
||||
<div class="relative">
|
||||
@@ -81,15 +82,7 @@
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Afia (AI) — opens the in-app assistant. Matches the platform AI button. --}}
|
||||
<button type="button" @click="$dispatch('afia-open')"
|
||||
class="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>
|
||||
@include('partials.afia-button', ['compact' => true])
|
||||
@else
|
||||
<a href="{{ route('login') }}" class="rounded-lg bg-indigo-600 px-4 py-2 text-sm font-semibold text-white hover:bg-indigo-700">Sign in</a>
|
||||
@endauth
|
||||
|
||||
Reference in New Issue
Block a user