This commit is contained in:
Rayan Konecny 2026-04-27 22:32:51 -03:00
parent 63c9f720ad
commit 227b492bc3
10 changed files with 115 additions and 24 deletions

9
.claude/settings.json Normal file
View file

@ -0,0 +1,9 @@
{
"permissions": {
"allow": [
"Bash(xargs cat *)",
"Bash(python3 -c \"import json,sys; d=json.load\\(sys.stdin\\); print\\(json.dumps\\(d.get\\('exports', {}\\), indent=2\\)\\)\")",
"Bash(python3 -c \"import json,sys; d=json.load\\(sys.stdin\\); print\\(json.dumps\\({k: v for k, v in d.items\\(\\) if k not in ['dependencies', 'devDependencies', 'peerDependencies']}, indent=2\\)\\)\")"
]
}
}

View file

@ -2,7 +2,7 @@ import { Response } from 'express';
import { AuthRequest } from '../middlewares/auth.middleware.js'; import { AuthRequest } from '../middlewares/auth.middleware.js';
import * as usersService from '../services/users.service.js'; import * as usersService from '../services/users.service.js';
export async function getMe(req: AuthRequest, res: Response) { export async function get(req: AuthRequest, res: Response): Promise<void> {
try { try {
const user = await usersService.getUser(req.userId!); const user = await usersService.getUser(req.userId!);
res.json(user); res.json(user);
@ -11,7 +11,7 @@ export async function getMe(req: AuthRequest, res: Response) {
} }
} }
export async function updateMe(req: AuthRequest, res: Response) { export async function update(req: AuthRequest, res: Response): Promise<void> {
const parsed = usersService.updateUserSchema.safeParse(req.body); const parsed = usersService.updateUserSchema.safeParse(req.body);
if (!parsed.success) { if (!parsed.success) {
res.status(400).json({ error: parsed.error.flatten() }); res.status(400).json({ error: parsed.error.flatten() });
@ -26,7 +26,8 @@ export async function updateMe(req: AuthRequest, res: Response) {
} }
} }
export async function deleteMe(req: AuthRequest, res: Response) { export async function remove(req: AuthRequest, res: Response): Promise<void> {
try { try {
await usersService.deleteUser(req.userId!); await usersService.deleteUser(req.userId!);
res.status(204).send(); res.status(204).send();

View file

@ -6,8 +6,8 @@ const router = Router();
router.use(authenticate); router.use(authenticate);
router.get('/me', usersController.getMe); router.get('/me', usersController.get);
router.put('/me', usersController.updateMe); router.put('/me', usersController.update);
router.delete('/me', usersController.deleteMe); router.delete('/me', usersController.remove);
export default router; export default router;

View file

@ -30,7 +30,6 @@
"react-native-worklets": "0.5.1" "react-native-worklets": "0.5.1"
}, },
"devDependencies": { "devDependencies": {
"@types/react": "~19.1.0",
"react-test-renderer": "19.1.0", "react-test-renderer": "19.1.0",
"typescript": "~5.9.2" "typescript": "~5.9.2"
} }

View file

@ -31,7 +31,6 @@
"react-native-worklets": "0.5.1" "react-native-worklets": "0.5.1"
}, },
"devDependencies": { "devDependencies": {
"@types/react": "~19.1.0",
"react-test-renderer": "19.1.0", "react-test-renderer": "19.1.0",
"typescript": "~5.9.2" "typescript": "~5.9.2"
}, },

View file

@ -14,19 +14,29 @@ import {
import { Button } from "@/components/Button"; import { Button } from "@/components/Button";
import { Input } from "@/components/Input"; import { Input } from "@/components/Input";
import { Link } from "expo-router"; import { Link } from "expo-router";
import { api, setAuthToken } from "@/server/api";
// Página de login
export default function IndexPage() { export default function IndexPage() {
const [email, setEmail] = useState(""); const [email, setEmail] = useState("");
const [password, setPassword] = useState(""); const [password, setPassword] = useState("");
const [loading, setLoading] = useState(false);
function handleSignIn() { async function handleSignIn() {
if (!email.trim() || !password.trim()) { if (!email.trim() || !password.trim()) {
Alert.alert("Entrar", "Por favor, preencha todos os campos."); Alert.alert("Entrar", "Por favor, preencha todos os campos.");
return; return;
} }
Alert.alert("Bem-vindo", `Acessando com o e-mail ${email}`); try {
setLoading(true);
const { data } = await api.post("/auth/login", { email, password });
setAuthToken(data.accessToken);
} catch (err: any) {
const message = err.response?.data?.error ?? "Erro ao fazer login.";
Alert.alert("Erro", message);
} finally {
setLoading(false);
}
} }
return ( return (
@ -54,6 +64,7 @@ export default function IndexPage() {
<Input <Input
placeholder="E-mail" placeholder="E-mail"
keyboardType="email-address" keyboardType="email-address"
autoCapitalize="none"
onChangeText={setEmail} onChangeText={setEmail}
/> />
@ -63,7 +74,11 @@ export default function IndexPage() {
onChangeText={setPassword} onChangeText={setPassword}
/> />
<Button label="Entrar" onPress={handleSignIn} /> <Button
label={loading ? "Entrando..." : "Entrar"}
onPress={handleSignIn}
disabled={loading}
/>
</View> </View>
<Text style={styles.footerText}> <Text style={styles.footerText}>
@ -93,7 +108,7 @@ const styles = StyleSheet.create({
title: { title: {
marginTop: 62, marginTop: 62,
fontSize: 32, fontSize: 32,
fontWeight: 900, fontWeight: "900",
color: "#e7e7e7", color: "#e7e7e7",
}, },
subtitle: { subtitle: {
@ -111,6 +126,6 @@ const styles = StyleSheet.create({
}, },
footerLink: { footerLink: {
color: "#007AFF", color: "#007AFF",
fontWeight: 700, fontWeight: "700",
}, },
}); });

View file

@ -1,4 +1,7 @@
import { useState } from "react";
import { import {
Alert,
Image, Image,
KeyboardAvoidingView, KeyboardAvoidingView,
Platform, Platform,
@ -10,14 +13,46 @@ import {
import { Button } from "@/components/Button"; import { Button } from "@/components/Button";
import { Input } from "@/components/Input"; import { Input } from "@/components/Input";
import { Link } from "expo-router"; import { Link, useRouter } from "expo-router";
import { api } from "@/server/api";
// Página de cadastro
export default function Signup() { export default function Signup() {
const router = useRouter();
const [name, setName] = useState("");
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [confirmPassword, setConfirmPassword] = useState("");
const [loading, setLoading] = useState(false);
async function handleSignUp() {
if (!name.trim() || !email.trim() || !password.trim() || !confirmPassword.trim()) {
Alert.alert("Cadastrar", "Por favor, preencha todos os campos.");
return;
}
if (password !== confirmPassword) {
Alert.alert("Cadastrar", "As senhas não coincidem.");
return;
}
try {
setLoading(true);
await api.post("/auth/register", { name, email, password });
Alert.alert("Sucesso", "Conta criada com sucesso!", [
{ text: "OK", onPress: () => router.replace("/") },
]);
} catch (err: any) {
const message = err.response?.data?.error ?? "Erro ao criar conta.";
Alert.alert("Erro", message);
} finally {
setLoading(false);
}
}
return ( return (
<KeyboardAvoidingView <KeyboardAvoidingView
style={{ flex: 1 }} style={{ flex: 1 }}
behavior={Platform.select({ ios: "padding", android: "height" })} behavior={Platform.OS === "ios" ? "padding" : "height"}
> >
<ScrollView <ScrollView
contentContainerStyle={{ flexGrow: 1 }} contentContainerStyle={{ flexGrow: 1 }}
@ -34,11 +69,24 @@ export default function Signup() {
<Text style={styles.subtitle}>Crie sua conta para acessar.</Text> <Text style={styles.subtitle}>Crie sua conta para acessar.</Text>
<View style={styles.form}> <View style={styles.form}>
<Input placeholder="Nome" /> <Input placeholder="Nome" onChangeText={setName} />
<Input placeholder="E-mail" keyboardType="email-address" /> <Input
<Input placeholder="Senha" secureTextEntry /> placeholder="E-mail"
<Input placeholder="Confirmar Senha" secureTextEntry /> keyboardType="email-address"
<Button label="Cadastrar" /> autoCapitalize="none"
onChangeText={setEmail}
/>
<Input placeholder="Senha" secureTextEntry onChangeText={setPassword} />
<Input
placeholder="Confirmar Senha"
secureTextEntry
onChangeText={setConfirmPassword}
/>
<Button
label={loading ? "Cadastrando..." : "Cadastrar"}
onPress={handleSignUp}
disabled={loading}
/>
</View> </View>
<Text style={styles.footerText}> <Text style={styles.footerText}>

View file

@ -0,0 +1,18 @@
import axios from 'axios';
import { Platform } from 'react-native';
const BASE_URL = Platform.OS === 'android'
? 'http://10.0.2.2:4000'
: 'http://localhost:4000';
export const api = axios.create({
baseURL: BASE_URL,
});
export function setAuthToken(token: string | null) {
if (token) {
api.defaults.headers.common.Authorization = `Bearer ${token}`;
} else {
delete api.defaults.headers.common.Authorization;
}
}

View file

@ -2,8 +2,10 @@
"extends": "expo/tsconfig.base", "extends": "expo/tsconfig.base",
"compilerOptions": { "compilerOptions": {
"strict": true, "strict": true,
"skipLibCheck": true,
"paths": { "paths": {
"@/*": ["./src/*"] "@/*": ["./src/*"],
"expo-router/src/*": ["./node_modules/expo-router/build/*"]
} }
}, },
"include": ["**/*.ts", "**/*.tsx", ".expo/types/**/*.ts", "expo-env.d.ts"] "include": ["**/*.ts", "**/*.tsx", ".expo/types/**/*.ts", "expo-env.d.ts"]