Add a branded boot splash (Ladill Mail style) to app pages
Deploy Ladill POS / deploy (push) Successful in 31s

InjectBootSplash middleware injects a full-screen loading splash — the app's
launcher icon (pulsing), an animated progress bar, and "Loading <app>…" — into
authenticated HTML pages, then fades it out on load. Shows once per browser
session (sessionStorage), so it masks the app's client-side boot without
flashing on every navigation. Self-branding from the app's own subdomain icon,
so the same partial/middleware drops into every Ladill app.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
isaacclad
2026-06-25 22:08:36 +00:00
co-authored by Claude Opus 4.8
parent f64a28cb21
commit 2103708ab6
4 changed files with 96 additions and 0 deletions
+46
View File
@@ -0,0 +1,46 @@
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\View;
use Symfony\Component\HttpFoundation\Response;
/**
* Injects a branded boot splash (Ladill Mail style) into authenticated HTML
* pages. The splash shows once per browser session while the app loads, then
* fades out masking the client-side boot so the app feels instant.
*/
class InjectBootSplash
{
public function handle(Request $request, Closure $next): Response
{
$response = $next($request);
if (! $request->isMethod('GET') || ! Auth::check()) {
return $response;
}
if (! str_contains((string) $response->headers->get('Content-Type', ''), 'text/html')) {
return $response;
}
$content = $response->getContent();
if (! is_string($content)
|| stripos($content, '<body') === false
|| str_contains($content, 'id="ladill-boot"')) {
return $response;
}
$splash = View::make('partials.boot-splash')->render();
$content = preg_replace_callback('/<body\b[^>]*>/i', fn ($m) => $m[0].$splash, $content, 1);
if (is_string($content)) {
$response->setContent($content);
}
return $response;
}
}
+1
View File
@@ -18,6 +18,7 @@ return Application::configure(basePath: dirname(__DIR__))
])); ]));
$middleware->web(append: [ $middleware->web(append: [
\App\Http\Middleware\SetActingAccount::class, \App\Http\Middleware\SetActingAccount::class,
\App\Http\Middleware\InjectBootSplash::class,
]); ]);
$middleware->alias([ $middleware->alias([
'platform.session' => \App\Http\Middleware\EnsurePlatformSession::class, 'platform.session' => \App\Http\Middleware\EnsurePlatformSession::class,
@@ -0,0 +1,43 @@
@php
// Branded boot splash (Ladill Mail style). Self-icon from this app's subdomain.
$sub = strtolower((string) ((explode('.', (string) (parse_url((string) config('app.url'), PHP_URL_HOST) ?: '')))[0] ?? ''));
$icon = $sub !== '' && is_file(public_path("images/launcher-icons/{$sub}.svg")) ? "images/launcher-icons/{$sub}.svg" : null;
$label = (string) config('app.name', 'Ladill');
@endphp
<div id="ladill-boot" role="status" aria-live="polite">
<div class="lb-wrap">
@if ($icon)
<img class="lb-logo" src="{{ asset($icon) }}?v={{ @filemtime(public_path($icon)) ?: '1' }}" alt="">
@endif
<div class="lb-bar"><span></span></div>
<div class="lb-title">Loading {{ $label }}</div>
</div>
</div>
<style>
#ladill-boot{position:fixed;inset:0;z-index:99999;background:#f8fafc;display:flex;align-items:center;justify-content:center;transition:opacity .35s ease;font-family:'Figtree',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif}
#ladill-boot .lb-wrap{display:flex;flex-direction:column;align-items:center;gap:24px;width:260px}
#ladill-boot .lb-logo{width:64px;height:64px;animation:lb-pulse 1.6s ease-in-out infinite}
#ladill-boot .lb-bar{width:100%;height:4px;background:#e2e8f0;border-radius:999px;overflow:hidden}
#ladill-boot .lb-bar>span{display:block;height:100%;width:8%;border-radius:999px;background:linear-gradient(90deg,#4f46e5,#7c3aed);animation:lb-fill 1.9s cubic-bezier(.4,0,.2,1) forwards}
#ladill-boot .lb-title{font-size:14px;color:#64748b;font-weight:500}
@keyframes lb-fill{0%{width:8%}55%{width:68%}100%{width:93%}}
@keyframes lb-pulse{0%,100%{transform:scale(1);opacity:1}50%{transform:scale(.93);opacity:.82}}
</style>
<script>
(function(){
var el=document.getElementById('ladill-boot');
if(!el)return;
// Show once per browser session; suppress instantly on later navigations (no flash).
try{
if(sessionStorage.getItem('lb_booted')){el.parentNode.removeChild(el);return;}
sessionStorage.setItem('lb_booted','1');
}catch(e){}
var start=Date.now(),MIN=550;
function done(){
var wait=Math.max(0,MIN-(Date.now()-start));
setTimeout(function(){if(!el)return;el.style.opacity='0';setTimeout(function(){if(el&&el.parentNode)el.parentNode.removeChild(el);},400);},wait);
}
if(document.readyState==='complete')done();else window.addEventListener('load',done);
setTimeout(done,8000); // safety net
})();
</script>
+6
View File
@@ -50,6 +50,12 @@ class PosRegisterTest extends TestCase
->assertSee('favicon.ico', false); ->assertSee('favicon.ico', false);
} }
public function test_boot_splash_injected_on_authenticated_pages(): void
{
$this->actingAs($this->user())->get(route('pos.dashboard'))
->assertOk()->assertSee('id="ladill-boot"', false);
}
public function test_register_renders_local_products_in_restaurant_mode(): void public function test_register_renders_local_products_in_restaurant_mode(): void
{ {
$user = $this->user(); $user = $this->user();