story-generator/src/pages/dashboard/classes/ClassesPage.tsx
Lucas Santana 021ef87203 style: padroniza layout das páginas de Classes e Professores
- Alinha o visual das páginas com o padrão do StudentsPage
- Ajusta espaçamentos, cores e tipografia
- Melhora a consistência dos componentes de lista
- Adiciona tratamento de erros uniforme
- Padroniza os estados de loading e empty
2024-12-20 10:49:03 -03:00

121 lines
4.3 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';
import type { Class } from '../../../types/database';
export function ClassesPage() {
const navigate = useNavigate();
const [classes, setClasses] = React.useState<Class[]>([]);
const [searchTerm, setSearchTerm] = React.useState('');
const [loading, setLoading] = React.useState(true);
const [error, setError] = React.useState<string | null>(null);
React.useEffect(() => {
const fetchClasses = async () => {
try {
const { data: { session } } = await supabase.auth.getSession();
if (!session?.user?.id) return;
const { data, error } = await supabase
.from('classes')
.select(`
*,
students:students(count),
teachers:teacher_classes(count)
`)
.eq('school_id', session.user.id);
if (error) throw error;
setClasses(data || []);
} catch (err) {
console.error('Erro ao buscar turmas:', err);
setError('Erro ao buscar turmas');
} finally {
setLoading(false);
}
};
fetchClasses();
}, []);
const filteredClasses = classes.filter(classItem =>
classItem.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
classItem.grade.toLowerCase().includes(searchTerm.toLowerCase())
);
return (
<div>
<div className="flex justify-between items-center mb-6">
<h1 className="text-2xl font-bold text-gray-900">Turmas</h1>
<button
onClick={() => navigate('nova')}
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 Turma
</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 turmas..."
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>
) : filteredClasses.length === 0 ? (
<div className="p-8 text-center text-gray-500">
{searchTerm ? 'Nenhuma turma encontrada' : 'Nenhuma turma cadastrada'}
</div>
) : (
<div className="divide-y divide-gray-200">
{filteredClasses.map((classItem) => (
<div
key={classItem.id}
className="p-4 hover:bg-gray-50 cursor-pointer"
onClick={() => navigate(`/dashboard/turmas/${classItem.id}`)}
>
<div className="flex justify-between items-center">
<div>
<h3 className="text-lg font-medium text-gray-900">
{classItem.name}
</h3>
<div className="flex items-center gap-2 text-sm text-gray-500">
<GraduationCap className="h-4 w-4" />
{classItem.grade} {(classItem as any).students?.count || 0} alunos
</div>
</div>
<div className="flex items-center gap-6">
<span className="px-2 py-1 rounded-full text-xs font-medium bg-green-100 text-green-800">
Ativo
</span>
<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>
);
}