This commit is contained in:
2026-01-29 10:42:04 +00:00
parent 1f2b41a9c3
commit 02478b4cee

View File

@@ -1,18 +1,31 @@
import { Ionicons } from '@expo/vector-icons';
import { useRouter } from 'expo-router';
import { memo, useState } from 'react';
import { FlatList, Platform, SafeAreaView, StatusBar, StyleSheet, Text, TextInput, TouchableOpacity, View } from 'react-native';
import { memo, useEffect, useState } from 'react';
import {
FlatList,
Platform,
SafeAreaView,
StatusBar,
StyleSheet,
Text,
TextInput,
TouchableOpacity,
View,
} from 'react-native';
import { useTheme } from '../../../themecontext';
import { supabase } from '../../lib/supabase';
/* =======================
Interfaces
======================= */
export interface Aluno {
id: number;
id: string;
nome: string;
estado: string;
email: string;
telefone: string;
estado: string; // virá da tabela estagios
turma: string;
empresa: string;
cargo: string;
}
interface Turma {
@@ -20,16 +33,18 @@ interface Turma {
alunos: Aluno[];
}
const turmasData: Turma[] = [
{ nome: '12ºA', alunos: [{ id: 1, nome: 'João Silva', estado: 'Em estágio', email: 'joao@escola.pt', telefone: '912345678', turma: '12ºA', empresa: 'Empresa X', cargo: 'Dev' }] },
{ nome: '12ºB', alunos: [{ id: 2, nome: 'Maria Fernandes', estado: 'Em estágio', email: 'maria@escola.pt', telefone: '912345681', turma: '12ºB', empresa: 'Empresa W', cargo: 'Design' }] },
];
/* =======================
Componente
======================= */
const ListaAlunosProfessor = memo(() => {
const { isDarkMode } = useTheme();
const router = useRouter();
const [search, setSearch] = useState('');
const [expanded, setExpanded] = useState<{ [key: string]: boolean }>({});
const [expanded, setExpanded] = useState<Record<string, boolean>>({});
const [turmas, setTurmas] = useState<Turma[]>([]);
const [loading, setLoading] = useState(true);
const cores = {
fundo: isDarkMode ? '#121212' : '#f1f3f5',
@@ -39,29 +54,105 @@ const ListaAlunosProfessor = memo(() => {
azul: '#0d6efd',
};
const toggleExpand = (turmaNome: string) => setExpanded(prev => ({ ...prev, [turmaNome]: !prev[turmaNome] }));
/* =======================
Fetch Supabase
======================= */
const filteredTurmas = turmasData
useEffect(() => {
fetchAlunos();
}, []);
const fetchAlunos = async () => {
setLoading(true);
const { data, error } = await supabase
.from('profiles')
.select(`
id,
nome,
email,
telefone,
turma_curso,
ano,
n_escola
`)
.order('ano', { ascending: false });
if (error) {
console.error('Erro ao buscar alunos:', error);
setLoading(false);
return;
}
const agrupadas: Record<string, Aluno[]> = {};
data.forEach((item: any) => {
const nomeTurma = `${item.ano} ${item.turma_curso}`;
if (!agrupadas[nomeTurma]) {
agrupadas[nomeTurma] = [];
}
agrupadas[nomeTurma].push({
id: item.id,
nome: item.nome,
email: item.email,
telefone: item.telefone ?? '—',
estado: 'Sem estágio',
turma: nomeTurma,
});
});
const turmasFormatadas: Turma[] = Object.keys(agrupadas).map(nome => ({
nome,
alunos: agrupadas[nome],
}));
setTurmas(turmasFormatadas);
setLoading(false);
};
const toggleExpand = (turmaNome: string) =>
setExpanded(prev => ({ ...prev, [turmaNome]: !prev[turmaNome] }));
/* =======================
Pesquisa
======================= */
const filteredTurmas = turmas
.map(turma => ({
...turma,
alunos: turma.alunos.filter(a => a.nome.toLowerCase().includes(search.toLowerCase())),
alunos: turma.alunos.filter(a =>
a.nome.toLowerCase().includes(search.toLowerCase())
),
}))
.filter(t => t.alunos.length > 0);
/* =======================
Render
======================= */
return (
<SafeAreaView style={[styles.safe, { backgroundColor: cores.fundo }]}>
<StatusBar barStyle={isDarkMode ? 'light-content' : 'dark-content'} />
{/* Header com botão voltar */}
{/* Header */}
<View style={styles.header}>
<TouchableOpacity style={[styles.btnVoltar, { backgroundColor: cores.card }]} onPress={() => router.back()}>
<TouchableOpacity
style={[styles.btnVoltar, { backgroundColor: cores.card }]}
onPress={() => router.back()}
>
<Ionicons name="arrow-back" size={24} color={cores.texto} />
</TouchableOpacity>
<Text style={[styles.tituloGeral, { color: cores.texto }]}>Alunos</Text>
<Text style={[styles.tituloGeral, { color: cores.texto }]}>
Alunos
</Text>
<View style={styles.spacer} />
</View>
{/* Input de pesquisa */}
{/* Pesquisa */}
<TextInput
style={[styles.search, { backgroundColor: cores.card, color: cores.texto }]}
placeholder="Pesquisar aluno..."
@@ -70,14 +161,23 @@ const ListaAlunosProfessor = memo(() => {
onChangeText={setSearch}
/>
{/* Lista de turmas e alunos */}
{loading && (
<Text style={{ textAlign: 'center', marginVertical: 20, color: cores.texto }}>
A carregar alunos...
</Text>
)}
{/* Lista */}
<FlatList
data={filteredTurmas}
keyExtractor={item => item.nome}
contentContainerStyle={{ paddingBottom: 20 }}
renderItem={({ item }) => (
<View style={[styles.card, { backgroundColor: cores.card }]}>
<TouchableOpacity onPress={() => toggleExpand(item.nome)}>
<Text style={[styles.turmaNome, { color: cores.azul }]}>{item.nome} ({item.alunos.length})</Text>
<Text style={[styles.turmaNome, { color: cores.azul }]}>
{item.nome} ({item.alunos.length})
</Text>
</TouchableOpacity>
{expanded[item.nome] && (
@@ -89,12 +189,21 @@ const ListaAlunosProfessor = memo(() => {
onPress={() =>
router.push({
pathname: '/Professor/Alunos/DetalhesAluno',
params: { aluno: JSON.stringify(aluno) }
params: { aluno: JSON.stringify(aluno) },
})
}
>
<Text style={[styles.alunoNome, { color: cores.texto }]}>{aluno.nome}</Text>
<Text style={[styles.alunoEstado, { color: cores.textoSecundario }]}>{aluno.estado}</Text>
<Text style={[styles.alunoNome, { color: cores.texto }]}>
{aluno.nome}
</Text>
<Text
style={[
styles.alunoEstado,
{ color: cores.textoSecundario },
]}
>
{aluno.estado}
</Text>
</TouchableOpacity>
))}
</View>
@@ -108,17 +217,66 @@ const ListaAlunosProfessor = memo(() => {
export default ListaAlunosProfessor;
/* =======================
Styles
======================= */
const styles = StyleSheet.create({
safe: { flex: 1, paddingTop: Platform.OS === 'android' ? StatusBar.currentHeight : 0 },
header: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', paddingHorizontal: 20, paddingVertical: 10 },
btnVoltar: { width: 40, height: 40, borderRadius: 20, justifyContent: 'center', alignItems: 'center', elevation: 2 },
tituloGeral: { fontSize: 22, fontWeight: 'bold' },
spacer: { width: 40 },
search: { borderRadius: 10, padding: 10, margin: 10 },
card: { borderRadius: 10, padding: 15, marginBottom: 10, elevation: 2 },
turmaNome: { fontSize: 18, fontWeight: 'bold' },
listaAlunos: { marginTop: 10, paddingLeft: 10 },
alunoItem: { paddingVertical: 8 },
alunoNome: { fontSize: 16, fontWeight: '600' },
alunoEstado: { fontSize: 14, marginTop: 2 },
safe: {
flex: 1,
paddingTop: Platform.OS === 'android' ? StatusBar.currentHeight : 0,
},
header: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
paddingHorizontal: 20,
paddingVertical: 10,
},
btnVoltar: {
width: 40,
height: 40,
borderRadius: 20,
justifyContent: 'center',
alignItems: 'center',
elevation: 2,
},
tituloGeral: {
fontSize: 22,
fontWeight: 'bold',
},
spacer: {
width: 40,
},
search: {
borderRadius: 10,
padding: 10,
margin: 10,
},
card: {
borderRadius: 10,
padding: 15,
marginHorizontal: 10,
marginBottom: 10,
elevation: 2,
},
turmaNome: {
fontSize: 18,
fontWeight: 'bold',
},
listaAlunos: {
marginTop: 10,
paddingLeft: 10,
},
alunoItem: {
paddingVertical: 8,
},
alunoNome: {
fontSize: 16,
fontWeight: '600',
},
alunoEstado: {
fontSize: 14,
marginTop: 2,
},
});