feat(ui): animated boot splash and loading screen
Show a Lottie-animated splash while the app bundle boots and dismiss it once WebAppComponent initialises, plus a reusable mifosx-loading-screen component with loading and error animations. lottie.min.js is served from the lottie-web package instead of being vendored into src/assets. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+259
-8
@@ -11,7 +11,7 @@
|
||||
<html lang="en" class="h-full bg-gray-100">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>{{'APP_NAME' | translate}}</title>
|
||||
<title>CBS Nexora</title>
|
||||
<base href="/" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
@@ -111,13 +111,264 @@
|
||||
});
|
||||
</script>
|
||||
|
||||
<mifosx-web-app>
|
||||
<div class="loader-wrapper">
|
||||
<div class="bottom triangle"></div>
|
||||
<div class="top triangle"></div>
|
||||
<div class="left triangle"></div>
|
||||
<div class="right triangle"></div>
|
||||
<div id="cbs-app-splash-screen" class="cbs-init-screen">
|
||||
<style>
|
||||
.cbs-init-screen {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: radial-gradient(circle at center, #0f172a 0%, #090d16 100%);
|
||||
color: #f8fafc;
|
||||
font-family:
|
||||
'Plus Jakarta Sans',
|
||||
-apple-system,
|
||||
BlinkMacSystemFont,
|
||||
'Segoe UI',
|
||||
Roboto,
|
||||
sans-serif;
|
||||
z-index: 999999;
|
||||
overflow: hidden;
|
||||
transition:
|
||||
opacity 0.6s cubic-bezier(0.16, 1, 0.3, 1),
|
||||
transform 0.6s cubic-bezier(0.16, 1, 0.3, 1);
|
||||
}
|
||||
.cbs-init-screen.cbs-splash-fade-out {
|
||||
opacity: 0;
|
||||
transform: scale(1.02);
|
||||
pointer-events: none;
|
||||
}
|
||||
.cbs-init-brand {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
.cbs-init-icon {
|
||||
width: 72px;
|
||||
height: 72px;
|
||||
border-radius: 18px;
|
||||
background: linear-gradient(135deg, #1e3a8a 0%, #0284c7 100%);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 12px 28px rgba(14, 165, 233, 0.35);
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
.cbs-init-title {
|
||||
font-size: 1.6rem;
|
||||
font-weight: 800;
|
||||
letter-spacing: -0.02em;
|
||||
margin: 0;
|
||||
}
|
||||
.cbs-init-sub {
|
||||
font-size: 0.8rem;
|
||||
color: #94a3b8;
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.04em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.cbs-init-lottie-box {
|
||||
width: 130px;
|
||||
height: 130px;
|
||||
margin: 8px auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
filter: drop-shadow(0 8px 24px rgba(37, 99, 235, 0.25));
|
||||
}
|
||||
.cbs-init-spinner {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
position: relative;
|
||||
}
|
||||
.cbs-init-ring {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-radius: 50%;
|
||||
border: 3px solid transparent;
|
||||
}
|
||||
.cbs-init-ring-1 {
|
||||
border-top-color: #2563eb;
|
||||
border-right-color: #2563eb;
|
||||
animation: cbsInitSpin 1.1s cubic-bezier(0.5, 0, 0.5, 1) infinite;
|
||||
}
|
||||
.cbs-init-ring-2 {
|
||||
inset: 6px;
|
||||
border-bottom-color: #0ea5e9;
|
||||
border-left-color: #0ea5e9;
|
||||
animation: cbsInitSpin 1.5s cubic-bezier(0.5, 0, 0.5, 1) infinite reverse;
|
||||
}
|
||||
.cbs-init-ring-3 {
|
||||
inset: 12px;
|
||||
border-top-color: #10b981;
|
||||
animation: cbsInitSpin 0.9s cubic-bezier(0.5, 0, 0.5, 1) infinite;
|
||||
}
|
||||
.cbs-init-track {
|
||||
width: 260px;
|
||||
height: 5px;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 99px;
|
||||
overflow: hidden;
|
||||
margin: 18px 0 12px;
|
||||
position: relative;
|
||||
}
|
||||
.cbs-init-fill {
|
||||
height: 100%;
|
||||
width: 0%;
|
||||
background: linear-gradient(90deg, #2563eb, #38bdf8);
|
||||
border-radius: 99px;
|
||||
box-shadow: 0 0 12px rgba(56, 189, 248, 0.6);
|
||||
transition: width 0.3s ease;
|
||||
}
|
||||
.cbs-init-status {
|
||||
font-size: 0.82rem;
|
||||
color: #94a3b8;
|
||||
font-family: inherit;
|
||||
margin: 0;
|
||||
height: 20px;
|
||||
}
|
||||
@keyframes cbsInitSpin {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<div class="cbs-init-brand">
|
||||
<div class="cbs-init-icon">
|
||||
<svg width="40" height="40" viewBox="0 0 32 32">
|
||||
<rect x="2" y="2" width="13" height="13" rx="2.5" fill="#ffffff" />
|
||||
<rect x="17" y="2" width="13" height="13" rx="2.5" fill="#29abe2" />
|
||||
<rect x="2" y="17" width="13" height="13" rx="2.5" fill="#00b388" />
|
||||
<rect x="17" y="17" width="13" height="13" rx="2.5" fill="#6b7fa3" />
|
||||
</svg>
|
||||
</div>
|
||||
<h2 class="cbs-init-title">CBS Nexora</h2>
|
||||
<span class="cbs-init-sub">Next-Generation Core Banking</span>
|
||||
</div>
|
||||
</mifosx-web-app>
|
||||
|
||||
<div id="cbs-init-lottie-target" class="cbs-init-lottie-box">
|
||||
<div class="cbs-init-spinner">
|
||||
<div class="cbs-init-ring cbs-init-ring-1"></div>
|
||||
<div class="cbs-init-ring cbs-init-ring-2"></div>
|
||||
<div class="cbs-init-ring cbs-init-ring-3"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="cbs-init-track">
|
||||
<div class="cbs-init-fill" id="cbs-init-progress-bar"></div>
|
||||
</div>
|
||||
|
||||
<p class="cbs-init-status" id="cbs-init-status-text">Connecting to Secure Banking Gateway...</p>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function () {
|
||||
var splash = document.getElementById('cbs-app-splash-screen');
|
||||
var bar = document.getElementById('cbs-init-progress-bar');
|
||||
var status = document.getElementById('cbs-init-status-text');
|
||||
var progress = 0;
|
||||
var minTimePassed = false;
|
||||
var angularReady = false;
|
||||
var dismissed = false;
|
||||
|
||||
// Load Lottie Vector Animation
|
||||
var script = document.createElement('script');
|
||||
script.src = 'assets/js/lottie.min.js';
|
||||
script.onload = function () {
|
||||
if (typeof lottie !== 'undefined') {
|
||||
var target = document.getElementById('cbs-init-lottie-target');
|
||||
if (target) {
|
||||
target.innerHTML = '';
|
||||
lottie.loadAnimation({
|
||||
container: target,
|
||||
renderer: 'svg',
|
||||
loop: true,
|
||||
autoplay: true,
|
||||
path: 'assets/animations/loading_animation.json'
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
document.head.appendChild(script);
|
||||
|
||||
// Progress simulation stages (Guaranteed ~2 seconds duration on refresh)
|
||||
var interval = setInterval(function () {
|
||||
if (progress < 90) {
|
||||
progress += 5;
|
||||
} else if (angularReady && progress < 100) {
|
||||
progress += 5;
|
||||
}
|
||||
|
||||
if (bar) bar.style.width = Math.min(progress, 100) + '%';
|
||||
|
||||
if (status) {
|
||||
if (progress >= 85) {
|
||||
status.innerText = 'Launching CBS Nexora Dashboard...';
|
||||
} else if (progress >= 60) {
|
||||
status.innerText = 'Synchronizing core banking ledger schema...';
|
||||
} else if (progress >= 30) {
|
||||
status.innerText = 'Authenticating token & cryptographic keys...';
|
||||
}
|
||||
}
|
||||
|
||||
if (progress >= 100 && angularReady && minTimePassed) {
|
||||
clearInterval(interval);
|
||||
dismissSplash();
|
||||
}
|
||||
}, 90);
|
||||
|
||||
// Enforce minimum hold time (2000ms = 2 seconds) so refresh always shows the animation
|
||||
setTimeout(function () {
|
||||
minTimePassed = true;
|
||||
if (angularReady) {
|
||||
progress = 100;
|
||||
if (bar) bar.style.width = '100%';
|
||||
dismissSplash();
|
||||
}
|
||||
}, 2000);
|
||||
|
||||
function dismissSplash() {
|
||||
if (dismissed) return;
|
||||
dismissed = true;
|
||||
clearInterval(interval);
|
||||
if (bar) bar.style.width = '100%';
|
||||
if (status) status.innerText = 'Ready! System loaded successfully.';
|
||||
setTimeout(function () {
|
||||
if (splash) {
|
||||
splash.classList.add('cbs-splash-fade-out');
|
||||
setTimeout(function () {
|
||||
if (splash && splash.parentNode) {
|
||||
splash.parentNode.removeChild(splash);
|
||||
}
|
||||
}, 600);
|
||||
}
|
||||
}, 250);
|
||||
}
|
||||
|
||||
// Global hook for Angular to notify when ready
|
||||
window.hideAppSplash = function () {
|
||||
angularReady = true;
|
||||
if (minTimePassed) {
|
||||
dismissSplash();
|
||||
}
|
||||
};
|
||||
|
||||
// Safety fallback (Max 7s in case of backend network failure)
|
||||
setTimeout(function () {
|
||||
angularReady = true;
|
||||
minTimePassed = true;
|
||||
dismissSplash();
|
||||
}, 7000);
|
||||
})();
|
||||
</script>
|
||||
|
||||
<mifosx-web-app></mifosx-web-app>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user