feat: landing page + logo, dev-mode banner + sync cap, non-blocking sync with progress splash

This commit is contained in:
cesnimda
2026-06-30 16:58:35 +02:00
parent dcb939e4f2
commit fe5920919f
29 changed files with 552 additions and 39 deletions
+18 -8
View File
@@ -1,35 +1,45 @@
import { Link, Outlet, useLocation } from 'react-router-dom';
import { Link, Outlet, useLocation, useNavigate } from 'react-router-dom';
import { useEffect, useState } from 'react';
import { AuthApi, SyncApi } from '../api/client.js';
import Logo from './Logo.jsx';
import DevBanner from './DevBanner.jsx';
export default function Layout() {
const [user, setUser] = useState(null);
const loc = useLocation();
const navigate = useNavigate();
useEffect(() => {
AuthApi.me().then(setUser).catch(() => {});
}, []);
const nav = [
{ to: '/', label: 'Dashboard' },
{ to: '/cleanup', label: 'Cleanup' },
{ to: '/unsubscribe', label: 'Unsubscribe' }
{ to: '/app', label: 'Dashboard', end: true },
{ to: '/app/cleanup', label: 'Cleanup' },
{ to: '/app/unsubscribe', label: 'Unsubscribe' }
];
const isActive = (n) => (n.end ? loc.pathname === n.to : loc.pathname.startsWith(n.to));
const logout = async () => {
try { await AuthApi.logout(); } catch { /* ignore */ }
navigate('/');
};
return (
<div className="app">
<DevBanner />
<header className="topbar">
<div className="brand">📥 InboxIntel</div>
<Link to="/app" className="brand-link"><Logo size={28} withWordmark /></Link>
<nav>
{nav.map((n) => (
<Link key={n.to} to={n.to} className={loc.pathname === n.to ? 'active' : ''}>
{n.label}
</Link>
<Link key={n.to} to={n.to} className={isActive(n) ? 'active' : ''}>{n.label}</Link>
))}
</nav>
<div className="spacer" />
<button onClick={() => SyncApi.incremental()}>Sync now</button>
<span className="user">{user?.email}</span>
<button className="ghost" onClick={logout}>Log out</button>
</header>
<main>
<Outlet />