26.1.29
This commit is contained in:
@@ -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,
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user