/* chrome.jsx — Sidebar, XP widget, Streak, Stats, Topbar, Filters, Toasts. Exported to window. */
function XPWidget({ xp, justLeveled }) {
const { Ic, Store } = window;
const info = Store.levelInfo(xp);
return (
{info.level}
Nível {info.level}
Dev {info.level < 3 ? "Aprendiz" : info.level < 6 ? "Pleno" : info.level < 10 ? "Sênior" : "Lenda"}
{info.into}/{info.need} XP
);
}
function StreakPill({ streak }) {
return (
🔥
{streak} {streak === 1 ? "dia" : "dias"}
sequência
);
}
const NAV = [
{ id: "tarefas", label: "Tarefas", icon: "Tasks" },
];
function Sidebar({ game, justLeveled, user, onOpenSettings, onLogout, canAdmin, onOpenAdmin,
canApprove, pendingCount, onOpenApprovals }) {
const { Ic } = window;
const [active, setActive] = React.useState("tarefas");
const [menu, setMenu] = React.useState(false);
const ref = React.useRef(null);
React.useEffect(() => {
const h = (e) => { if (ref.current && !ref.current.contains(e.target)) setMenu(false); };
document.addEventListener("mousedown", h); return () => document.removeEventListener("mousedown", h);
}, []);
const initials = (user.name || "U").trim().split(/\s+/).map(w => w[0]).slice(0, 2).join("").toUpperCase();
return (
);
}
function Topbar({ query, setQuery, onNew, muted, toggleMute }) {
const { Ic } = window;
return (
);
}
function StatBar({ game, totalTasks }) {
const { Ic, Store } = window;
const rate = totalTasks ? Math.round((game.totalDone / (totalTasks + game.totalDone)) * 100) : 0;
const stats = [
{ ico: "Check", c: "var(--c-melhoria)", bg: "var(--c-melhoria-bg)", num: game.todayCount, lbl: "concluídas hoje" },
{ ico: "Calendar", c: "var(--accent)", bg: "rgba(var(--accent-rgb),0.14)", num: game.weekCount, lbl: "essa semana" },
{ ico: "Target", c: "var(--gold)", bg: "rgba(245,185,66,0.14)", num: rate + "%", lbl: "taxa de conclusão" },
{ ico: "Trophy", c: "var(--c-feature)", bg: "var(--c-feature-bg)", num: game.totalDone, lbl: "total concluídas" },
];
return (
{stats.map((s, i) => {
const I = Ic[s.ico];
return (
);
})}
);
}
function Filters({ catFilter, setCatFilter, prioFilter, setPrioFilter }) {
const { Store } = window;
const cats = Object.entries(Store.CATEGORIES);
const prios = Object.entries(Store.PRIORITIES);
const toggle = (val, cur, set) => set(cur === val ? null : val);
const any = catFilter || prioFilter;
return (
{ setCatFilter(null); setPrioFilter(null); }}>Todas
{cats.map(([k, m]) => (
toggle(k, catFilter, setCatFilter)}
style={catFilter === k ? { color: m.color, borderColor: m.color } : {}}>{m.label}
))}
{prios.map(([k, m]) => (
toggle(k, prioFilter, setPrioFilter)}
style={prioFilter === k ? { color: m.color, borderColor: m.color } : {}}>{m.label}
))}
{any && { setCatFilter(null); setPrioFilter(null); }}>Limpar filtros ✕ }
);
}
function ToastHost({ toasts }) {
return (
{toasts.map(t => (
{t.e} {t.t}
))}
);
}
/* MobileHero — logo grande de marca no topo do conteúdo (só no celular).
Fica dentro da .main (rola com a página); o CSS a esconde no desktop. */
function MobileHero() {
return (
MIL GRAU RP
TAREFAS
);
}
/* MobileChrome — barra superior, FAB e barra inferior do celular.
Renderizada sempre; o CSS a esconde no desktop (≥821px). */
function MobileChrome({ game, justLeveled, user, muted, toggleMute, onNew,
onOpenSettings, onLogout, canAdmin, onOpenAdmin,
canApprove, pendingCount, onOpenApprovals }) {
const { Ic, FX } = window;
const [drawer, setDrawer] = React.useState(false);
const [sheet, setSheet] = React.useState(null); // "nivel" | "sequencia" | null
const initials = (user.name || "U").trim().split(/\s+/).map(w => w[0]).slice(0, 2).join("").toUpperCase();
return (
<>
{ setDrawer(true); FX.sfx.play("click"); }}>
{muted ? : }
{ setSheet(null); const m = document.querySelector(".main"); if (m) m.scrollTo({ top: 0, behavior: "smooth" }); }}>
Tarefas
{ setSheet(s => s === "nivel" ? null : "nivel"); FX.sfx.play("click"); }}>
Nível
{ setSheet(s => s === "sequencia" ? null : "sequencia"); FX.sfx.play("click"); }}>
Sequência
{drawer && (
setDrawer(false)}>
e.stopPropagation()}>
Mil Grau RPTarefas
{user.avatar
?
:
{initials} }
{user.name} {user.role}
{ setDrawer(false); onOpenSettings("perfil"); }}> Meu perfil
{ setDrawer(false); onOpenSettings("conta"); }}> Configurações
{canApprove && (
{ setDrawer(false); onOpenApprovals && onOpenApprovals(); }}>
Solicitações
{pendingCount > 0 && {pendingCount} }
)}
{canAdmin && { setDrawer(false); onOpenAdmin && onOpenAdmin(); }}> Administração }
Sair
)}
{sheet && (
setSheet(null)}>
e.stopPropagation()}>
{sheet === "nivel" && (<>
Seu nível
Conclua tarefas para ganhar XP e subir de nível.
>)}
{sheet === "sequencia" && (<>
Sua sequência
Volte todos os dias e conclua tarefas para manter sua ofensiva acesa.
>)}
)}
>
);
}
Object.assign(window, { Sidebar, Topbar, StatBar, Filters, ToastHost, XPWidget, StreakPill, MobileChrome, MobileHero });