/* login.jsx — Entrar OU Criar conta. Sem Discord. */ function Sparks() { const pts = React.useMemo(() => [...Array(22)].map(() => ({ left: Math.random()*100 + "%", top: Math.random()*100 + "%", delay: (Math.random()*4).toFixed(2) + "s", dur: (2.5 + Math.random()*3).toFixed(2) + "s", scale: (0.5 + Math.random()*1.4).toFixed(2), })), []); return (<> {pts.map((p, i) => ( ))} ); } function LoginScreen({ onLogin }) { const { Ic, FX } = window; const [mode, setMode] = React.useState("login"); const [leaving, setLeaving] = React.useState(false); const [email, setEmail] = React.useState(""); const [pw, setPw] = React.useState(""); const [show, setShow] = React.useState(false); const [rName, setRName] = React.useState(""); const [rEmail, setREmail] = React.useState(""); const [rPw, setRPw] = React.useState(""); const [rPw2, setRPw2] = React.useState(""); const [busy, setBusy] = React.useState(false); const [err, setErr] = React.useState(""); const [okMsg, setOkMsg] = React.useState(""); const doLogin = async (e) => { if (e) e.preventDefault(); if (leaving || busy) return; setErr(""); if (!email.trim() || pw.length < 4) { setErr("Informe e-mail e senha."); return; } setBusy(true); try { const res = await window.Api.login(email.trim(), pw); FX.sfx.play("complete"); setLeaving(true); setTimeout(() => onLogin(res.user), 520); } catch (e) { setErr(e.message || "Falha no login"); FX.sfx.play("click"); setBusy(false); } }; const doRegister = async (e) => { if (e) e.preventDefault(); if (busy) return; setErr(""); if (!rName.trim() || rName.trim().length < 2) { setErr("Informe seu nome (mínimo 2 letras)."); return; } if (!rEmail.trim() || !/.+@.+\..+/.test(rEmail)) { setErr("E-mail inválido."); return; } if (rPw.length < 12) { setErr("A senha precisa de no mínimo 12 caracteres."); return; } if (rPw !== rPw2) { setErr("As senhas não coincidem."); return; } setBusy(true); try { const res = await window.Api.register(rName.trim(), rEmail.trim(), rPw); FX.sfx.play("complete"); setOkMsg(res.message || "Solicitação enviada. Aguarde aprovação do administrador."); setMode("submitted"); setRName(""); setREmail(""); setRPw(""); setRPw2(""); } catch (e) { setErr(e.message || "Falha no cadastro"); FX.sfx.play("click"); } finally { setBusy(false); } }; const Tabs = () => (
); return (
{mode === "login" && (
Mil Grau RP

Bem-vindo de volta

Entre para ver suas tarefas

setEmail(e.target.value)} autoComplete="email" disabled={busy} required />
setPw(e.target.value)} autoComplete="current-password" disabled={busy} required />
{err &&

{err}

}

Sem acesso? { e.preventDefault(); setMode("register"); setErr(""); }}>Criar conta

)} {mode === "register" && (
Mil Grau RP

Criar conta

Após o cadastro, um administrador precisa aprovar seu acesso.

setRName(e.target.value)} autoComplete="name" disabled={busy} required />
setREmail(e.target.value)} autoComplete="email" disabled={busy} required />
setRPw(e.target.value)} autoComplete="new-password" disabled={busy} required />
setRPw2(e.target.value)} autoComplete="new-password" disabled={busy} required />
{err &&

{err}

}

Já tem conta? { e.preventDefault(); setMode("login"); setErr(""); }}>Entrar

)} {mode === "submitted" && (
Mil Grau RP
📨

Solicitação enviada

{okMsg}

)}
Mil Grau RP

Mil Grau RP

Organize seus projetos e bugs em um só lugar

); } window.LoginScreen = LoginScreen;