Skip to content

Commit 2db6b45

Browse files
committed
feat (auth) - tela de login, registro, recuperação de senha e esqueci minha senha_ fix recuperação de senha + criação de e-mail para tal recuperação + fix layout tela de recuperação de senha _2
1 parent 81e2dd9 commit 2db6b45

8 files changed

Lines changed: 1201 additions & 2 deletions

File tree

src/backend/Usuarios.API/Controllers/AuthControllers.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,8 @@ public async Task<IActionResult> ForgotPassword([FromBody] ForgotPasswordRequest
9595
var resetToken = Guid.NewGuid().ToString();
9696
// Aqui poderia salvar o token no banco com expiração
9797

98-
var resetLink = $"http://localhost:5173/reset-password?token={resetToken}&email={request.Email}";
98+
var frontendUrl = _config["FrontendSettings:Url"] ?? "http://localhost:5173";
99+
var resetLink = $"{frontendUrl.TrimEnd('/')}/reset-password?token={Uri.EscapeDataString(resetToken)}&email={Uri.EscapeDataString(request.Email)}";
99100

100101
var subject = "Redefinição de Senha - Paga Aí";
101102
var body = $@"
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
using System.Net;
2+
using System.Net.Mail;
3+
using Microsoft.Extensions.Configuration;
4+
5+
namespace Usuario.API.Services;
6+
7+
public class EmailService
8+
{
9+
private readonly IConfiguration _config;
10+
11+
public EmailService(IConfiguration config)
12+
{
13+
_config = config;
14+
}
15+
16+
public async Task SendEmailAsync(string toEmail, string subject, string body)
17+
{
18+
var smtpServer = _config["EmailSettings:SmtpServer"];
19+
var smtpPort = int.Parse(_config["EmailSettings:SmtpPort"]!);
20+
var senderEmail = _config["EmailSettings:SenderEmail"];
21+
var senderPassword = _config["EmailSettings:SenderPassword"];
22+
var enableSsl = bool.Parse(_config["EmailSettings:EnableSsl"]!);
23+
24+
using var client = new SmtpClient(smtpServer, smtpPort)
25+
{
26+
Credentials = new NetworkCredential(senderEmail, senderPassword),
27+
EnableSsl = enableSsl
28+
};
29+
30+
var mailMessage = new MailMessage
31+
{
32+
From = new MailAddress(senderEmail!),
33+
Subject = subject,
34+
Body = body,
35+
IsBodyHtml = true
36+
};
37+
38+
mailMessage.To.Add(toEmail);
39+
40+
await client.SendMailAsync(mailMessage);
41+
}
42+
}

src/frontend/src/App.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { isAuthenticated } from "./services/authService";
1010

1111
export default function App() {
1212
const location = useLocation();
13-
const publicRoutes = ["/login", "/register", "/forgot-password", "/"];
13+
const publicRoutes = ["/login", "/register", "/forgot-password", "/reset-password", "/"];
1414
const isPublicRoute = publicRoutes.includes(location.pathname);
1515
const authenticated = isAuthenticated();
1616

Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
import { useState } from "react";
2+
import { Link } from "react-router-dom";
3+
import { forgotPassword } from "../services/authService";
4+
5+
function validarEmail(email) {
6+
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
7+
}
8+
9+
export default function ForgotPassword() {
10+
const [email, setEmail] = useState("");
11+
const [loading, setLoading] = useState(false);
12+
const [erro, setErro] = useState("");
13+
const [sucesso, setSucesso] = useState("");
14+
15+
async function handleSubmit(event) {
16+
event.preventDefault();
17+
setErro("");
18+
setSucesso("");
19+
20+
if (!email) {
21+
setErro("Por favor, informe o e-mail de recuperação.");
22+
return;
23+
}
24+
25+
if (!validarEmail(email)) {
26+
setErro("Informe um e-mail válido.");
27+
return;
28+
}
29+
30+
try {
31+
setLoading(true);
32+
await forgotPassword(email);
33+
setSucesso("Se o e-mail existir, você receberá instruções em breve.");
34+
} catch (e) {
35+
setErro(e.message || "Falha ao enviar instruções.");
36+
} finally {
37+
setLoading(false);
38+
}
39+
}
40+
41+
return (
42+
<div style={styles.page}>
43+
<div style={styles.card}>
44+
<div style={styles.brand}>
45+
<div style={styles.icon}>💰</div>
46+
<div>
47+
<h1 style={styles.title}>Recuperar senha</h1>
48+
<p style={styles.subtitle}>Digite o e-mail cadastrado para receber instruções.</p>
49+
</div>
50+
</div>
51+
52+
<form style={styles.form} onSubmit={handleSubmit}>
53+
<label style={styles.label} htmlFor="email">Email</label>
54+
<input
55+
id="email"
56+
type="email"
57+
placeholder="seu@email.com"
58+
value={email}
59+
onChange={(e) => setEmail(e.target.value)}
60+
style={styles.input}
61+
/>
62+
63+
{erro && <div style={styles.errorMessage}>{erro}</div>}
64+
{sucesso && <div style={styles.successMessage}>{sucesso}</div>}
65+
66+
<button type="submit" style={styles.loginButton} disabled={loading}>
67+
{loading ? "Enviando..." : "Enviar instruções"}
68+
</button>
69+
</form>
70+
71+
<div style={styles.divider}>
72+
<span style={styles.dividerLine} />
73+
<span style={styles.dividerText}>ou</span>
74+
<span style={styles.dividerLine} />
75+
</div>
76+
77+
<div style={styles.createRow}>
78+
<span style={styles.createText}>Lembrou sua senha?</span>
79+
<Link to="/login" style={styles.createLink}>Entrar</Link>
80+
</div>
81+
</div>
82+
</div>
83+
);
84+
}
85+
86+
const styles = {
87+
page: {
88+
minHeight: "100vh",
89+
display: "flex",
90+
alignItems: "center",
91+
justifyContent: "center",
92+
padding: "24px",
93+
background: "radial-gradient(circle at top, rgba(255,255,255,0.14), transparent 30%), linear-gradient(135deg, #6d28d9 0%, #7c3aed 55%, #9333ea 100%)",
94+
},
95+
card: {
96+
width: "100%",
97+
maxWidth: "420px",
98+
background: "#ffffff",
99+
borderRadius: "32px",
100+
boxShadow: "0 32px 80px rgba(15, 23, 42, 0.18)",
101+
padding: "40px 32px",
102+
display: "flex",
103+
flexDirection: "column",
104+
gap: "24px",
105+
},
106+
brand: {
107+
display: "flex",
108+
flexDirection: "column",
109+
alignItems: "center",
110+
textAlign: "center",
111+
gap: "16px",
112+
},
113+
icon: {
114+
width: "72px",
115+
height: "72px",
116+
borderRadius: "20px",
117+
background: "linear-gradient(143deg, rgba(124,58,237,1) 0%, rgba(109,40,217,1) 100%)",
118+
display: "flex",
119+
alignItems: "center",
120+
justifyContent: "center",
121+
fontSize: "28px",
122+
color: "#ffffff",
123+
boxShadow: "0 18px 50px rgba(124, 58, 237, 0.2)",
124+
},
125+
title: {
126+
fontSize: "28px",
127+
fontWeight: 700,
128+
color: "#111827",
129+
margin: 0,
130+
},
131+
subtitle: {
132+
fontSize: "14px",
133+
color: "#6b7280",
134+
},
135+
form: {
136+
display: "flex",
137+
flexDirection: "column",
138+
gap: "18px",
139+
},
140+
label: {
141+
fontSize: "13px",
142+
fontWeight: 600,
143+
color: "#374151",
144+
marginBottom: "8px",
145+
},
146+
input: {
147+
width: "100%",
148+
padding: "14px 16px",
149+
borderRadius: "14px",
150+
border: "1px solid #e5e7eb",
151+
backgroundColor: "#f8fafc",
152+
fontSize: "14px",
153+
color: "#111827",
154+
outline: "none",
155+
},
156+
errorMessage: {
157+
borderRadius: "12px",
158+
padding: "12px 14px",
159+
backgroundColor: "#fee2e2",
160+
color: "#991b1b",
161+
fontSize: "13px",
162+
},
163+
successMessage: {
164+
borderRadius: "12px",
165+
padding: "12px 14px",
166+
backgroundColor: "#dcfce7",
167+
color: "#166534",
168+
fontSize: "13px",
169+
},
170+
loginButton: {
171+
width: "100%",
172+
padding: "14px 16px",
173+
borderRadius: "14px",
174+
border: "none",
175+
backgroundColor: "#7c3aed",
176+
color: "#ffffff",
177+
fontWeight: 700,
178+
fontSize: "15px",
179+
cursor: "pointer",
180+
transition: "background-color 0.2s ease",
181+
},
182+
divider: {
183+
display: "flex",
184+
alignItems: "center",
185+
gap: "16px",
186+
},
187+
dividerLine: {
188+
flex: 1,
189+
height: "1px",
190+
backgroundColor: "#e5e7eb",
191+
},
192+
dividerText: {
193+
fontSize: "13px",
194+
fontWeight: 700,
195+
color: "#9ca3af",
196+
textTransform: "uppercase",
197+
},
198+
createRow: {
199+
display: "flex",
200+
alignItems: "center",
201+
justifyContent: "center",
202+
gap: "6px",
203+
color: "#6b7280",
204+
fontSize: "14px",
205+
},
206+
createText: {
207+
color: "#6b7280",
208+
},
209+
createLink: {
210+
color: "#7c3aed",
211+
textDecoration: "none",
212+
fontWeight: 700,
213+
},
214+
};

0 commit comments

Comments
 (0)