criar aluno
This commit is contained in:
227
app/Professor/Alunos/CriarAluno.tsx
Normal file
227
app/Professor/Alunos/CriarAluno.tsx
Normal file
@@ -0,0 +1,227 @@
|
||||
// app/Professor/Alunos/CriarAluno.tsx
|
||||
import { Ionicons } from '@expo/vector-icons';
|
||||
import { useRouter } from 'expo-router';
|
||||
import { useMemo, useState } from 'react';
|
||||
import {
|
||||
ActivityIndicator,
|
||||
Alert,
|
||||
KeyboardAvoidingView,
|
||||
Platform,
|
||||
ScrollView,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TextInput,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
} from 'react-native';
|
||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||
import { useTheme } from '../../../themecontext';
|
||||
import { supabase } from '../../lib/supabase';
|
||||
|
||||
const CriarAluno = () => {
|
||||
const { isDarkMode } = useTheme();
|
||||
const router = useRouter();
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
// ESTADOS
|
||||
const [email, setEmail] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
const [nome, setNome] = useState('');
|
||||
const [residencia, setResidencia] = useState('');
|
||||
const [telefone, setTelefone] = useState('');
|
||||
const [ano, setAno] = useState('');
|
||||
const [nEscola, setNEscola] = useState('');
|
||||
const [curso, setCurso] = useState('');
|
||||
const [tipo, setTipo] = useState<'aluno' | 'professor' | 'empresa'>('aluno');
|
||||
|
||||
const cores = useMemo(() => ({
|
||||
fundo: isDarkMode ? '#0A0A0A' : '#FFFFFF',
|
||||
card: isDarkMode ? '#161618' : '#F8FAFC',
|
||||
texto: isDarkMode ? '#F8FAFC' : '#1A365D',
|
||||
secundario: isDarkMode ? '#94A3B8' : '#718096',
|
||||
azul: '#2390a6',
|
||||
laranja: '#E38E00',
|
||||
borda: isDarkMode ? '#2D2D2D' : '#E2E8F0',
|
||||
}), [isDarkMode]);
|
||||
|
||||
const handleCriar = async () => {
|
||||
if (!email || !password || !nome || (tipo === 'aluno' && (!nEscola || !ano || !curso))) {
|
||||
Alert.alert("Atenção", "Preenche os campos obrigatórios para evitar que isto dê merda.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (password.length < 6) {
|
||||
Alert.alert("Erro", "A password tem de ter pelo menos 6 caracteres.");
|
||||
return;
|
||||
}
|
||||
|
||||
setLoading(true);
|
||||
|
||||
try {
|
||||
// 1. Criar Utilizador no Auth
|
||||
const { data: authData, error: authError } = await supabase.auth.signUp({
|
||||
email,
|
||||
password,
|
||||
options: { data: { nome, tipo } }
|
||||
});
|
||||
|
||||
if (authError) throw authError;
|
||||
const user = authData.user;
|
||||
if (!user) throw new Error("Erro ao gerar ID.");
|
||||
|
||||
// 2. Tabela 'profiles'
|
||||
const { error: profileError } = await supabase
|
||||
.from('profiles')
|
||||
.upsert({
|
||||
id: user.id,
|
||||
nome,
|
||||
email,
|
||||
residencia,
|
||||
telefone,
|
||||
n_escola: tipo === 'aluno' ? nEscola : null,
|
||||
curso: tipo === 'aluno' ? curso.toUpperCase() : null,
|
||||
tipo
|
||||
});
|
||||
|
||||
if (profileError) throw profileError;
|
||||
|
||||
// 3. Tabela 'alunos' (SÓ SE FOR ALUNO)
|
||||
if (tipo === 'aluno') {
|
||||
const { error: alunoError } = await supabase
|
||||
.from('alunos')
|
||||
.insert([{
|
||||
id: user.id,
|
||||
nome,
|
||||
n_escola: nEscola,
|
||||
ano: parseInt(ano),
|
||||
turma_curso: curso.toUpperCase()
|
||||
}]);
|
||||
if (alunoError) throw alunoError;
|
||||
}
|
||||
|
||||
Alert.alert("Sucesso", `${tipo.toUpperCase()} criado com sucesso!`, [
|
||||
{ text: "OK", onPress: () => router.back() }
|
||||
]);
|
||||
|
||||
} catch (err: any) {
|
||||
console.error(err);
|
||||
Alert.alert("Erro no Registo", err.message);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={{ flex: 1, backgroundColor: cores.fundo }}>
|
||||
<SafeAreaView style={{ flex: 1 }} edges={['top']}>
|
||||
<KeyboardAvoidingView behavior={Platform.OS === 'ios' ? 'padding' : 'height'} style={{ flex: 1 }}>
|
||||
<ScrollView contentContainerStyle={styles.scroll} showsVerticalScrollIndicator={false}>
|
||||
|
||||
<View style={styles.header}>
|
||||
<TouchableOpacity onPress={() => router.back()} style={[styles.backBtn, { borderColor: cores.borda }]}>
|
||||
<Ionicons name="close" size={24} color={cores.azul} />
|
||||
</TouchableOpacity>
|
||||
<View>
|
||||
<Text style={[styles.title, { color: cores.texto }]}>Novo Registo</Text>
|
||||
<Text style={[styles.subtitle, { color: cores.laranja }]}>Criar utilizador no sistema</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* SELETOR DE TIPO */}
|
||||
<Text style={styles.sectionTitle}>Tipo de Utilizador</Text>
|
||||
<View style={styles.selectorContainer}>
|
||||
{(['aluno', 'professor', 'empresa'] as const).map((item) => (
|
||||
<TouchableOpacity
|
||||
key={item}
|
||||
style={[
|
||||
styles.selectorBtn,
|
||||
{ backgroundColor: tipo === item ? cores.azul : cores.card, borderColor: cores.borda }
|
||||
]}
|
||||
onPress={() => setTipo(item)}
|
||||
>
|
||||
<Text style={[styles.selectorText, { color: tipo === item ? '#FFF' : cores.secundario }]}>
|
||||
{item.charAt(0).toUpperCase() + item.slice(1)}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
))}
|
||||
</View>
|
||||
|
||||
<View style={styles.form}>
|
||||
<Text style={styles.sectionTitle}>Dados de Acesso</Text>
|
||||
<TextInput
|
||||
style={[styles.input, { backgroundColor: cores.card, color: cores.texto, borderColor: cores.borda }]}
|
||||
value={email} onChangeText={setEmail} placeholder="Email" autoCapitalize="none" placeholderTextColor={cores.secundario}
|
||||
/>
|
||||
<TextInput
|
||||
style={[styles.input, { backgroundColor: cores.card, color: cores.texto, borderColor: cores.borda }]}
|
||||
value={password} onChangeText={setPassword} secureTextEntry placeholder="Password (mín. 6 caracteres)" placeholderTextColor={cores.secundario}
|
||||
/>
|
||||
|
||||
<Text style={styles.sectionTitle}>Informação Geral</Text>
|
||||
<TextInput
|
||||
style={[styles.input, { backgroundColor: cores.card, color: cores.texto, borderColor: cores.borda }]}
|
||||
value={nome} onChangeText={setNome} placeholder={tipo === 'empresa' ? "Nome da Empresa" : "Nome Completo"} placeholderTextColor={cores.secundario}
|
||||
/>
|
||||
<TextInput
|
||||
style={[styles.input, { backgroundColor: cores.card, color: cores.texto, borderColor: cores.borda }]}
|
||||
value={residencia} onChangeText={setResidencia} placeholder="Morada" placeholderTextColor={cores.secundario}
|
||||
/>
|
||||
<TextInput
|
||||
style={[styles.input, { backgroundColor: cores.card, color: cores.texto, borderColor: cores.borda }]}
|
||||
value={telefone} onChangeText={setTelefone} keyboardType="phone-pad" placeholder="Telemóvel" placeholderTextColor={cores.secundario}
|
||||
/>
|
||||
|
||||
{/* CAMPOS ESPECÍFICOS PARA ALUNO */}
|
||||
{tipo === 'aluno' && (
|
||||
<>
|
||||
<Text style={styles.sectionTitle}>Dados Escolares</Text>
|
||||
<View style={{ flexDirection: 'row', gap: 10 }}>
|
||||
<TextInput
|
||||
style={[styles.input, { flex: 1, backgroundColor: cores.card, color: cores.texto, borderColor: cores.borda }]}
|
||||
value={ano} onChangeText={setAno} keyboardType="numeric" placeholder="Ano" placeholderTextColor={cores.secundario}
|
||||
/>
|
||||
<TextInput
|
||||
style={[styles.input, { flex: 2, backgroundColor: cores.card, color: cores.texto, borderColor: cores.borda }]}
|
||||
value={nEscola} onChangeText={setNEscola} keyboardType="numeric" placeholder="Nº Aluno" placeholderTextColor={cores.secundario}
|
||||
/>
|
||||
</View>
|
||||
<TextInput
|
||||
style={[styles.input, { backgroundColor: cores.card, color: cores.texto, borderColor: cores.borda }]}
|
||||
value={curso} onChangeText={setCurso} autoCapitalize="characters" placeholder="Curso" placeholderTextColor={cores.secundario}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</View>
|
||||
|
||||
<TouchableOpacity
|
||||
style={[styles.submitBtn, { backgroundColor: cores.azul }]}
|
||||
onPress={handleCriar}
|
||||
disabled={loading}
|
||||
>
|
||||
{loading ? <ActivityIndicator color="#fff" /> : <Text style={styles.submitBtnText}>REGISTAR {tipo.toUpperCase()}</Text>}
|
||||
</TouchableOpacity>
|
||||
|
||||
</ScrollView>
|
||||
</KeyboardAvoidingView>
|
||||
</SafeAreaView>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
scroll: { padding: 24, paddingBottom: 60 },
|
||||
header: { flexDirection: 'row', alignItems: 'center', marginBottom: 25 },
|
||||
backBtn: { width: 45, height: 45, borderRadius: 12, borderWidth: 1, justifyContent: 'center', alignItems: 'center', marginRight: 15 },
|
||||
title: { fontSize: 24, fontWeight: 'bold' },
|
||||
subtitle: { fontSize: 12, fontWeight: '600', textTransform: 'uppercase' },
|
||||
sectionTitle: { fontSize: 13, fontWeight: 'bold', marginTop: 20, marginBottom: 10, opacity: 0.6 },
|
||||
selectorContainer: { flexDirection: 'row', gap: 10, marginBottom: 10 },
|
||||
selectorBtn: { flex: 1, height: 45, borderRadius: 10, borderWidth: 1, justifyContent: 'center', alignItems: 'center' },
|
||||
selectorText: { fontSize: 13, fontWeight: 'bold' },
|
||||
form: { gap: 12 },
|
||||
input: { height: 55, borderRadius: 15, borderWidth: 1, paddingHorizontal: 15, fontSize: 16 },
|
||||
submitBtn: { height: 60, borderRadius: 15, marginTop: 30, justifyContent: 'center', alignItems: 'center' },
|
||||
submitBtnText: { color: '#fff', fontSize: 16, fontWeight: 'bold' },
|
||||
});
|
||||
|
||||
export default CriarAluno;
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
ActivityIndicator,
|
||||
Alert,
|
||||
FlatList,
|
||||
Modal,
|
||||
RefreshControl,
|
||||
StatusBar,
|
||||
StyleSheet,
|
||||
@@ -42,6 +43,11 @@ const ListaAlunosProfessor = memo(() => {
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [refreshing, setRefreshing] = useState(false);
|
||||
|
||||
// Estados para o Modal de Eliminação
|
||||
const [showDeleteModal, setShowDeleteModal] = useState(false);
|
||||
const [alunoParaEliminar, setAlunoParaEliminar] = useState<Aluno | null>(null);
|
||||
const [isDeleting, setIsDeleting] = useState(false);
|
||||
|
||||
const azulEPVC = '#2390a6';
|
||||
const laranjaEPVC = '#E38E00';
|
||||
|
||||
@@ -55,6 +61,8 @@ const ListaAlunosProfessor = memo(() => {
|
||||
azulSuave: isDarkMode ? 'rgba(35, 144, 166, 0.12)' : '#F0F9FA',
|
||||
borda: isDarkMode ? '#2D2D2D' : '#E2E8F0',
|
||||
verde: '#10B981',
|
||||
vermelho: '#EF4444',
|
||||
vermelhoSuave: isDarkMode ? 'rgba(239, 68, 68, 0.15)' : '#FEE2E2',
|
||||
}), [isDarkMode]);
|
||||
|
||||
const fetchAlunos = async () => {
|
||||
@@ -62,22 +70,14 @@ const ListaAlunosProfessor = memo(() => {
|
||||
setLoading(true);
|
||||
const { data, error } = await supabase
|
||||
.from('alunos')
|
||||
.select(`
|
||||
id,
|
||||
nome,
|
||||
n_escola,
|
||||
ano,
|
||||
turma_curso,
|
||||
estagios(id)
|
||||
`)
|
||||
.select(`id, nome, n_escola, ano, turma_curso, estagios(id)`)
|
||||
.order('ano', { ascending: false })
|
||||
.order('nome', { ascending: true });
|
||||
|
||||
if (error) throw error;
|
||||
if (!data) return setTurmas([]);
|
||||
|
||||
|
||||
const agrupadas: Record<string, Aluno[]> = {};
|
||||
data.forEach(item => {
|
||||
data?.forEach(item => {
|
||||
const nomeTurma = `${item.ano}º ${item.turma_curso}`.trim().toUpperCase();
|
||||
if (!agrupadas[nomeTurma]) agrupadas[nomeTurma] = [];
|
||||
agrupadas[nomeTurma].push({
|
||||
@@ -89,18 +89,32 @@ const ListaAlunosProfessor = memo(() => {
|
||||
});
|
||||
});
|
||||
|
||||
setTurmas(Object.keys(agrupadas)
|
||||
.sort((a, b) => b.localeCompare(a))
|
||||
.map(nome => ({ nome, alunos: agrupadas[nome] }))
|
||||
);
|
||||
setTurmas(Object.keys(agrupadas).sort((a, b) => b.localeCompare(a)).map(nome => ({ nome, alunos: agrupadas[nome] })));
|
||||
} catch (err) {
|
||||
console.error('Erro:', err);
|
||||
console.error(err);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
setRefreshing(false);
|
||||
}
|
||||
};
|
||||
|
||||
const confirmarEliminacao = async () => {
|
||||
if (!alunoParaEliminar) return;
|
||||
try {
|
||||
setIsDeleting(true);
|
||||
const { error } = await supabase.from('alunos').delete().eq('id', alunoParaEliminar.id);
|
||||
if (error) throw error;
|
||||
|
||||
setShowDeleteModal(false);
|
||||
setAlunoParaEliminar(null);
|
||||
fetchAlunos();
|
||||
} catch (err) {
|
||||
Alert.alert("Erro", "Não foi possível eliminar o aluno.");
|
||||
} finally {
|
||||
setIsDeleting(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => { fetchAlunos(); }, []);
|
||||
|
||||
const onRefresh = useCallback(() => {
|
||||
@@ -113,8 +127,7 @@ const ListaAlunosProfessor = memo(() => {
|
||||
.map(turma => ({
|
||||
...turma,
|
||||
alunos: turma.alunos.filter(a =>
|
||||
a.nome.toLowerCase().includes(search.toLowerCase()) ||
|
||||
a.n_escola.includes(search)
|
||||
a.nome.toLowerCase().includes(search.toLowerCase()) || a.n_escola.includes(search)
|
||||
),
|
||||
}))
|
||||
.filter(t => t.alunos.length > 0);
|
||||
@@ -123,38 +136,29 @@ const ListaAlunosProfessor = memo(() => {
|
||||
return (
|
||||
<View style={{ flex: 1, backgroundColor: cores.fundo }}>
|
||||
<StatusBar barStyle={isDarkMode ? 'light-content' : 'dark-content'} />
|
||||
|
||||
<SafeAreaView style={styles.safe} edges={['top']}>
|
||||
|
||||
{/* HEADER EPVC STYLE */}
|
||||
{/* HEADER */}
|
||||
<View style={styles.header}>
|
||||
<TouchableOpacity
|
||||
style={[styles.btnAction, { borderColor: cores.borda }]}
|
||||
onPress={() => router.back()}
|
||||
>
|
||||
<TouchableOpacity style={[styles.btnAction, { borderColor: cores.borda }]} onPress={() => router.back()}>
|
||||
<Ionicons name="chevron-back" size={24} color={cores.azul} />
|
||||
</TouchableOpacity>
|
||||
|
||||
<View style={{ alignItems: 'center' }}>
|
||||
<Text style={[styles.headerTitle, { color: cores.texto }]}>Alunos</Text>
|
||||
<Text style={[styles.headerSubtitle, { color: cores.laranja }]}>Gestão de Turmas</Text>
|
||||
</View>
|
||||
|
||||
<TouchableOpacity
|
||||
style={[styles.btnAction, { borderColor: cores.borda }]}
|
||||
onPress={fetchAlunos}
|
||||
>
|
||||
<TouchableOpacity style={[styles.btnAction, { borderColor: cores.borda }]} onPress={fetchAlunos}>
|
||||
<Ionicons name="reload-outline" size={20} color={cores.azul} />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
||||
{/* SEARCH MODERNO */}
|
||||
{/* SEARCH */}
|
||||
<View style={styles.searchSection}>
|
||||
<View style={[styles.searchBar, { backgroundColor: cores.card, borderColor: cores.borda }]}>
|
||||
<Ionicons name="search-outline" size={20} color={cores.azul} />
|
||||
<TextInput
|
||||
style={[styles.searchInput, { color: cores.texto }]}
|
||||
placeholder="Pesquisar por aluno ou nº..."
|
||||
placeholder="Pesquisar por aluno..."
|
||||
placeholderTextColor={cores.secundario}
|
||||
value={search}
|
||||
onChangeText={setSearch}
|
||||
@@ -177,59 +181,73 @@ const ListaAlunosProfessor = memo(() => {
|
||||
<Text style={[styles.sectionTitle, { color: cores.texto }]}>{item.nome}</Text>
|
||||
<View style={[styles.sectionLine, { backgroundColor: cores.borda }]} />
|
||||
</View>
|
||||
|
||||
{item.alunos.map((aluno) => (
|
||||
<TouchableOpacity
|
||||
key={aluno.id}
|
||||
activeOpacity={0.8}
|
||||
style={[styles.alunoCard, { backgroundColor: cores.card, borderColor: cores.borda }]}
|
||||
onPress={() => router.push({
|
||||
pathname: '/Professor/Alunos/DetalhesAluno',
|
||||
params: { alunoId: aluno.id }
|
||||
})}
|
||||
onPress={() => router.push({ pathname: '/Professor/Alunos/DetalhesAluno', params: { alunoId: aluno.id } })}
|
||||
onLongPress={() => {
|
||||
setAlunoParaEliminar(aluno);
|
||||
setShowDeleteModal(true);
|
||||
}}
|
||||
>
|
||||
<View style={[styles.avatar, { backgroundColor: cores.azulSuave }]}>
|
||||
<Text style={[styles.avatarText, { color: cores.azul }]}>
|
||||
{aluno.nome.charAt(0)}
|
||||
</Text>
|
||||
<Text style={[styles.avatarText, { color: cores.azul }]}>{aluno.nome.charAt(0).toUpperCase()}</Text>
|
||||
</View>
|
||||
|
||||
<View style={styles.alunoInfo}>
|
||||
<Text style={[styles.alunoNome, { color: cores.texto }]}>{aluno.nome}</Text>
|
||||
<View style={styles.idRow}>
|
||||
<Ionicons name="finger-print-outline" size={13} color={cores.secundario} />
|
||||
<Text style={[styles.idText, { color: cores.secundario }]}>Nº {aluno.n_escola}</Text>
|
||||
</View>
|
||||
<Text style={[styles.idText, { color: cores.secundario }]}>Nº {aluno.n_escola}</Text>
|
||||
</View>
|
||||
|
||||
{aluno.tem_estagio ? (
|
||||
<View style={[styles.statusBadge, { backgroundColor: cores.verde + '20' }]}>
|
||||
<Ionicons name="checkmark-circle" size={12} color={cores.verde} />
|
||||
<Text style={[styles.statusText, { color: cores.verde }]}>COLOCADO</Text>
|
||||
</View>
|
||||
) : (
|
||||
<View style={[styles.statusBadge, { backgroundColor: cores.laranja + '20' }]}>
|
||||
<Ionicons name="alert-circle" size={12} color={cores.laranja} />
|
||||
<Text style={[styles.statusText, { color: cores.laranja }]}>PENDENTE</Text>
|
||||
</View>
|
||||
)}
|
||||
<Ionicons name="ellipsis-vertical" size={18} color={cores.borda} />
|
||||
</TouchableOpacity>
|
||||
))}
|
||||
</View>
|
||||
)}
|
||||
ListEmptyComponent={() => (
|
||||
<View style={styles.emptyContainer}>
|
||||
<Ionicons name="people-outline" size={60} color={cores.borda} />
|
||||
<Text style={{ color: cores.secundario, marginTop: 10, fontWeight: '700' }}>Nenhum aluno encontrado.</Text>
|
||||
</View>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* FAB */}
|
||||
{/* MODAL DE ELIMINAÇÃO CUSTOMIZADO */}
|
||||
<Modal visible={showDeleteModal} transparent animationType="fade">
|
||||
<View style={styles.modalOverlay}>
|
||||
<View style={[styles.modalCard, { backgroundColor: cores.fundo }]}>
|
||||
<View style={[styles.warningIconBox, { backgroundColor: cores.vermelhoSuave }]}>
|
||||
<Ionicons name="trash-outline" size={32} color={cores.vermelho} />
|
||||
</View>
|
||||
|
||||
<Text style={[styles.modalTitle, { color: cores.texto }]}>Eliminar Aluno?</Text>
|
||||
<Text style={[styles.modalDesc, { color: cores.secundario }]}>
|
||||
Estás prestes a apagar <Text style={{fontWeight:'800', color: cores.texto}}>{alunoParaEliminar?.nome}</Text>.
|
||||
Esta ação é irreversível e **vai dar merda** se não tiveres a certeza!
|
||||
</Text>
|
||||
|
||||
<View style={styles.modalButtons}>
|
||||
<TouchableOpacity
|
||||
style={[styles.btnModal, { borderColor: cores.borda, borderWidth: 1 }]}
|
||||
onPress={() => setShowDeleteModal(false)}
|
||||
>
|
||||
<Text style={[styles.btnText, { color: cores.secundario }]}>Cancelar</Text>
|
||||
</TouchableOpacity>
|
||||
|
||||
<TouchableOpacity
|
||||
style={[styles.btnModal, { backgroundColor: cores.vermelho }]}
|
||||
onPress={confirmarEliminacao}
|
||||
disabled={isDeleting}
|
||||
>
|
||||
{isDeleting ? (
|
||||
<ActivityIndicator color="#fff" size="small" />
|
||||
) : (
|
||||
<Text style={[styles.btnText, { color: '#fff' }]}>Eliminar</Text>
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</Modal>
|
||||
|
||||
<TouchableOpacity
|
||||
style={[styles.fab, { backgroundColor: cores.azul, bottom: insets.bottom + 20 }]}
|
||||
onPress={() => Alert.alert("EPVC", "Funcionalidade de registo de aluno em desenvolvimento.")}
|
||||
onPress={() => router.push('/Professor/Alunos/CriarAluno')} // Altera esta linha
|
||||
>
|
||||
<Ionicons name="person-add" size={24} color="#fff" />
|
||||
<Text style={styles.fabText}>Novo Aluno</Text>
|
||||
@@ -242,29 +260,34 @@ const ListaAlunosProfessor = memo(() => {
|
||||
const styles = StyleSheet.create({
|
||||
safe: { flex: 1 },
|
||||
header: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', paddingHorizontal: 24, paddingVertical: 15 },
|
||||
headerTitle: { fontSize: 22, fontWeight: '900', letterSpacing: -0.5 },
|
||||
headerSubtitle: { fontSize: 11, fontWeight: '800', textTransform: 'uppercase', letterSpacing: 0.5 },
|
||||
headerTitle: { fontSize: 22, fontWeight: '900' },
|
||||
headerSubtitle: { fontSize: 11, fontWeight: '800', textTransform: 'uppercase' },
|
||||
btnAction: { width: 44, height: 44, borderRadius: 14, justifyContent: 'center', alignItems: 'center', borderWidth: 1 },
|
||||
searchSection: { paddingHorizontal: 24, marginBottom: 10 },
|
||||
searchBar: { flexDirection: 'row', alignItems: 'center', paddingHorizontal: 16, height: 56, borderRadius: 20, borderWidth: 1.5 },
|
||||
searchInput: { flex: 1, marginLeft: 12, fontSize: 14, fontWeight: '700' },
|
||||
listPadding: { paddingHorizontal: 24, paddingTop: 10 },
|
||||
sectionHeader: { flexDirection: 'row', alignItems: 'center', marginTop: 10, marginBottom: 18 },
|
||||
sectionHeader: { flexDirection: 'row', alignItems: 'center', marginBottom: 18 },
|
||||
sectionDot: { width: 8, height: 8, borderRadius: 4, marginRight: 10 },
|
||||
sectionTitle: { fontSize: 13, fontWeight: '900', textTransform: 'uppercase', letterSpacing: 0.8 },
|
||||
sectionTitle: { fontSize: 13, fontWeight: '900', textTransform: 'uppercase' },
|
||||
sectionLine: { flex: 1, height: 1, marginLeft: 15, opacity: 0.5 },
|
||||
alunoCard: { flexDirection: 'row', alignItems: 'center', padding: 16, borderRadius: 28, marginBottom: 12, borderWidth: 1, elevation: 3, shadowColor: '#000', shadowOpacity: 0.04, shadowRadius: 10 },
|
||||
alunoCard: { flexDirection: 'row', alignItems: 'center', padding: 16, borderRadius: 28, marginBottom: 12, borderWidth: 1 },
|
||||
avatar: { width: 48, height: 48, borderRadius: 16, justifyContent: 'center', alignItems: 'center' },
|
||||
avatarText: { fontSize: 18, fontWeight: '900' },
|
||||
alunoInfo: { flex: 1, marginLeft: 15 },
|
||||
alunoNome: { fontSize: 16, fontWeight: '800', letterSpacing: -0.3 },
|
||||
idRow: { flexDirection: 'row', alignItems: 'center', gap: 5, marginTop: 3 },
|
||||
alunoNome: { fontSize: 16, fontWeight: '800' },
|
||||
idText: { fontSize: 13, fontWeight: '600' },
|
||||
statusBadge: { flexDirection: 'row', alignItems: 'center', gap: 4, paddingHorizontal: 8, paddingVertical: 4, borderRadius: 10 },
|
||||
statusText: { fontSize: 9, fontWeight: '900' },
|
||||
emptyContainer: { marginTop: 80, alignItems: 'center' },
|
||||
fab: { position: 'absolute', right: 24, flexDirection: 'row', alignItems: 'center', paddingHorizontal: 22, paddingVertical: 16, borderRadius: 22, elevation: 8, shadowColor: '#2390a6', shadowOpacity: 0.3, shadowRadius: 10 },
|
||||
fabText: { color: '#fff', fontSize: 15, fontWeight: '900', marginLeft: 10, textTransform: 'uppercase' },
|
||||
fab: { position: 'absolute', right: 24, flexDirection: 'row', alignItems: 'center', paddingHorizontal: 22, paddingVertical: 16, borderRadius: 22 },
|
||||
fabText: { color: '#fff', fontSize: 15, fontWeight: '900', marginLeft: 10 },
|
||||
// Estilos do Modal
|
||||
modalOverlay: { flex: 1, backgroundColor: 'rgba(0,0,0,0.7)', justifyContent: 'center', alignItems: 'center', padding: 30 },
|
||||
modalCard: { width: '100%', borderRadius: 32, padding: 24, alignItems: 'center' },
|
||||
warningIconBox: { width: 70, height: 70, borderRadius: 25, justifyContent: 'center', alignItems: 'center', marginBottom: 20 },
|
||||
modalTitle: { fontSize: 22, fontWeight: '900', marginBottom: 10 },
|
||||
modalDesc: { fontSize: 15, textAlign: 'center', lineHeight: 22, marginBottom: 25 },
|
||||
modalButtons: { flexDirection: 'row', gap: 12, width: '100%' },
|
||||
btnModal: { flex: 1, height: 56, borderRadius: 18, justifyContent: 'center', alignItems: 'center' },
|
||||
btnText: { fontSize: 16, fontWeight: '800' }
|
||||
});
|
||||
|
||||
export default ListaAlunosProfessor;
|
||||
Reference in New Issue
Block a user