140 lines
5.2 KiB
TypeScript
140 lines
5.2 KiB
TypeScript
"use client";
|
|
|
|
import Image from "next/image";
|
|
import { FormEvent, useState } from "react";
|
|
import { useRouter, useSearchParams } from "next/navigation";
|
|
import { api, type SessionUser } from "@/lib/api";
|
|
|
|
export function LoginForm({
|
|
variant,
|
|
tenantSlug,
|
|
}: {
|
|
variant: "admin" | "tenant";
|
|
tenantSlug?: string;
|
|
}) {
|
|
const router = useRouter();
|
|
const search = useSearchParams();
|
|
const oauthError = {
|
|
google_off: "Entrar com o Google está indisponível no momento.",
|
|
google_unlinked: "Este e-mail Google não tem acesso à administração.",
|
|
google_denied: "Não foi possível entrar com o Google.",
|
|
}[search.get("error") ?? ""];
|
|
const [email, setEmail] = useState("");
|
|
const [password, setPassword] = useState("");
|
|
const [error, setError] = useState("");
|
|
const [loading, setLoading] = useState(false);
|
|
|
|
async function onSubmit(event: FormEvent) {
|
|
event.preventDefault();
|
|
setError("");
|
|
setLoading(true);
|
|
try {
|
|
const result = await api<{ user: SessionUser }>("/api/auth/login", {
|
|
method: "POST",
|
|
body: JSON.stringify({
|
|
email,
|
|
password,
|
|
tenantSlug: variant === "tenant" ? tenantSlug : undefined,
|
|
}),
|
|
});
|
|
const next = search.get("next");
|
|
if (result.user.kind === "platform") {
|
|
router.replace(next?.startsWith("/admin") ? next : "/admin");
|
|
return;
|
|
}
|
|
const home = `/${result.user.tenantSlug}`;
|
|
router.replace(next?.startsWith(home) ? next : home);
|
|
} catch (err) {
|
|
setError(err instanceof Error ? err.message : "Falha no login.");
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
}
|
|
|
|
return (
|
|
<main className="flex min-h-dvh flex-col bg-black text-[#E8F4E5]">
|
|
<div className="mx-auto flex w-full max-w-md flex-1 flex-col justify-center px-6 py-16">
|
|
<a href="/" className="mb-10 block w-fit">
|
|
<Image
|
|
src="/brand/sinka-logo.webp"
|
|
alt="Sinka"
|
|
width={180}
|
|
height={58}
|
|
className="h-8 w-auto"
|
|
priority
|
|
/>
|
|
</a>
|
|
<p className="text-xs font-semibold uppercase tracking-[0.28em] text-[#82C85C]">
|
|
{variant === "admin" ? "Administração" : "Operação"}
|
|
</p>
|
|
<h1 className="font-display mt-2 text-4xl font-extrabold uppercase leading-none">
|
|
{variant === "admin" ? "Administração Sinka" : "Entrar"}
|
|
</h1>
|
|
<p className="mt-4 text-[#E8F4E5]/60">
|
|
{variant === "admin"
|
|
? "Crie e acompanhe os clientes da Sinka."
|
|
: "Entre com e-mail e senha, ou com o Google."}
|
|
</p>
|
|
|
|
<form className="mt-8 flex flex-col gap-4" onSubmit={onSubmit}>
|
|
<label className="flex flex-col gap-2 text-sm">
|
|
E-mail
|
|
<input
|
|
className="rounded-xl border border-white/10 bg-white/5 px-4 py-3 outline-none focus:border-[#82C85C]"
|
|
type="email"
|
|
value={email}
|
|
onChange={(event) => setEmail(event.target.value)}
|
|
required
|
|
autoComplete="username"
|
|
/>
|
|
</label>
|
|
<label className="flex flex-col gap-2 text-sm">
|
|
Senha
|
|
<input
|
|
className="rounded-xl border border-white/10 bg-white/5 px-4 py-3 outline-none focus:border-[#82C85C]"
|
|
type="password"
|
|
value={password}
|
|
onChange={(event) => setPassword(event.target.value)}
|
|
required
|
|
autoComplete="current-password"
|
|
/>
|
|
</label>
|
|
{error || oauthError ? <p className="text-sm text-red-300">{error || oauthError}</p> : null}
|
|
<button className="btn-sinka mt-2 justify-center" disabled={loading} type="submit">
|
|
{loading ? "Entrando…" : "Entrar"}
|
|
</button>
|
|
</form>
|
|
<p className="mt-5 text-center text-xs uppercase tracking-[0.2em] text-[#E8F4E5]/35">ou</p>
|
|
<a
|
|
className="btn-google mt-4"
|
|
href={`/api/auth/google${
|
|
variant === "tenant"
|
|
? `?tenant=${encodeURIComponent(tenantSlug ?? "")}&next=${encodeURIComponent(`/${tenantSlug}`)}`
|
|
: "?next=/admin"
|
|
}`}
|
|
>
|
|
<svg width="18" height="18" viewBox="0 0 18 18" aria-hidden>
|
|
<path
|
|
fill="#4285F4"
|
|
d="M17.64 9.2c0-.637-.057-1.251-.164-1.84H9v3.481h4.844c-.209 1.125-.843 2.078-1.796 2.717v2.258h2.908c1.702-1.567 2.684-3.874 2.684-6.615z"
|
|
/>
|
|
<path
|
|
fill="#34A853"
|
|
d="M9 18c2.43 0 4.467-.806 5.956-2.184l-2.908-2.258c-.806.54-1.837.86-3.048.86-2.344 0-4.328-1.584-5.036-3.711H.957v2.332C2.438 15.983 5.482 18 9 18z"
|
|
/>
|
|
<path
|
|
fill="#FBBC05"
|
|
d="M3.964 10.707c-.18-.54-.282-1.117-.282-1.707s.102-1.167.282-1.707V4.961H.957C.348 6.175 0 7.55 0 9s.348 2.825.957 4.039l3.007-2.332z"
|
|
/>
|
|
<path
|
|
fill="#EA4335"
|
|
d="M9 3.58c1.321 0 2.508.454 3.44 1.345l2.582-2.58C13.463.891 11.426 0 9 0 5.482 0 2.438 2.017.957 4.961L3.964 7.29C4.672 5.163 6.656 3.58 9 3.58z"
|
|
/>
|
|
</svg>
|
|
Entrar com o Google
|
|
</a>
|
|
</div>
|
|
</main>
|
|
);
|
|
}
|