ATUALIZACOES

This commit is contained in:
2026-03-17 17:21:21 +00:00
parent 3404c0044d
commit ff2b3fd8e7
17 changed files with 2058 additions and 1694 deletions

View File

@@ -1,6 +1,15 @@
// components/Auth.tsx
import { Ionicons } from '@expo/vector-icons';
import { useRouter } from 'expo-router';
import { useState } from 'react';
import { ActivityIndicator, Alert, StyleSheet, Text, TextInput, TouchableOpacity, View } from 'react-native';
import {
ActivityIndicator,
StyleSheet,
Text,
TextInput,
TouchableOpacity,
View
} from 'react-native';
import { supabase } from '../app/lib/supabase';
interface AuthProps {
@@ -11,96 +20,206 @@ export default function Auth({ onLoginSuccess }: AuthProps) {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [loading, setLoading] = useState(false);
const [isFocused, setIsFocused] = useState<string | null>(null);
// ESTADO PARA OS AVISOS MODERNOS
const [errorMessage, setErrorMessage] = useState<string | null>(null);
const router = useRouter();
const handleLogin = async () => {
setErrorMessage(null); // Limpa erros anteriores
if (!email || !password) {
Alert.alert('Atenção', 'Preencha todos os campos');
setErrorMessage('Preencha todos os campos para continuar.');
return;
}
setLoading(true);
try {
// 1. LOGIN NO AUTH DO SUPABASE
const { data: { user }, error: authError } = await supabase.auth.signInWithPassword({ email, password });
if (authError) throw authError;
// 2. BUSCAR DADOS NA TABELA PROFILES LOGO APÓS O LOGIN
if (user) {
const { data: profile, error: profileError } = await supabase
const { error: profileError } = await supabase
.from('profiles')
.select('*')
.eq('id', user.id)
.single();
if (profileError) {
console.warn("Perfil não encontrado na tabela profiles.");
} else {
console.log("Perfil carregado com sucesso:", profile.nome);
}
if (profileError) console.warn("Perfil não encontrado.");
}
Alert.alert('Bem-vindo(a)!');
// 3. SE SUCESSO, EXECUTA O CALLBACK E NAVEGA
if (onLoginSuccess) {
onLoginSuccess();
} else {
router.replace('/(tabs)/estagios'); // Caminho padrão caso não venha callback
router.replace('/(tabs)/estagios');
}
} catch (err: any) {
Alert.alert('Erro', err.message);
// Tradução simples de erro comum
const msg = err.message === 'Invalid login credentials'
? 'Email ou palavra-passe incorretos.'
: 'Ocorreu um erro ao entrar. Tenta novamente.';
setErrorMessage(msg);
} finally {
setLoading(false);
}
};
return (
<View style={styles.form}>
<Text style={styles.label}>Email</Text>
<TextInput
style={styles.input}
placeholder="email@address.com"
value={email}
onChangeText={setEmail}
autoCapitalize="none"
keyboardType="email-address"
editable={!loading}
/>
<View style={styles.container}>
{/* AVISO DE ERRO MODERNO */}
{errorMessage && (
<View style={styles.errorBanner}>
<Ionicons name="alert-circle" size={20} color="#EF4444" />
<Text style={styles.errorText}>{errorMessage}</Text>
</View>
)}
<Text style={styles.label}>Palavra-passe</Text>
<TextInput
style={styles.input}
placeholder="Insira a sua palavra-passe"
value={password}
onChangeText={setPassword}
secureTextEntry
editable={!loading}
/>
<View style={styles.inputWrapper}>
<Text style={[styles.label, isFocused === 'email' && { color: '#2390a6' }]}>Email</Text>
<TextInput
style={[styles.input, isFocused === 'email' && styles.inputFocused]}
placeholder="Insira o seu email"
placeholderTextColor="#94A3B8"
value={email}
onChangeText={(val) => {
setEmail(val);
if(errorMessage) setErrorMessage(null);
}}
onFocus={() => setIsFocused('email')}
onBlur={() => setIsFocused(null)}
autoCapitalize="none"
keyboardType="email-address"
editable={!loading}
/>
</View>
<View style={styles.inputWrapper}>
<View style={styles.labelRow}>
<Text style={[styles.label, isFocused === 'pass' && { color: '#2390a6' }]}>Palavra-passe</Text>
</View>
<TextInput
style={[styles.input, isFocused === 'pass' && styles.inputFocused]}
placeholder="Insira a sua palavra-passe"
placeholderTextColor="#94A3B8"
value={password}
onChangeText={(val) => {
setPassword(val);
if(errorMessage) setErrorMessage(null);
}}
onFocus={() => setIsFocused('pass')}
onBlur={() => setIsFocused(null)}
secureTextEntry
editable={!loading}
/>
</View>
<TouchableOpacity
activeOpacity={0.8}
style={[styles.button, loading && styles.buttonDisabled]}
onPress={handleLogin}
disabled={loading}
>
{loading ? <ActivityIndicator color="#fff" /> : <Text style={styles.buttonText}>ENTRAR</Text>}
{loading ? (
<ActivityIndicator color="#fff" />
) : (
<Text style={styles.buttonText}>ENTRAR</Text>
)}
</TouchableOpacity>
<TouchableOpacity onPress={() => router.push('/redefenirsenha')}>
<Text style={styles.forgotText}>Esqueceu a palavra-passe?</Text>
<TouchableOpacity
style={styles.forgotButton}
onPress={() => router.push('/redefenirsenha')}
>
<Text style={styles.forgotText}>Esqueceu-se da palavra-passe?</Text>
</TouchableOpacity>
</View>
);
}
// ... (teus estilos mantêm-se iguais)
const styles = StyleSheet.create({
form: { backgroundColor: '#fff', borderRadius: 16, padding: 24, marginTop: 20, shadowColor: '#000', shadowOffset: { width: 0, height: 6 }, shadowOpacity: 0.1, shadowRadius: 12, elevation: 5 },
label: { fontSize: 14, fontWeight: '600', color: '#2d3436', marginBottom: 8 },
input: { backgroundColor: '#f1f2f6', borderRadius: 12, paddingHorizontal: 16, paddingVertical: 14, fontSize: 16, marginBottom: 20, borderWidth: 0, color: '#2d3436' },
button: { backgroundColor: '#0984e3', borderRadius: 12, paddingVertical: 16, alignItems: 'center', marginBottom: 12, shadowColor: '#0984e3', shadowOffset: { width: 0, height: 4 }, shadowOpacity: 0.3, shadowRadius: 6, elevation: 3 },
buttonDisabled: { backgroundColor: '#74b9ff' },
buttonText: { color: '#fff', fontSize: 17, fontWeight: '700' },
forgotText: { color: '#0984e3', fontSize: 15, fontWeight: '500', textAlign: 'center', marginTop: 8 },
container: {
width: '100%',
marginTop: 10,
},
errorBanner: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: '#FEF2F2',
padding: 16,
borderRadius: 16,
marginBottom: 20,
borderWidth: 1,
borderColor: '#FEE2E2',
},
errorText: {
color: '#B91C1C',
fontSize: 14,
fontWeight: '600',
marginLeft: 10,
flex: 1,
},
inputWrapper: {
marginBottom: 20,
},
labelRow: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
},
label: {
fontSize: 14,
fontWeight: '700',
color: '#475569',
marginBottom: 10,
marginLeft: 4,
},
input: {
backgroundColor: '#FBFDFF',
borderRadius: 18,
paddingHorizontal: 20,
paddingVertical: 18,
fontSize: 16,
color: '#0F172A',
borderWidth: 1.5,
borderColor: '#F1F5F9',
},
inputFocused: {
borderColor: '#2390a6',
backgroundColor: '#FFFFFF',
},
button: {
backgroundColor: '#dd8707', // A cor que pediste
borderRadius: 18,
paddingVertical: 20,
alignItems: 'center',
marginTop: 10,
shadowColor: '#dd8707',
shadowOffset: { width: 0, height: 6 },
shadowOpacity: 0.3,
shadowRadius: 10,
elevation: 6,
},
buttonDisabled: {
backgroundColor: '#E2E8F0',
elevation: 0,
},
buttonText: {
color: '#fff',
fontSize: 16,
fontWeight: '800',
letterSpacing: 1,
},
forgotButton: {
marginTop: 20,
alignItems: 'center',
},
forgotText: {
color: '#2390a6',
fontSize: 15,
fontWeight: '700',
},
});