story-generator/src/pages/dashboard/students/StudentsPage.tsx

175 lines
5.5 KiB
TypeScript

import React from 'react';
import { Plus, Search, MoreVertical, GraduationCap } from 'lucide-react';
import { useNavigate } from 'react-router-dom';
import { useDatabase } from '../../../hooks/useDatabase';
import { supabase } from '../../../lib/supabase';
interface StudentData {
id: string;
name: string;
email: string;
class_id: string;
school_id: string;
status: 'active' | 'inactive';
classes: {
name: string;
}[];
}
interface StudentWithDetails {
id: string;
name: string;
email: string;
class_name: string;
stories_count: number;
status: 'active' | 'inactive';
}
export function StudentsPage() {
const navigate = useNavigate();
const { loading, error } = useDatabase();
const [students, setStudents] = React.useState<StudentWithDetails[]>([]);
const [searchTerm, setSearchTerm] = React.useState('');
React.useEffect(() => {
const fetchStudents = async () => {
try {
const { data: authData } = await supabase.auth.getSession();
if (!authData.session?.user) return;
const { data: studentsData, error: studentsError } = await supabase
.from('students')
.select(`
id,
name,
email,
class_id,
status,
classes (
name
)
`);
if (studentsError) throw studentsError;
const studentsWithCounts = studentsData.map((student) => ({
id: student.id,
name: student.name,
email: student.email,
class_name: student.classes && student.classes[0] ? student.classes[0].name : 'Sem turma',
stories_count: 0,
status: student.status || 'active'
}));
setStudents(studentsWithCounts);
} catch (err) {
console.error('Erro ao buscar alunos:', err);
}
};
fetchStudents();
}, []);
const handleAddStudent = () => {
navigate('/dashboard/alunos/novo');
};
const handleStudentClick = (studentId: string) => {
navigate(`/dashboard/alunos/${studentId}`);
};
const getStatusColor = (status: StudentData['status']) => {
return status === 'active'
? 'bg-green-100 text-green-800'
: 'bg-gray-100 text-gray-800';
};
const getStatusText = (status: StudentData['status']) => {
return status === 'active' ? 'Ativo' : 'Inativo';
};
const filteredStudents = students.filter(s =>
s.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
s.class_name.toLowerCase().includes(searchTerm.toLowerCase())
);
return (
<div>
<div className="flex justify-between items-center mb-6">
<h1 className="text-2xl font-bold text-gray-900">Alunos</h1>
<button
onClick={handleAddStudent}
className="flex items-center gap-2 px-4 py-2 bg-purple-600 text-white rounded-lg hover:bg-purple-700 transition"
>
<Plus className="h-5 w-5" />
Adicionar Aluno
</button>
</div>
{error && (
<div className="mb-4 p-4 bg-red-50 text-red-600 rounded-lg">
{error}
</div>
)}
<div className="bg-white rounded-xl shadow-sm border border-gray-200 overflow-hidden">
<div className="p-4 border-b border-gray-200">
<div className="relative">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-5 w-5 text-gray-400" />
<input
type="text"
placeholder="Buscar alunos..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
className="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:ring-purple-500 focus:border-purple-500"
/>
</div>
</div>
{loading ? (
<div className="p-8 text-center text-gray-500">Carregando...</div>
) : filteredStudents.length === 0 ? (
<div className="p-8 text-center text-gray-500">
Nenhum aluno encontrado
</div>
) : (
<div className="divide-y divide-gray-200">
{filteredStudents.map((student) => (
<div
key={student.id}
className="p-4 hover:bg-gray-50 cursor-pointer"
onClick={() => handleStudentClick(student.id)}
>
<div className="flex justify-between items-center">
<div>
<h3 className="text-lg font-medium text-gray-900">
{student.name}
</h3>
<div className="flex items-center gap-2 text-sm text-gray-500">
<GraduationCap className="h-4 w-4" />
{student.class_name}
</div>
</div>
<div className="flex items-center gap-6">
<div className="text-sm text-gray-500">
<span className="font-medium text-gray-900">
{student.stories_count}
</span>{' '}
histórias
</div>
<div className={`px-2 py-1 rounded-full text-xs font-medium ${getStatusColor(student.status)}`}>
{getStatusText(student.status)}
</div>
<button className="p-2 hover:bg-gray-100 rounded-full">
<MoreVertical className="h-5 w-5 text-gray-400" />
</button>
</div>
</div>
</div>
))}
</div>
)}
</div>
</div>
);
}