// zlap-termin — app shell z prawdziwym API backend
const { useState, useEffect, useMemo, useRef, useCallback } = React;
const API = window.ZlapApi;
const A = API.adapters;
/* Topbar */
function TopBar({ state, email, onLogout, startedAt }) {
return (
);
}
/* Sidebar */
const NAV = [
{ id: "dashboard", label: "Dashboard", icon: "activity" },
{ id: "check", label: "Sprawdź", icon: "search" },
{ id: "auto", label: "Auto-task", icon: "zap" },
{ id: "reservations", label: "Rezerwacje", icon: "check" },
{ id: "slots", label: "Terminy", icon: "clock" },
{ id: "logs", label: "Logi", icon: "terminal" },
{ id: "settings", label: "Ustawienia", icon: "settings" },
];
function Sidebar({ active, onNav, collapsed, setCollapsed }) {
return (
<>
>
);
}
/* Toast host */
function useToasts() {
const [toasts, setToasts] = useState([]);
const push = useCallback((msg, tone = "ok") => {
const id = Math.random().toString(36).slice(2);
setToasts(t => [...t, { id, msg, tone }]);
setTimeout(() => setToasts(t => t.filter(x => x.id !== id)), 5000);
}, []);
const node = (
{toasts.map(t => (
{t.msg}
))}
);
return [push, node];
}
/* ============================ APP ============================ */
function App() {
const [session, setSession] = useState(null); // {email, csrf_token} albo null
const [loading, setLoading] = useState(true);
const [route, setRoute] = useState("dashboard");
const [status, setStatus] = useState(null);
const [navCollapsed, setNavCollapsed] = useState(false);
const [toast, toastNode] = useToasts();
// Bootstrap: sprawdź session
useEffect(() => {
API.session()
.then(s => setSession(s))
.catch(() => setSession(null))
.finally(() => setLoading(false));
}, []);
// Polling statusu (co 3s gdy zalogowany)
useEffect(() => {
if (!session) return;
let cancelled = false;
const tick = async () => {
try {
const s = await API.status();
if (!cancelled) setStatus(s);
} catch (e) {
if (e.status === 401 && !cancelled) setSession(null);
}
};
tick();
const iv = setInterval(tick, 3000);
return () => { cancelled = true; clearInterval(iv); };
}, [session]);
const handleLogin = async (email, password) => {
try {
const res = await API.login(email, password);
setSession(res);
toast("Zalogowano", "ok");
} catch (e) {
toast(`Błąd logowania: ${e.message || e}`, "err");
throw e;
}
};
const handleLogout = async () => {
try { await API.logout(); } catch (_) {}
setSession(null);
toast("Wylogowano", "info");
};
if (loading) {
return (
);
}
if (!session) {
return ;
}
const botState = status?.state || "offline";
const startedAt = status?.started_at ? new Date(status.started_at) : null;
const view = () => {
switch (route) {
case "dashboard": return ;
case "check": return ;
case "auto": return ;
case "reservations": return ;
case "slots": return ;
case "logs": return ;
case "settings": return ;
default: return null;
}
};
return (
);
}
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render();