Initial SeConectai briefing system
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
"use server";
|
||||
|
||||
import argon2 from "argon2";
|
||||
import { redirect } from "next/navigation";
|
||||
import { z } from "zod";
|
||||
import { db } from "@/lib/db";
|
||||
import { hashToken } from "@/app/(publico)/recuperar-senha/actions";
|
||||
|
||||
const redefinirSenhaSchema = z
|
||||
.object({
|
||||
token: z.string().min(1),
|
||||
senha: z.string().min(8, "A senha precisa ter pelo menos 8 caracteres."),
|
||||
confirmarSenha: z.string()
|
||||
})
|
||||
.refine((data) => data.senha === data.confirmarSenha, {
|
||||
path: ["confirmarSenha"],
|
||||
message: "As senhas nao conferem."
|
||||
});
|
||||
|
||||
export type RedefinirSenhaState = {
|
||||
ok: boolean;
|
||||
message?: string;
|
||||
};
|
||||
|
||||
export async function redefinirSenha(
|
||||
_state: RedefinirSenhaState,
|
||||
formData: FormData
|
||||
): Promise<RedefinirSenhaState> {
|
||||
const parsed = redefinirSenhaSchema.safeParse({
|
||||
token: formData.get("token"),
|
||||
senha: formData.get("senha"),
|
||||
confirmarSenha: formData.get("confirmarSenha")
|
||||
});
|
||||
|
||||
if (!parsed.success) {
|
||||
return {
|
||||
ok: false,
|
||||
message: parsed.error.issues[0]?.message ?? "Revise os dados informados."
|
||||
};
|
||||
}
|
||||
|
||||
const tokenHash = hashToken(parsed.data.token);
|
||||
const resetToken = await db.passwordResetToken.findUnique({
|
||||
where: { tokenHash },
|
||||
select: {
|
||||
id: true,
|
||||
userId: true,
|
||||
expiraEm: true,
|
||||
usadoEm: true
|
||||
}
|
||||
});
|
||||
|
||||
if (!resetToken || resetToken.usadoEm || resetToken.expiraEm < new Date()) {
|
||||
return { ok: false, message: "Link invalido ou expirado." };
|
||||
}
|
||||
|
||||
await db.$transaction([
|
||||
db.user.update({
|
||||
where: { id: resetToken.userId },
|
||||
data: { senhaHash: await argon2.hash(parsed.data.senha) }
|
||||
}),
|
||||
db.passwordResetToken.update({
|
||||
where: { id: resetToken.id },
|
||||
data: { usadoEm: new Date() }
|
||||
})
|
||||
]);
|
||||
|
||||
redirect("/login");
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import Link from "next/link";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { RedefinirSenhaForm } from "./redefinir-senha-form";
|
||||
|
||||
export default function RedefinirSenhaPage({
|
||||
searchParams
|
||||
}: {
|
||||
searchParams: { token?: string };
|
||||
}) {
|
||||
const token = searchParams.token ?? "";
|
||||
|
||||
return (
|
||||
<main className="container flex min-h-screen items-center justify-center py-8">
|
||||
<Card className="w-full max-w-md shadow-sm">
|
||||
<CardHeader>
|
||||
<CardTitle>Redefinir senha</CardTitle>
|
||||
<p className="text-sm leading-6 text-muted-foreground">
|
||||
Crie uma nova senha para acessar o briefing.
|
||||
</p>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{token ? (
|
||||
<RedefinirSenhaForm token={token} />
|
||||
) : (
|
||||
<p className="text-sm text-muted-foreground">Link de redefinicao invalido.</p>
|
||||
)}
|
||||
<p className="mt-6 text-center text-sm text-muted-foreground">
|
||||
<Link href="/login" className="text-primary underline-offset-4 hover:underline">
|
||||
Voltar para o login
|
||||
</Link>
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
"use client";
|
||||
|
||||
import { useFormState, useFormStatus } from "react-dom";
|
||||
import { Alert, AlertDescription } from "@/components/ui/alert";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { redefinirSenha, type RedefinirSenhaState } from "./actions";
|
||||
|
||||
const initialState: RedefinirSenhaState = { ok: false };
|
||||
|
||||
function SubmitButton() {
|
||||
const { pending } = useFormStatus();
|
||||
|
||||
return (
|
||||
<Button type="submit" className="w-full" disabled={pending}>
|
||||
{pending ? "Salvando..." : "Redefinir senha"}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
export function RedefinirSenhaForm({ token }: { token: string }) {
|
||||
const [state, action] = useFormState(redefinirSenha, initialState);
|
||||
|
||||
return (
|
||||
<form action={action} className="space-y-4">
|
||||
<input type="hidden" name="token" value={token} />
|
||||
{state.message ? (
|
||||
<Alert variant="destructive">
|
||||
<AlertDescription>{state.message}</AlertDescription>
|
||||
</Alert>
|
||||
) : null}
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="senha">Nova senha</Label>
|
||||
<Input id="senha" name="senha" type="password" autoComplete="new-password" required />
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="confirmarSenha">Confirmar nova senha</Label>
|
||||
<Input id="confirmarSenha" name="confirmarSenha" type="password" autoComplete="new-password" required />
|
||||
</div>
|
||||
<SubmitButton />
|
||||
</form>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user