mirror of
https://github.com/lucasrcsantana/story-generator.git
synced 2025-12-18 14:27:51 +00:00
175 lines
5.5 KiB
TypeScript
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>
|
|
);
|
|
}
|