diff --git a/index.html b/index.html index d0ca0c1..25b0e88 100644 --- a/index.html +++ b/index.html @@ -100,8 +100,10 @@ } from 'lucide-react'; import { app } from './firebase.js'; import { getAuth, signInWithEmailAndPassword } from 'https://www.gstatic.com/firebasejs/12.1.0/firebase-auth.js'; + import { getDatabase, ref, push, set, onValue, remove } from 'https://www.gstatic.com/firebasejs/12.1.0/firebase-database.js'; const auth = getAuth(app); + const db = getDatabase(app); const INITIAL_RESIDENTS = [ { id: 1, unit: '1º Esq', name: 'Ana Silva', contact: '912 345 678', email: 'ana.silva@email.com', status: 'Pago', pending: 0, role: 'morador' }, @@ -383,10 +385,36 @@ } }; - const [residents, setResidents] = useState(INITIAL_RESIDENTS); - const [finances, setFinances] = useState(INITIAL_FINANCES); - const [issues, setIssues] = useState(INITIAL_ISSUES); - const [bookings, setBookings] = useState(INITIAL_BOOKINGS); + const [residents, setResidents] = useState([]); + const [finances, setFinances] = useState([]); + const [issues, setIssues] = useState([]); + const [bookings, setBookings] = useState([]); + const [invoices, setInvoices] = useState([]); + + useEffect(() => { + const loadData = (path, setter, sortFunc = null) => { + return onValue(ref(db, path), (snapshot) => { + const data = snapshot.val(); + if (data) { + let parsed = Object.entries(data).map(([id, val]) => ({ id, ...val })); + if (sortFunc) parsed = parsed.sort(sortFunc); + setter(parsed); + } else { + setter([]); + } + }, (error) => console.error(`Erro ao carregar ${path}:`, error)); + }; + + const unsubResidents = loadData('condominos', setResidents); + const unsubFinances = loadData('financas', setFinances, (a,b) => new Date(b.date) - new Date(a.date)); + const unsubIssues = loadData('manutencao', setIssues, (a,b) => new Date(b.date) - new Date(a.date)); + const unsubBookings = loadData('reservas', setBookings, (a,b) => new Date(a.date) - new Date(b.date)); + const unsubInvoices = loadData('faturacao', setInvoices, (a,b) => new Date(b.date) - new Date(a.date)); + + return () => { + unsubResidents(); unsubFinances(); unsubIssues(); unsubBookings(); unsubInvoices(); + }; + }, []); const [notificationsList, setNotificationsList] = useState(INITIAL_NOTIFICATIONS); const [isNotificationsOpen, setNotificationsOpen] = useState(false); @@ -497,78 +525,174 @@ }); }; - const handleToggleRole = (id) => { - setResidents(residents.map(r => r.id === id ? { ...r, role: r.role === 'admin' ? 'morador' : 'admin' } : r)); - showNotification('Permissões de utilizador atualizadas', 'success'); - }; - - const handleSaveResident = (e) => { - e.preventDefault(); - if (editingItem) { - setResidents(residents.map(r => r.id === editingItem.id ? { ...formData, id: r.id } : r)); - showNotification(`Condómino ${formData.name} atualizado`); - } else { - setResidents([...residents, { ...formData, id: Date.now(), pending: Number(formData.pending) }]); - showNotification(`Novo condómino ${formData.name} adicionado`); + const handleToggleRole = async (id) => { + try { + const resident = residents.find(r => r.id === id); + if (resident) { + const newRole = resident.role === 'admin' ? 'morador' : 'admin'; + const residentRef = ref(db, `condominos/${id}`); + await set(residentRef, { ...resident, role: newRole }); + showNotification('Permissões de utilizador atualizadas', 'success'); + } + } catch (error) { + console.error("Erro ao atualizar permissão:", error); + showNotification("Erro ao atualizar permissão.", "error"); } - handleCloseModal(); }; - const handleDeleteResident = (id) => { + const handleSaveResident = async (e) => { + e.preventDefault(); + try { + if (editingItem) { + const residentRef = ref(db, `condominos/${editingItem.id}`); + await set(residentRef, { + unit: formData.unit || '', + name: formData.name || '', + contact: formData.contact || '', + email: formData.email || '', + status: formData.status || 'Pago', + pending: Number(formData.pending) || 0, + role: formData.role || 'morador' + }); + showNotification(`Condómino ${formData.name} atualizado`); + } else { + const residentsListRef = ref(db, 'condominos'); + const newResidentRef = push(residentsListRef); + await set(newResidentRef, { + unit: formData.unit || '', + name: formData.name || '', + contact: formData.contact || '', + email: formData.email || '', + status: formData.status || 'Pago', + pending: Number(formData.pending) || 0, + role: formData.role || 'morador' + }); + showNotification(`Novo condómino ${formData.name} adicionado`); + } + handleCloseModal(); + } catch (error) { + console.error("Erro ao guardar no Firebase:", error); + showNotification("Erro ao guardar os dados.", "error"); + } + }; + + const handleDeleteResident = async (id) => { if (window.confirm('Tem a certeza que deseja eliminar este condómino?')) { - setResidents(residents.filter(r => r.id !== id)); - showNotification('Condómino removido', 'error'); + try { + const residentRef = ref(db, `condominos/${id}`); + await remove(residentRef); + showNotification('Condómino removido', 'error'); + } catch (error) { + console.error("Erro ao eliminar no Firebase:", error); + showNotification("Erro ao eliminar.", "error"); + } } }; - const handleSaveFinance = (e) => { + const handleSaveFinance = async (e) => { e.preventDefault(); - const amount = Number(formData.amount); - const newTransaction = { ...formData, id: Date.now(), amount }; - setFinances([newTransaction, ...finances]); - showNotification(`Movimento de ${amount}€ registado`); - handleCloseModal(); + if (!formData.amount || !formData.category || !formData.date) { + showNotification("Preencha todos os campos obrigatórios.", "error"); + return; + } + try { + const amount = Number(formData.amount); + const newFinanceRef = push(ref(db, 'financas')); + await set(newFinanceRef, { ...formData, amount }); + showNotification(`Movimento de ${amount}€ registado`); + handleCloseModal(); + } catch (error) { + console.error("Erro ao guardar finanças:", error); + showNotification("Erro ao guardar movimento.", "error"); + } }; - const handleSaveIssue = (e) => { + const handleSaveIssue = async (e) => { e.preventDefault(); - const newIssue = { ...formData, id: Date.now() }; - setIssues([newIssue, ...issues]); - showNotification('Nova ocorrência reportada', 'warning'); - handleCloseModal(); + if (!formData.title || !formData.location) { + showNotification("Preencha todos os campos obrigatórios.", "error"); + return; + } + try { + const newIssueRef = push(ref(db, 'manutencao')); + await set(newIssueRef, { ...formData }); + showNotification('Nova ocorrência reportada', 'warning'); + handleCloseModal(); + } catch (error) { + console.error("Erro ao reportar ocorrência:", error); + showNotification("Erro ao reportar ocorrência.", "error"); + } }; - const handleResolveIssue = (id) => { - setIssues(issues.map(i => i.id === id ? { ...i, status: 'Resolvido' } : i)); - showNotification('Ocorrência resolvida com sucesso'); + const handleResolveIssue = async (id) => { + try { + const issue = issues.find(i => i.id === id); + if (issue) { + await set(ref(db, `manutencao/${id}`), { ...issue, status: 'Resolvido' }); + showNotification('Ocorrência resolvida com sucesso'); + } + } catch (error) { + console.error("Erro ao resolver ocorrência:", error); + showNotification("Erro ao resolver ocorrência.", "error"); + } }; - const handleSaveBooking = (e) => { + const handleSaveBooking = async (e) => { e.preventDefault(); - const facilityNames = { 'gym': 'Ginásio', 'hall': 'Salão de Festas', 'park': 'Parque de Jogos' }; - const newBooking = { - ...formData, - id: Date.now(), - facilityName: facilityNames[formData.facility], - status: 'Confirmado' - }; - setBookings([newBooking, ...bookings]); - - if (newBooking.cost > 0) { - const newIncome = { - id: Date.now() + 1, - type: 'income', - category: `Reserva: ${newBooking.facilityName}`, - date: newBooking.date, - amount: newBooking.cost, - desc: `Reserva por ${newBooking.resident}` + if (!formData.resident || !formData.date || !formData.time) { + showNotification("Preencha todos os campos obrigatórios.", "error"); + return; + } + try { + const facilityNames = { 'gym': 'Ginásio', 'hall': 'Salão de Festas', 'park': 'Parque de Jogos' }; + const bookingData = { + ...formData, + facilityName: facilityNames[formData.facility], + status: 'Confirmado' }; - setFinances(prev => [newIncome, ...prev]); - } + + const newBookingRef = push(ref(db, 'reservas')); + await set(newBookingRef, bookingData); - showNotification(`Reserva confirmada para ${newBooking.facilityName}`); - handleCloseModal(); - } + if (bookingData.cost > 0) { + const newIncomeRef = push(ref(db, 'financas')); + await set(newIncomeRef, { + type: 'income', + category: `Reserva: ${bookingData.facilityName}`, + date: bookingData.date, + amount: bookingData.cost, + desc: `Reserva por ${bookingData.resident}` + }); + } + showNotification(`Reserva confirmada para ${bookingData.facilityName}`); + handleCloseModal(); + } catch (error) { + console.error("Erro ao criar reserva:", error); + showNotification("Erro ao criar reserva.", "error"); + } + }; + + const handleGenerateInvoice = async (resident) => { + if (resident.pending <= 0) { + showNotification(`Não há dívidas para a fração ${resident.unit}`, 'warning'); + return; + } + try { + const newInvoiceRef = push(ref(db, 'faturacao')); + await set(newInvoiceRef, { + residentId: resident.id, + unit: resident.unit, + name: resident.name, + amount: Number(resident.pending), + date: new Date().toISOString().split('T')[0], + status: 'Emitida' + }); + showNotification(`Fatura instantânea gerada para a fração ${resident.unit}`, 'success'); + } catch (error) { + console.error("Erro ao faturar:", error); + showNotification("Erro ao gerar fatura.", "error"); + } + }; const DashboardView = () => (
@@ -1300,12 +1424,7 @@ ))} -
- - - - -
+
)} @@ -1349,7 +1468,7 @@ Notificar