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" />
|
<meta name="twitter:image" content="https://auraajenticai.cloud/logo.png" />
|
||||||
<link rel="canonical" href="https://auraajenticai.cloud" />
|
<link rel="canonical" href="https://auraajenticai.cloud" />
|
||||||
<link rel="icon" type="image/png" href="logo.png" />
|
<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" />
|
<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/chatbox.jsx?v=9"></script>
|
||||||
<script type="text/babel" src="src/pages.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>
|
<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>
|
</body>
|
||||||
</html>
|
</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 = [
|
const NAV_LINKS = [
|
||||||
{ id: "services", label: "Services", labelBn: "সার্ভিস", url: "#/services" },
|
{ id: "services", label: "Services", labelBn: "সার্ভিস", url: "#/services" },
|
||||||
{ id: "case-studies", label: "Case Studies", labelBn: "কেস স্টাডি", url: "#/case-studies" },
|
{ id: "case-studies", label: "Case Studies", labelBn: "কেস স্টাডি", url: "#/case-studies" },
|
||||||
{ id: "pricing", label: "Pricing", labelBn: "মূল্য", url: "#/pricing" },
|
{ id: "pricing", label: "Pricing", labelBn: "মূল্য", url: "#/pricing" },
|
||||||
{ id: "blog", label: "Blog", labelBn: "ব্লগ", url: "#/blog" },
|
{ id: "blog", label: "Blog", labelBn: "ব্লগ", url: "#/blog" },
|
||||||
{ id: "contact", label: "Contact", labelBn: "যোগাযোগ", url: "#/contact" },
|
{ id: "contact", label: "Contact", labelBn: "যোগাযোগ", url: "#/contact" },
|
||||||
]
|
];
|
||||||
|
|
||||||
const NAV_TXT = {
|
const NAV_TXT = {
|
||||||
en: { search: "Search", getQuote: "Get a Quote", clientLogin: "Client Login", getStarted: "Get Started" },
|
en: {
|
||||||
bn: { search: "খুঁজুন", getQuote: "কোট নিন", clientLogin: "ক্লায়েন্ট লগইন", getStarted: "শুরু করুন" },
|
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
|
// Next redirects ensure clients land seamlessly in the right SaaS portal
|
||||||
// service catalog, not a blank dashboard. aura-auth's /register already reads and honors `next`.
|
|
||||||
const REGISTER_URL = "https://auth.auraajenticai.cloud/register?next=" + encodeURIComponent("https://client.auraajenticai.cloud/services");
|
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 Nav = ({ onCmdK, theme, onToggleTheme, accent, lang, onToggleLang, route }) => {
|
||||||
const [scrolled, setScrolled] = React.useState(false);
|
const [scrolled, setScrolled] = React.useState(false);
|
||||||
const [menuOpen, setMenuOpen] = 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 links = NAV_LINKS;
|
||||||
const t = NAV_TXT[lang] || NAV_TXT.en;
|
const t = NAV_TXT[lang] || NAV_TXT.en;
|
||||||
const bn = lang === "bn";
|
const bn = lang === "bn";
|
||||||
@@ -29,6 +48,49 @@ const Nav = ({ onCmdK, theme, onToggleTheme, accent, lang, onToggleLang, route }
|
|||||||
return () => window.removeEventListener("scroll", onScroll);
|
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 (
|
return (
|
||||||
<>
|
<>
|
||||||
<nav style={{
|
<nav style={{
|
||||||
@@ -138,6 +200,32 @@ const Nav = ({ onCmdK, theme, onToggleTheme, accent, lang, onToggleLang, route }
|
|||||||
{lang === "en" ? "বাং" : "EN"}
|
{lang === "en" ? "বাং" : "EN"}
|
||||||
</button>
|
</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={{
|
<a href="mailto:ceo@auraajenticai.cloud?subject=Project Quote" className="nav-quote-btn" style={{
|
||||||
padding: "7px 13px",
|
padding: "7px 13px",
|
||||||
background: "transparent",
|
background: "transparent",
|
||||||
@@ -152,7 +240,15 @@ const Nav = ({ onCmdK, theme, onToggleTheme, accent, lang, onToggleLang, route }
|
|||||||
{t.getQuote}
|
{t.getQuote}
|
||||||
</a>
|
</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",
|
padding: "8px 12px",
|
||||||
background: "transparent",
|
background: "transparent",
|
||||||
border: "1px solid var(--line)",
|
border: "1px solid var(--line)",
|
||||||
@@ -162,7 +258,9 @@ const Nav = ({ onCmdK, theme, onToggleTheme, accent, lang, onToggleLang, route }
|
|||||||
fontWeight: 500,
|
fontWeight: 500,
|
||||||
textDecoration: "none",
|
textDecoration: "none",
|
||||||
whiteSpace: "nowrap",
|
whiteSpace: "nowrap",
|
||||||
}}>
|
cursor: "pointer",
|
||||||
|
}}
|
||||||
|
>
|
||||||
{t.clientLogin}
|
{t.clientLogin}
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
@@ -220,6 +318,7 @@ const Nav = ({ onCmdK, theme, onToggleTheme, accent, lang, onToggleLang, route }
|
|||||||
@media (max-width: 640px) {
|
@media (max-width: 640px) {
|
||||||
.nav-search-btn { display: none !important; }
|
.nav-search-btn { display: none !important; }
|
||||||
.nav-quote-btn { display: none !important; }
|
.nav-quote-btn { display: none !important; }
|
||||||
|
.nav-install-btn { display: none !important; }
|
||||||
.nav-badge { display: none !important; }
|
.nav-badge { display: none !important; }
|
||||||
}
|
}
|
||||||
@media (max-width: 480px) {
|
@media (max-width: 480px) {
|
||||||
@@ -246,7 +345,51 @@ const Nav = ({ onCmdK, theme, onToggleTheme, accent, lang, onToggleLang, route }
|
|||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
gap: 2,
|
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 => (
|
{links.map(l => (
|
||||||
<a
|
<a
|
||||||
key={l.id}
|
key={l.id}
|
||||||
@@ -299,9 +442,11 @@ const Nav = ({ onCmdK, theme, onToggleTheme, accent, lang, onToggleLang, route }
|
|||||||
textDecoration: "none",
|
textDecoration: "none",
|
||||||
}}
|
}}
|
||||||
>{t.getQuote}</a>
|
>{t.getQuote}</a>
|
||||||
<a
|
<button
|
||||||
href="https://auth.auraajenticai.cloud/login"
|
onClick={() => {
|
||||||
onClick={() => setMenuOpen(false)}
|
setMenuOpen(false);
|
||||||
|
setClientModalOpen(true);
|
||||||
|
}}
|
||||||
style={{
|
style={{
|
||||||
padding: "8px 14px",
|
padding: "8px 14px",
|
||||||
background: "transparent",
|
background: "transparent",
|
||||||
@@ -310,9 +455,9 @@ const Nav = ({ onCmdK, theme, onToggleTheme, accent, lang, onToggleLang, route }
|
|||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
fontWeight: 500,
|
fontWeight: 500,
|
||||||
borderRadius: 8,
|
borderRadius: 8,
|
||||||
textDecoration: "none",
|
cursor: "pointer",
|
||||||
}}
|
}}
|
||||||
>{t.clientLogin}</a>
|
>{t.clientLogin}</button>
|
||||||
<a
|
<a
|
||||||
href={REGISTER_URL}
|
href={REGISTER_URL}
|
||||||
onClick={() => setMenuOpen(false)}
|
onClick={() => setMenuOpen(false)}
|
||||||
@@ -329,6 +474,264 @@ const Nav = ({ onCmdK, theme, onToggleTheme, accent, lang, onToggleLang, route }
|
|||||||
</div>
|
</div>
|
||||||
</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,
|
"cleanUrls": false,
|
||||||
"trailingSlash": 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": [
|
"rewrites": [
|
||||||
{
|
{
|
||||||
"source": "/privacy",
|
"source": "/privacy",
|
||||||
@@ -19,27 +64,16 @@
|
|||||||
"destination": "/terms.html"
|
"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"
|
"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