mirror of
https://github.com/lucasrcsantana/story-generator.git
synced 2025-12-17 05:47:52 +00:00
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:
parent
e9e72677a4
commit
f1a7cd8730
286
src/pages/dashboard/settings/SettingsPage.tsx
Normal file
286
src/pages/dashboard/settings/SettingsPage.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
@ -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 />
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
Loading…
Reference in New Issue
Block a user