feat: adiciona página de configurações da escola

- Cria página de configurações com formulário para dados da escola
- Adiciona campos para informações básicas e endereço
- Implementa integração com Supabase para salvar dados
- Adiciona feedback visual de sucesso/erro
- Atualiza rotas e menu lateral com novo link
This commit is contained in:
Lucas Santana 2024-12-20 10:52:39 -03:00
parent e9e72677a4
commit f1a7cd8730
2 changed files with 291 additions and 0 deletions

View File

@ -0,0 +1,286 @@
import React from 'react';
import { Building2, Mail, Phone, MapPin, Save, User } from 'lucide-react';
import { supabase } from '../../../lib/supabase';
import type { School } from '../../../types/database';
interface SchoolSettings {
name: string;
email: string;
phone: string;
address: string;
city: string;
state: string;
zipCode: string;
directorName: string;
}
export function SettingsPage() {
const [settings, setSettings] = React.useState<SchoolSettings>({
name: '',
email: '',
phone: '',
address: '',
city: '',
state: '',
zipCode: '',
directorName: ''
});
const [loading, setLoading] = React.useState(true);
const [saving, setSaving] = React.useState(false);
const [error, setError] = React.useState<string | null>(null);
const [successMessage, setSuccessMessage] = React.useState<string | null>(null);
React.useEffect(() => {
const fetchSchoolSettings = async () => {
try {
const { data: { session } } = await supabase.auth.getSession();
if (!session?.user?.id) return;
const { data, error } = await supabase
.from('schools')
.select('*')
.eq('id', session.user.id)
.single();
if (error) throw error;
if (data) {
setSettings({
name: data.name || '',
email: data.email || '',
phone: data.phone || '',
address: data.address || '',
city: data.city || '',
state: data.state || '',
zipCode: data.zip_code || '',
directorName: data.director_name || ''
});
}
} catch (err) {
console.error('Erro ao carregar configurações:', err);
setError('Não foi possível carregar as configurações da escola');
} finally {
setLoading(false);
}
};
fetchSchoolSettings();
}, []);
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = e.target;
setSettings(prev => ({ ...prev, [name]: value }));
};
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setSaving(true);
setError(null);
setSuccessMessage(null);
try {
const { data: { session } } = await supabase.auth.getSession();
if (!session?.user?.id) throw new Error('Usuário não autenticado');
const { error } = await supabase
.from('schools')
.update({
name: settings.name,
email: settings.email,
phone: settings.phone,
address: settings.address,
city: settings.city,
state: settings.state,
zip_code: settings.zipCode,
director_name: settings.directorName,
updated_at: new Date().toISOString()
})
.eq('id', session.user.id);
if (error) throw error;
setSuccessMessage('Configurações atualizadas com sucesso!');
} catch (err) {
console.error('Erro ao salvar configurações:', err);
setError('Não foi possível salvar as configurações');
} finally {
setSaving(false);
}
};
if (loading) {
return <div className="p-8 text-center text-gray-500">Carregando...</div>;
}
return (
<div>
<div className="flex justify-between items-center mb-6">
<h1 className="text-2xl font-bold text-gray-900">Configurações da Escola</h1>
</div>
<div className="bg-white rounded-xl shadow-sm border border-gray-200 overflow-hidden">
<form onSubmit={handleSubmit} className="p-6 space-y-6">
{error && (
<div className="p-4 bg-red-50 text-red-600 rounded-lg">
{error}
</div>
)}
{successMessage && (
<div className="p-4 bg-green-50 text-green-600 rounded-lg">
{successMessage}
</div>
)}
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label htmlFor="name" className="block text-sm font-medium text-gray-700 mb-1">
Nome da Escola
</label>
<div className="relative">
<Building2 className="absolute left-3 top-1/2 -translate-y-1/2 h-5 w-5 text-gray-400" />
<input
type="text"
id="name"
name="name"
value={settings.name}
onChange={handleChange}
className="pl-10 w-full rounded-lg border-gray-300 focus:ring-purple-500 focus:border-purple-500"
required
/>
</div>
</div>
<div>
<label htmlFor="email" className="block text-sm font-medium text-gray-700 mb-1">
Email
</label>
<div className="relative">
<Mail className="absolute left-3 top-1/2 -translate-y-1/2 h-5 w-5 text-gray-400" />
<input
type="email"
id="email"
name="email"
value={settings.email}
onChange={handleChange}
className="pl-10 w-full rounded-lg border-gray-300 focus:ring-purple-500 focus:border-purple-500"
required
/>
</div>
</div>
<div>
<label htmlFor="phone" className="block text-sm font-medium text-gray-700 mb-1">
Telefone
</label>
<div className="relative">
<Phone className="absolute left-3 top-1/2 -translate-y-1/2 h-5 w-5 text-gray-400" />
<input
type="tel"
id="phone"
name="phone"
value={settings.phone}
onChange={handleChange}
className="pl-10 w-full rounded-lg border-gray-300 focus:ring-purple-500 focus:border-purple-500"
/>
</div>
</div>
<div>
<label htmlFor="directorName" className="block text-sm font-medium text-gray-700 mb-1">
Nome do Diretor(a)
</label>
<div className="relative">
<User className="absolute left-3 top-1/2 -translate-y-1/2 h-5 w-5 text-gray-400" />
<input
type="text"
id="directorName"
name="directorName"
value={settings.directorName}
onChange={handleChange}
className="pl-10 w-full rounded-lg border-gray-300 focus:ring-purple-500 focus:border-purple-500"
/>
</div>
</div>
</div>
<div className="border-t border-gray-200 pt-6">
<h2 className="text-lg font-medium text-gray-900 mb-4">Endereço</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label htmlFor="address" className="block text-sm font-medium text-gray-700 mb-1">
Endereço
</label>
<div className="relative">
<MapPin className="absolute left-3 top-1/2 -translate-y-1/2 h-5 w-5 text-gray-400" />
<input
type="text"
id="address"
name="address"
value={settings.address}
onChange={handleChange}
className="pl-10 w-full rounded-lg border-gray-300 focus:ring-purple-500 focus:border-purple-500"
/>
</div>
</div>
<div className="grid grid-cols-2 gap-4">
<div>
<label htmlFor="city" className="block text-sm font-medium text-gray-700 mb-1">
Cidade
</label>
<input
type="text"
id="city"
name="city"
value={settings.city}
onChange={handleChange}
className="w-full rounded-lg border-gray-300 focus:ring-purple-500 focus:border-purple-500"
/>
</div>
<div>
<label htmlFor="state" className="block text-sm font-medium text-gray-700 mb-1">
Estado
</label>
<input
type="text"
id="state"
name="state"
value={settings.state}
onChange={handleChange}
className="w-full rounded-lg border-gray-300 focus:ring-purple-500 focus:border-purple-500"
/>
</div>
</div>
<div>
<label htmlFor="zipCode" className="block text-sm font-medium text-gray-700 mb-1">
CEP
</label>
<input
type="text"
id="zipCode"
name="zipCode"
value={settings.zipCode}
onChange={handleChange}
className="w-full rounded-lg border-gray-300 focus:ring-purple-500 focus:border-purple-500"
/>
</div>
</div>
</div>
<div className="flex justify-end pt-6">
<button
type="submit"
disabled={saving}
className="flex items-center gap-2 px-6 py-2 bg-purple-600 text-white rounded-lg hover:bg-purple-700 transition disabled:opacity-50"
>
<Save className="h-5 w-5" />
{saving ? 'Salvando...' : 'Salvar Alterações'}
</button>
</div>
</form>
</div>
</div>
);
}

View File

@ -13,6 +13,7 @@ import { TeachersPage } from './pages/dashboard/teachers/TeachersPage';
import { InviteTeacherPage } from './pages/dashboard/teachers/InviteTeacherPage';
import { StudentsPage } from './pages/dashboard/students/StudentsPage';
import { AddStudentPage } from './pages/dashboard/students/AddStudentPage';
import { SettingsPage } from './pages/dashboard/settings/SettingsPage';
export const router = createBrowserRouter([
{
@ -100,6 +101,10 @@ export const router = createBrowserRouter([
element: <AddStudentPage />,
}
]
},
{
path: 'configuracoes',
element: <SettingsPage />
}
]
},