feat(pwa): transform auraajenticai.cloud into installable PWA app with client dashboard gateway
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 20 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 100 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 62 KiB |
+25
-1
@@ -17,7 +17,16 @@
|
||||
<meta name="twitter:image" content="https://auraajenticai.cloud/logo.png" />
|
||||
<link rel="canonical" href="https://auraajenticai.cloud" />
|
||||
<link rel="icon" type="image/png" href="logo.png" />
|
||||
<link rel="apple-touch-icon" href="logo.png" />
|
||||
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
|
||||
<link rel="icon" type="image/png" sizes="192x192" href="/icon-192.png" />
|
||||
<link rel="icon" type="image/png" sizes="512x512" href="/icon-512.png" />
|
||||
<link rel="manifest" href="/manifest.json" />
|
||||
<meta name="theme-color" content="#080d1a" />
|
||||
<meta name="mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||
<meta name="apple-mobile-web-app-title" content="Aura Cloud" />
|
||||
|
||||
<meta name="google-site-verification" content="vEK4vy89hAbckU9sUlqn7mZlMxLjNT--CvuLruocMfQ" />
|
||||
|
||||
@@ -350,5 +359,20 @@
|
||||
<script type="text/babel" src="src/chatbox.jsx?v=9"></script>
|
||||
<script type="text/babel" src="src/pages.jsx?v=9"></script>
|
||||
<script type="text/babel" src="src/app.jsx?v=9"></script>
|
||||
|
||||
<!-- PWA Service Worker Registration -->
|
||||
<script>
|
||||
if ('serviceWorker' in navigator) {
|
||||
window.addEventListener('load', function() {
|
||||
navigator.serviceWorker.register('/sw.js', { scope: '/' })
|
||||
.then(function(reg) {
|
||||
console.log('[Aura PWA] Service Worker registered with scope:', reg.scope);
|
||||
})
|
||||
.catch(function(err) {
|
||||
console.warn('[Aura PWA] Service Worker registration failed:', err);
|
||||
});
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
{
|
||||
"name": "Aura Ajentic AI — Enterprise AI & Logistics Platform",
|
||||
"short_name": "Aura Cloud",
|
||||
"description": "Autonomous AI Sales Agents, Courier MCP logistics engine, and real-time COD fraud defense.",
|
||||
"start_url": "/?source=pwa",
|
||||
"scope": "/",
|
||||
"display": "standalone",
|
||||
"orientation": "any",
|
||||
"background_color": "#080d1a",
|
||||
"theme_color": "#080d1a",
|
||||
"lang": "en",
|
||||
"dir": "ltr",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/icon-192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png",
|
||||
"purpose": "any"
|
||||
},
|
||||
{
|
||||
"src": "/icon-maskable-192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable"
|
||||
},
|
||||
{
|
||||
"src": "/icon-512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png",
|
||||
"purpose": "any"
|
||||
},
|
||||
{
|
||||
"src": "/icon-maskable-512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable"
|
||||
}
|
||||
],
|
||||
"shortcuts": [
|
||||
{
|
||||
"name": "Client Portal Login",
|
||||
"short_name": "Client Login",
|
||||
"description": "Sign in to Aura Client Dashboard",
|
||||
"url": "https://auth.auraajenticai.cloud/login?next=https%3A%2F%2Fclient.auraajenticai.cloud",
|
||||
"icons": [{ "src": "/icon-192.png", "sizes": "192x192" }]
|
||||
},
|
||||
{
|
||||
"name": "Courier Logistics Hub",
|
||||
"short_name": "Courier Hub",
|
||||
"description": "Steadfast, Pathao & RedX Logistics Automation",
|
||||
"url": "https://client.auraajenticai.cloud#view-courier",
|
||||
"icons": [{ "src": "/icon-192.png", "sizes": "192x192" }]
|
||||
},
|
||||
{
|
||||
"name": "AI Agent Studio",
|
||||
"short_name": "AI Studio",
|
||||
"description": "Omnichannel AI Agent Copilot & Inbox",
|
||||
"url": "https://client.auraajenticai.cloud#view-inbox",
|
||||
"icons": [{ "src": "/icon-192.png", "sizes": "192x192" }]
|
||||
}
|
||||
],
|
||||
"categories": ["business", "productivity", "utilities"],
|
||||
"prefer_related_applications": false
|
||||
}
|
||||
+416
-13
@@ -1,24 +1,43 @@
|
||||
// Top nav — minimal, sticky, blurred — mobile-responsive
|
||||
// Top nav — minimal, sticky, blurred — mobile-responsive + PWA Install & Client Gateway
|
||||
const NAV_LINKS = [
|
||||
{ id: "services", label: "Services", labelBn: "সার্ভিস", url: "#/services" },
|
||||
{ id: "case-studies", label: "Case Studies", labelBn: "কেস স্টাডি", url: "#/case-studies" },
|
||||
{ id: "pricing", label: "Pricing", labelBn: "মূল্য", url: "#/pricing" },
|
||||
{ id: "blog", label: "Blog", labelBn: "ব্লগ", url: "#/blog" },
|
||||
{ id: "contact", label: "Contact", labelBn: "যোগাযোগ", url: "#/contact" },
|
||||
]
|
||||
];
|
||||
|
||||
const NAV_TXT = {
|
||||
en: { search: "Search", getQuote: "Get a Quote", clientLogin: "Client Login", getStarted: "Get Started" },
|
||||
bn: { search: "খুঁজুন", getQuote: "কোট নিন", clientLogin: "ক্লায়েন্ট লগইন", getStarted: "শুরু করুন" },
|
||||
en: {
|
||||
search: "Search",
|
||||
getQuote: "Get a Quote",
|
||||
clientLogin: "Client Login",
|
||||
getStarted: "Get Started",
|
||||
installApp: "Install App",
|
||||
appInstalled: "App Installed",
|
||||
},
|
||||
bn: {
|
||||
search: "খুঁজুন",
|
||||
getQuote: "কোট নিন",
|
||||
clientLogin: "ক্লায়েন্ট লগইন",
|
||||
getStarted: "শুরু করুন",
|
||||
installApp: "অ্যাপ ইনস্টল",
|
||||
appInstalled: "অ্যাপ ইনস্টলড",
|
||||
},
|
||||
};
|
||||
|
||||
// Where a self-serve account should land right after signing up -- the client portal's own
|
||||
// service catalog, not a blank dashboard. aura-auth's /register already reads and honors `next`.
|
||||
// Next redirects ensure clients land seamlessly in the right SaaS portal
|
||||
const REGISTER_URL = "https://auth.auraajenticai.cloud/register?next=" + encodeURIComponent("https://client.auraajenticai.cloud/services");
|
||||
const CLIENT_LOGIN_URL = "https://auth.auraajenticai.cloud/login?next=" + encodeURIComponent("https://client.auraajenticai.cloud");
|
||||
|
||||
const Nav = ({ onCmdK, theme, onToggleTheme, accent, lang, onToggleLang, route }) => {
|
||||
const [scrolled, setScrolled] = React.useState(false);
|
||||
const [menuOpen, setMenuOpen] = React.useState(false);
|
||||
const [clientModalOpen, setClientModalOpen] = React.useState(false);
|
||||
const [installModalOpen, setInstallModalOpen] = React.useState(false);
|
||||
const [deferredPrompt, setDeferredPrompt] = React.useState(null);
|
||||
const [isInstalled, setIsInstalled] = React.useState(false);
|
||||
|
||||
const links = NAV_LINKS;
|
||||
const t = NAV_TXT[lang] || NAV_TXT.en;
|
||||
const bn = lang === "bn";
|
||||
@@ -29,6 +48,49 @@ const Nav = ({ onCmdK, theme, onToggleTheme, accent, lang, onToggleLang, route }
|
||||
return () => window.removeEventListener("scroll", onScroll);
|
||||
}, []);
|
||||
|
||||
// PWA standalone detection & prompt listener
|
||||
React.useEffect(() => {
|
||||
const isStandalone = window.matchMedia("(display-mode: standalone)").matches || window.navigator.standalone === true;
|
||||
if (isStandalone) {
|
||||
setIsInstalled(true);
|
||||
}
|
||||
|
||||
const handleBeforeInstall = (e) => {
|
||||
e.preventDefault();
|
||||
setDeferredPrompt(e);
|
||||
};
|
||||
|
||||
const handleAppInstalled = () => {
|
||||
setIsInstalled(true);
|
||||
setDeferredPrompt(null);
|
||||
};
|
||||
|
||||
window.addEventListener("beforeinstallprompt", handleBeforeInstall);
|
||||
window.addEventListener("appinstalled", handleAppInstalled);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("beforeinstallprompt", handleBeforeInstall);
|
||||
window.removeEventListener("appinstalled", handleAppInstalled);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const handleInstallClick = async () => {
|
||||
if (deferredPrompt) {
|
||||
try {
|
||||
deferredPrompt.prompt();
|
||||
const choice = await deferredPrompt.userChoice;
|
||||
if (choice && choice.outcome === "accepted") {
|
||||
setIsInstalled(true);
|
||||
setDeferredPrompt(null);
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn("PWA install prompt error:", err);
|
||||
}
|
||||
} else {
|
||||
setInstallModalOpen(true);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<nav style={{
|
||||
@@ -138,6 +200,32 @@ const Nav = ({ onCmdK, theme, onToggleTheme, accent, lang, onToggleLang, route }
|
||||
{lang === "en" ? "বাং" : "EN"}
|
||||
</button>
|
||||
|
||||
{/* PWA Install Button (Desktop) */}
|
||||
{!isInstalled && (
|
||||
<button
|
||||
onClick={handleInstallClick}
|
||||
className="nav-install-btn"
|
||||
title={bn ? "Aura Cloud অ্যাপ আপনার ডিভাইসে ইনস্টল করুন" : "Install Aura Cloud App on your device"}
|
||||
style={{
|
||||
display: "inline-flex",
|
||||
alignItems: "center",
|
||||
gap: 6,
|
||||
padding: "6px 11px",
|
||||
background: "linear-gradient(135deg, rgba(124,92,255,0.16), rgba(0,212,255,0.16))",
|
||||
border: "1px solid rgba(124,92,255,0.45)",
|
||||
borderRadius: 8,
|
||||
color: "var(--text)",
|
||||
fontSize: 12.5,
|
||||
fontWeight: 600,
|
||||
cursor: "pointer",
|
||||
whiteSpace: "nowrap",
|
||||
}}
|
||||
>
|
||||
<span>📲</span>
|
||||
<span>{t.installApp}</span>
|
||||
</button>
|
||||
)}
|
||||
|
||||
<a href="mailto:ceo@auraajenticai.cloud?subject=Project Quote" className="nav-quote-btn" style={{
|
||||
padding: "7px 13px",
|
||||
background: "transparent",
|
||||
@@ -152,7 +240,15 @@ const Nav = ({ onCmdK, theme, onToggleTheme, accent, lang, onToggleLang, route }
|
||||
{t.getQuote}
|
||||
</a>
|
||||
|
||||
<a href="https://auth.auraajenticai.cloud/login" className="nav-login-btn" style={{
|
||||
{/* Client Login Gateway Trigger */}
|
||||
<a
|
||||
href={CLIENT_LOGIN_URL}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
setClientModalOpen(true);
|
||||
}}
|
||||
className="nav-login-btn"
|
||||
style={{
|
||||
padding: "8px 12px",
|
||||
background: "transparent",
|
||||
border: "1px solid var(--line)",
|
||||
@@ -162,7 +258,9 @@ const Nav = ({ onCmdK, theme, onToggleTheme, accent, lang, onToggleLang, route }
|
||||
fontWeight: 500,
|
||||
textDecoration: "none",
|
||||
whiteSpace: "nowrap",
|
||||
}}>
|
||||
cursor: "pointer",
|
||||
}}
|
||||
>
|
||||
{t.clientLogin}
|
||||
</a>
|
||||
|
||||
@@ -220,6 +318,7 @@ const Nav = ({ onCmdK, theme, onToggleTheme, accent, lang, onToggleLang, route }
|
||||
@media (max-width: 640px) {
|
||||
.nav-search-btn { display: none !important; }
|
||||
.nav-quote-btn { display: none !important; }
|
||||
.nav-install-btn { display: none !important; }
|
||||
.nav-badge { display: none !important; }
|
||||
}
|
||||
@media (max-width: 480px) {
|
||||
@@ -246,7 +345,51 @@ const Nav = ({ onCmdK, theme, onToggleTheme, accent, lang, onToggleLang, route }
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: 2,
|
||||
maxHeight: "calc(100vh - 64px)",
|
||||
overflowY: "auto",
|
||||
}}>
|
||||
{/* Mobile PWA Install Banner */}
|
||||
{!isInstalled && (
|
||||
<div style={{
|
||||
padding: "12px 14px",
|
||||
borderRadius: 12,
|
||||
background: "linear-gradient(135deg, rgba(124,92,255,0.18), rgba(0,212,255,0.18))",
|
||||
border: "1px solid rgba(124,92,255,0.35)",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
marginBottom: 10,
|
||||
}}>
|
||||
<div>
|
||||
<div style={{ fontWeight: 600, fontSize: 13, color: "var(--text)" }}>
|
||||
{bn ? "📲 Aura Cloud অ্যাপ" : "📲 Aura Cloud App"}
|
||||
</div>
|
||||
<div style={{ fontSize: 11, color: "var(--text-dim)", marginTop: 2 }}>
|
||||
{bn ? "হোমস্ক্রিনে ১-ট্যাপে ইনস্টল করুন" : "Install to home screen for 1-tap launch"}
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => {
|
||||
setMenuOpen(false);
|
||||
handleInstallClick();
|
||||
}}
|
||||
style={{
|
||||
padding: "6px 12px",
|
||||
background: "var(--text)",
|
||||
color: "var(--bg)",
|
||||
border: "none",
|
||||
borderRadius: 8,
|
||||
fontSize: 12,
|
||||
fontWeight: 600,
|
||||
cursor: "pointer",
|
||||
whiteSpace: "nowrap",
|
||||
}}
|
||||
>
|
||||
{bn ? "ইনস্টল" : "Install"}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{links.map(l => (
|
||||
<a
|
||||
key={l.id}
|
||||
@@ -299,9 +442,11 @@ const Nav = ({ onCmdK, theme, onToggleTheme, accent, lang, onToggleLang, route }
|
||||
textDecoration: "none",
|
||||
}}
|
||||
>{t.getQuote}</a>
|
||||
<a
|
||||
href="https://auth.auraajenticai.cloud/login"
|
||||
onClick={() => setMenuOpen(false)}
|
||||
<button
|
||||
onClick={() => {
|
||||
setMenuOpen(false);
|
||||
setClientModalOpen(true);
|
||||
}}
|
||||
style={{
|
||||
padding: "8px 14px",
|
||||
background: "transparent",
|
||||
@@ -310,9 +455,9 @@ const Nav = ({ onCmdK, theme, onToggleTheme, accent, lang, onToggleLang, route }
|
||||
fontSize: 13,
|
||||
fontWeight: 500,
|
||||
borderRadius: 8,
|
||||
textDecoration: "none",
|
||||
cursor: "pointer",
|
||||
}}
|
||||
>{t.clientLogin}</a>
|
||||
>{t.clientLogin}</button>
|
||||
<a
|
||||
href={REGISTER_URL}
|
||||
onClick={() => setMenuOpen(false)}
|
||||
@@ -329,6 +474,264 @@ const Nav = ({ onCmdK, theme, onToggleTheme, accent, lang, onToggleLang, route }
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Client Portal Gateway Modal */}
|
||||
{clientModalOpen && (
|
||||
<div
|
||||
onClick={() => setClientModalOpen(false)}
|
||||
style={{
|
||||
position: "fixed",
|
||||
inset: 0,
|
||||
zIndex: 100,
|
||||
background: "rgba(0, 0, 0, 0.72)",
|
||||
backdropFilter: "blur(10px)",
|
||||
WebkitBackdropFilter: "blur(10px)",
|
||||
display: "grid",
|
||||
placeItems: "center",
|
||||
padding: 16,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
style={{
|
||||
position: "relative",
|
||||
width: "100%",
|
||||
maxWidth: 460,
|
||||
background: "var(--bg-card, #0c1222)",
|
||||
border: "1px solid rgba(124, 92, 255, 0.35)",
|
||||
borderRadius: 16,
|
||||
padding: "26px 24px",
|
||||
boxShadow: "0 24px 60px rgba(0, 0, 0, 0.6), 0 0 40px rgba(124, 92, 255, 0.15)",
|
||||
animation: "float-up 0.25s ease-out",
|
||||
}}
|
||||
>
|
||||
{/* Modal Header */}
|
||||
<div style={{ display: "flex", alignItems: "flex-start", justifyContent: "space-between", marginBottom: 16 }}>
|
||||
<div style={{ display: "flex", alignItems: "center", gap: 10 }}>
|
||||
<div style={{
|
||||
width: 40,
|
||||
height: 40,
|
||||
borderRadius: 10,
|
||||
background: "linear-gradient(135deg, rgba(124, 92, 255, 0.25), rgba(0, 212, 255, 0.25))",
|
||||
border: "1px solid rgba(124, 92, 255, 0.4)",
|
||||
display: "grid",
|
||||
placeItems: "center",
|
||||
}}>
|
||||
<Icons.Logo size={22} />
|
||||
</div>
|
||||
<div>
|
||||
<h3 style={{ margin: 0, fontSize: 17, fontWeight: 600, color: "var(--text)" }}>
|
||||
{bn ? "অরা ক্লায়েন্ট ড্যাশবোর্ড" : "Aura Client Portal"}
|
||||
</h3>
|
||||
<div style={{ fontSize: 12, color: "var(--text-faint)", marginTop: 2 }}>
|
||||
client.auraajenticai.cloud
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => setClientModalOpen(false)}
|
||||
style={{
|
||||
background: "transparent",
|
||||
border: "none",
|
||||
color: "var(--text-dim)",
|
||||
cursor: "pointer",
|
||||
fontSize: 20,
|
||||
padding: 4,
|
||||
lineHeight: 1,
|
||||
}}
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<p style={{ fontSize: 13, color: "var(--text-dim)", lineHeight: 1.5, margin: "0 0 18px" }}>
|
||||
{bn
|
||||
? "আপনার মার্চেন্ট একাউন্টে প্রবেশ করে AI সেলস বট, Steadfast/Pathao কুরিয়ার ট্র্যাকিং এবং ক্যাশ-অন-ডেলিভারি ফ্রড ডিফেন্স পরিচালনা করুন।"
|
||||
: "Sign in to your merchant workspace to manage AI conversational agents, track live Steadfast & Pathao shipments, and prevent COD fraud."}
|
||||
</p>
|
||||
|
||||
{/* Quick Workspace Info */}
|
||||
<div style={{
|
||||
background: "rgba(255, 255, 255, 0.03)",
|
||||
border: "1px solid var(--line)",
|
||||
borderRadius: 10,
|
||||
padding: "12px 14px",
|
||||
marginBottom: 18,
|
||||
fontSize: 12,
|
||||
color: "var(--text-dim)",
|
||||
}}>
|
||||
<div style={{ fontWeight: 600, color: "var(--text)", marginBottom: 6 }}>
|
||||
{bn ? "সক্রিয় মার্চেন্ট ওয়ার্কস্পেসসমূহ:" : "Active Enterprise Workspaces:"}
|
||||
</div>
|
||||
<div style={{ display: "flex", gap: 6, flexWrap: "wrap" }}>
|
||||
<span style={{
|
||||
padding: "3px 8px",
|
||||
borderRadius: 6,
|
||||
background: "rgba(236, 72, 153, 0.15)",
|
||||
color: "#f472b6",
|
||||
border: "1px solid rgba(236, 72, 153, 0.3)",
|
||||
}}>✨ Glamour's Touch</span>
|
||||
<span style={{
|
||||
padding: "3px 8px",
|
||||
borderRadius: 6,
|
||||
background: "rgba(59, 130, 246, 0.15)",
|
||||
color: "#60a5fa",
|
||||
border: "1px solid rgba(59, 130, 246, 0.3)",
|
||||
}}>🔧 Applix BD</span>
|
||||
<span style={{
|
||||
padding: "3px 8px",
|
||||
borderRadius: 6,
|
||||
background: "rgba(34, 197, 94, 0.15)",
|
||||
color: "#4ade80",
|
||||
border: "1px solid rgba(34, 197, 94, 0.3)",
|
||||
}}>🛡️ All Enterprise</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Direct Login CTA */}
|
||||
<a
|
||||
href={CLIENT_LOGIN_URL}
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
gap: 8,
|
||||
width: "100%",
|
||||
padding: "12px 18px",
|
||||
background: "linear-gradient(135deg, #7c5cff, #00d4ff)",
|
||||
color: "#ffffff",
|
||||
fontWeight: 600,
|
||||
fontSize: 14,
|
||||
borderRadius: 10,
|
||||
textDecoration: "none",
|
||||
textAlign: "center",
|
||||
boxShadow: "0 4px 18px rgba(124, 92, 255, 0.4)",
|
||||
marginBottom: 14,
|
||||
boxSizing: "border-box",
|
||||
}}
|
||||
>
|
||||
<span>{bn ? "ড্যাশবোর্ডে লগইন করুন" : "Sign In to Client Dashboard"}</span>
|
||||
<Icons.Arrow size={14} />
|
||||
</a>
|
||||
|
||||
{/* Deep Links / Shortcuts */}
|
||||
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", fontSize: 12 }}>
|
||||
<a
|
||||
href={REGISTER_URL}
|
||||
style={{ color: "var(--text-dim)", textDecoration: "underline" }}
|
||||
>
|
||||
{bn ? "নতুন একাউন্ট তৈরি করুন" : "Create merchant account"}
|
||||
</a>
|
||||
<a
|
||||
href="https://client.auraajenticai.cloud#view-courier"
|
||||
style={{ color: "var(--accent)", textDecoration: "none" }}
|
||||
>
|
||||
{bn ? "কুরিয়ার হাব ↗" : "Courier Hub ↗"}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* PWA Install Help Modal (iOS & Fallback) */}
|
||||
{installModalOpen && (
|
||||
<div
|
||||
onClick={() => setInstallModalOpen(false)}
|
||||
style={{
|
||||
position: "fixed",
|
||||
inset: 0,
|
||||
zIndex: 100,
|
||||
background: "rgba(0, 0, 0, 0.72)",
|
||||
backdropFilter: "blur(10px)",
|
||||
WebkitBackdropFilter: "blur(10px)",
|
||||
display: "grid",
|
||||
placeItems: "center",
|
||||
padding: 16,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
style={{
|
||||
position: "relative",
|
||||
width: "100%",
|
||||
maxWidth: 440,
|
||||
background: "var(--bg-card, #0c1222)",
|
||||
border: "1px solid rgba(0, 212, 255, 0.35)",
|
||||
borderRadius: 16,
|
||||
padding: "24px 22px",
|
||||
boxShadow: "0 24px 60px rgba(0, 0, 0, 0.6)",
|
||||
}}
|
||||
>
|
||||
<div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 14 }}>
|
||||
<div style={{ display: "flex", alignItems: "center", gap: 8 }}>
|
||||
<span style={{ fontSize: 22 }}>📲</span>
|
||||
<h3 style={{ margin: 0, fontSize: 16, fontWeight: 600, color: "var(--text)" }}>
|
||||
{bn ? "Aura Cloud অ্যাপ ইনস্টল গাইড" : "Install Aura Cloud App"}
|
||||
</h3>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => setInstallModalOpen(false)}
|
||||
style={{ background: "transparent", border: "none", color: "var(--text-dim)", cursor: "pointer", fontSize: 18 }}
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<p style={{ fontSize: 13, color: "var(--text-dim)", lineHeight: 1.5, margin: "0 0 16px" }}>
|
||||
{bn
|
||||
? "Aura Cloud অ্যাপ আপনার মোবাইল বা কম্পিউটারে ইনস্টল করে সরাসরি ১-ট্যাপে ওপেন করুন:"
|
||||
: "Install the Aura Cloud App directly to your device for 1-tap instant launch and offline access:"}
|
||||
</p>
|
||||
|
||||
<div style={{ display: "flex", flexDirection: "column", gap: 10, fontSize: 12.5, color: "var(--text)" }}>
|
||||
<div style={{ padding: "10px 12px", background: "rgba(255,255,255,0.03)", borderRadius: 8, border: "1px solid var(--line)" }}>
|
||||
<strong>🍏 iOS / iPhone (Safari):</strong>
|
||||
<div style={{ color: "var(--text-dim)", marginTop: 4 }}>
|
||||
{bn
|
||||
? "সাফারি ব্রাউজারের নিচে শেয়ার বাটনে (Share ⎋) ট্যাপ করুন, এরপর নিচে স্ক্রল করে 'Add to Home Screen ⊞' সিলেক্ট করুন।"
|
||||
: "Tap the Share icon (⎋) at bottom of Safari, scroll down and tap 'Add to Home Screen ⊞'."}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ padding: "10px 12px", background: "rgba(255,255,255,0.03)", borderRadius: 8, border: "1px solid var(--line)" }}>
|
||||
<strong>🤖 Android (Chrome):</strong>
|
||||
<div style={{ color: "var(--text-dim)", marginTop: 4 }}>
|
||||
{bn
|
||||
? "ব্রাউজারের উপরের ডান কোণায় ৩-ডট মেনুতে (⋮) ট্যাপ করে 'Install app' বা 'Add to Home screen' নির্বাচন করুন।"
|
||||
: "Tap the 3 dots menu (⋮) in Chrome and select 'Install app' or 'Add to Home screen'."}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ padding: "10px 12px", background: "rgba(255,255,255,0.03)", borderRadius: 8, border: "1px solid var(--line)" }}>
|
||||
<strong>💻 Windows / Mac (Chrome & Edge):</strong>
|
||||
<div style={{ color: "var(--text-dim)", marginTop: 4 }}>
|
||||
{bn
|
||||
? "ব্রাউজারের অ্যাড্রেস বারের ডানপাশে ইনস্টল আইকন (⊕) ক্লিক করে ইনস্টল করুন।"
|
||||
: "Click the Install icon (⊕) in the browser address bar to install to desktop."}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={() => setInstallModalOpen(false)}
|
||||
style={{
|
||||
width: "100%",
|
||||
marginTop: 18,
|
||||
padding: "10px",
|
||||
background: "var(--text)",
|
||||
color: "var(--bg)",
|
||||
border: "none",
|
||||
borderRadius: 8,
|
||||
fontWeight: 600,
|
||||
fontSize: 13,
|
||||
cursor: "pointer",
|
||||
}}
|
||||
>
|
||||
{bn ? "ঠিক আছে, বুঝেছি" : "Got it"}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
// Aura Cloud Service Worker — High-Performance Offline & Shell Cache v1.0
|
||||
const CACHE_NAME = 'aura-cloud-pwa-v1';
|
||||
|
||||
const STATIC_PRECACHE = [
|
||||
'/',
|
||||
'/index.html',
|
||||
'/manifest.json',
|
||||
'/logo.png',
|
||||
'/apple-touch-icon.png',
|
||||
'/icon-192.png',
|
||||
'/icon-512.png',
|
||||
'/icon-maskable-192.png',
|
||||
'/icon-maskable-512.png',
|
||||
'/privacy.html',
|
||||
'/terms.html'
|
||||
];
|
||||
|
||||
self.addEventListener('install', (event) => {
|
||||
event.waitUntil(
|
||||
caches.open(CACHE_NAME).then((cache) => {
|
||||
return cache.addAll(STATIC_PRECACHE).catch((err) => {
|
||||
console.warn('[Aura PWA SW] Pre-cache initial warning:', err);
|
||||
});
|
||||
}).then(() => self.skipWaiting())
|
||||
);
|
||||
});
|
||||
|
||||
self.addEventListener('activate', (event) => {
|
||||
event.waitUntil(
|
||||
caches.keys().then((keys) => {
|
||||
return Promise.all(
|
||||
keys.filter((k) => k !== CACHE_NAME).map((k) => caches.delete(k))
|
||||
);
|
||||
}).then(() => self.clients.claim())
|
||||
);
|
||||
});
|
||||
|
||||
self.addEventListener('fetch', (event) => {
|
||||
const req = event.request;
|
||||
const url = new URL(req.url);
|
||||
|
||||
// 1. Bypass external/cross-origin requests (Auth, Analytics, Fonts, APIs)
|
||||
if (url.origin !== self.location.origin) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. Network-first strategy for navigation / HTML routes (instant updates)
|
||||
if (req.mode === 'navigate' || req.headers.get('accept')?.includes('text/html')) {
|
||||
event.respondWith(
|
||||
fetch(req)
|
||||
.then((networkRes) => {
|
||||
if (networkRes && networkRes.ok) {
|
||||
const clone = networkRes.clone();
|
||||
caches.open(CACHE_NAME).then((cache) => cache.put(req, clone));
|
||||
}
|
||||
return networkRes;
|
||||
})
|
||||
.catch(() => {
|
||||
return caches.match(req).then((cached) => cached || caches.match('/index.html'));
|
||||
})
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// 3. Stale-while-revalidate for same-origin static assets
|
||||
event.respondWith(
|
||||
caches.match(req).then((cached) => {
|
||||
const fetchPromise = fetch(req)
|
||||
.then((networkRes) => {
|
||||
if (networkRes && networkRes.ok) {
|
||||
const clone = networkRes.clone();
|
||||
caches.open(CACHE_NAME).then((cache) => cache.put(req, clone));
|
||||
}
|
||||
return networkRes;
|
||||
})
|
||||
.catch(() => null);
|
||||
|
||||
return cached || fetchPromise;
|
||||
})
|
||||
);
|
||||
});
|
||||
+54
-20
@@ -1,6 +1,51 @@
|
||||
{
|
||||
"cleanUrls": false,
|
||||
"trailingSlash": false,
|
||||
"headers": [
|
||||
{
|
||||
"source": "/sw.js",
|
||||
"headers": [
|
||||
{
|
||||
"key": "Cache-Control",
|
||||
"value": "no-cache, no-store, must-revalidate"
|
||||
},
|
||||
{
|
||||
"key": "Service-Worker-Allowed",
|
||||
"value": "/"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"source": "/manifest.json",
|
||||
"headers": [
|
||||
{
|
||||
"key": "Content-Type",
|
||||
"value": "application/manifest+json; charset=utf-8"
|
||||
},
|
||||
{
|
||||
"key": "Cache-Control",
|
||||
"value": "public, max-age=3600"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"source": "/(.*)",
|
||||
"headers": [
|
||||
{
|
||||
"key": "X-Content-Type-Options",
|
||||
"value": "nosniff"
|
||||
},
|
||||
{
|
||||
"key": "X-Frame-Options",
|
||||
"value": "SAMEORIGIN"
|
||||
},
|
||||
{
|
||||
"key": "Referrer-Policy",
|
||||
"value": "strict-origin-when-cross-origin"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"rewrites": [
|
||||
{
|
||||
"source": "/privacy",
|
||||
@@ -19,27 +64,16 @@
|
||||
"destination": "/terms.html"
|
||||
},
|
||||
{
|
||||
"source": "/((?!privacy|terms).*)",
|
||||
"source": "/manifest.json",
|
||||
"destination": "/manifest.json"
|
||||
},
|
||||
{
|
||||
"source": "/sw.js",
|
||||
"destination": "/sw.js"
|
||||
},
|
||||
{
|
||||
"source": "/((?!privacy|terms|manifest\\.json|sw\\.js|.*\\.(?:png|jpg|jpeg|svg|ico|json|js|css|webmanifest)).*)",
|
||||
"destination": "/index.html"
|
||||
}
|
||||
],
|
||||
"headers": [
|
||||
{
|
||||
"source": "/(.*)",
|
||||
"headers": [
|
||||
{
|
||||
"key": "X-Content-Type-Options",
|
||||
"value": "nosniff"
|
||||
},
|
||||
{
|
||||
"key": "X-Frame-Options",
|
||||
"value": "SAMEORIGIN"
|
||||
},
|
||||
{
|
||||
"key": "Referrer-Policy",
|
||||
"value": "strict-origin-when-cross-origin"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user