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 { InviteTeacherPage } from './pages/dashboard/teachers/InviteTeacherPage';
|
||||||
import { StudentsPage } from './pages/dashboard/students/StudentsPage';
|
import { StudentsPage } from './pages/dashboard/students/StudentsPage';
|
||||||
import { AddStudentPage } from './pages/dashboard/students/AddStudentPage';
|
import { AddStudentPage } from './pages/dashboard/students/AddStudentPage';
|
||||||
|
import { SettingsPage } from './pages/dashboard/settings/SettingsPage';
|
||||||
|
|
||||||
export const router = createBrowserRouter([
|
export const router = createBrowserRouter([
|
||||||
{
|
{
|
||||||
@ -100,6 +101,10 @@ export const router = createBrowserRouter([
|
|||||||
element: <AddStudentPage />,
|
element: <AddStudentPage />,
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'configuracoes',
|
||||||
|
element: <SettingsPage />
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user