diff --git a/index.html b/index.html index 5db40ff..552e22a 100644 --- a/index.html +++ b/index.html @@ -100,7 +100,7 @@ } from 'lucide-react'; import { app } from './firebase.js'; import { getAuth, signInWithEmailAndPassword, createUserWithEmailAndPassword } 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'; + import { getDatabase, ref, push, set, onValue, remove, update } from 'https://www.gstatic.com/firebasejs/12.1.0/firebase-database.js'; const auth = getAuth(app); const db = getDatabase(app); @@ -141,16 +141,8 @@ // --- VALIDAÇÕES OFICIAIS --- function validarNIF(nif) { - nif = nif.replace(/\s+/g, ''); - if (!/^\d{9}$/.test(nif)) return false; - if (nif.charAt(0) === '0') return false; - let soma = 0; - for (let i = 0; i < 8; i++) { - soma += parseInt(nif.charAt(i), 10) * (9 - i); - } - const resto = soma % 11; - const digitoControlo = (resto === 0 || resto === 1) ? 0 : (11 - resto); - return digitoControlo === parseInt(nif.charAt(8), 10); + nif = String(nif).replace(/\s+/g, ''); + return /^\d{9}$/.test(nif); } function validarDocumento(doc) { @@ -554,6 +546,14 @@ unit: data.unit, pending: 0 }); + + await push(ref(db, `notificacoes/admin`), { + timestamp: Date.now(), + message: `Novo pedido de registo: ${data.name} (${data.unit}). A aguardar aprovação.`, + time: 'Agora', + type: 'info', + read: false + }); sessionStorage.setItem('condo_auth', 'true'); sessionStorage.setItem('condo_role', 'morador'); @@ -599,6 +599,14 @@ pending: 0, contact: data.password // Guardado no campo contact apenas para o fallback mock local de login funcionar }); + + await push(ref(db, `notificacoes/admin`), { + timestamp: Date.now(), + message: `Novo pedido de registo: ${data.name} (${data.unit}). A aguardar aprovação.`, + time: 'Agora', + type: 'info', + read: false + }); } catch(dbErr) { console.error("Base de dados inacessível no fallback.", dbErr); } @@ -854,7 +862,19 @@ const sendSystemNotification = async (message, type = 'info', targetUserId = 'admin') => { const newNotif = { timestamp: Date.now(), message, time: 'Agora', type, read: false }; - await push(ref(db, `notificacoes/${targetUserId}`), newNotif); + if (targetUserId === 'todos') { + const promises = residents.map(r => push(ref(db, `notificacoes/${r.id}`), newNotif)); + promises.push(push(ref(db, `notificacoes/admin`), newNotif)); + await Promise.all(promises); + } else { + await push(ref(db, `notificacoes/${targetUserId}`), newNotif); + } + }; + + const handleMarkAsRead = async (notifId) => { + const targetFolder = userRole === 'admin' ? 'admin' : currentUserId; + const notifRef = ref(db, `notificacoes/${targetFolder}/${notifId}`); + await update(notifRef, { read: true }); }; const showNotification = (message, type = 'success') => { @@ -980,6 +1000,13 @@ const amount = Number(formData.amount); const newFinanceRef = push(ref(db, 'financas')); await set(newFinanceRef, { ...formData, amount }); + + if (formData.type === 'expense') { + sendSystemNotification(`Nova despesa registada: ${formData.category} - ${amount.toFixed(2)}€`, 'warning', 'admin'); + } else { + sendSystemNotification(`Nova receita registada: ${formData.category} - ${amount.toFixed(2)}€`, 'success', 'admin'); + } + showNotification(`Movimento de ${amount}€ registado`); handleCloseModal(); } catch (error) { @@ -997,6 +1024,12 @@ try { const newIssueRef = push(ref(db, 'manutencao')); await set(newIssueRef, { ...formData }); + + sendSystemNotification(`Nova ocorrência reportada: ${formData.title} (${formData.location})`, 'warning', 'admin'); + if (userRole !== 'admin') { + sendSystemNotification(`A sua ocorrência "${formData.title}" foi reportada com sucesso.`, 'info', currentUserId); + } + showNotification('Nova ocorrência reportada', 'warning'); handleCloseModal(); } catch (error) { @@ -1031,6 +1064,9 @@ const newPending = (Number(morador.pending) || 0) + valor; await set(ref(db, `condominos/${morador.id}/pending`), newPending); + sendSystemNotification(`Foi emitida uma nova fatura no valor de ${valor.toFixed(2)}€ (Categoria: ${formData.categoria})`, 'warning', morador.id); + sendSystemNotification(`Fatura de ${valor.toFixed(2)}€ emitida para ${morador.name} (${morador.unit})`, 'info', 'admin'); + showNotification(`Fatura de ${valor.toFixed(2)}€ emitida para ${morador.name}`); handleCloseModal(); } catch (error) { @@ -1042,6 +1078,7 @@ const handlePayFatura = async (fatura) => { try { await set(ref(db, `faturas/${fatura.id}/status`), 'Em Validação'); + sendSystemNotification(`O pagamento da sua fatura de ${fatura.categoria} foi submetido. Aguarda validação.`, 'info', fatura.moradorId); sendSystemNotification(`Comprovativo recebido da fração ${fatura.fracao}.`, 'info', 'admin'); showNotification("Comprovativo enviado! A aguardar validação do administrador.", "success"); } catch (error) { @@ -1061,6 +1098,7 @@ await set(ref(db, `condominos/${morador.id}/pending`), newPending); } sendSystemNotification(`O seu pagamento da fatura de ${fatura.categoria} foi aprovado!`, 'success', fatura.moradorId); + sendSystemNotification(`Pagamento aprovado para a fatura de ${fatura.categoria} da fração ${morador?.unit || fatura.fracao}.`, 'success', 'admin'); showNotification("Pagamento aprovado com sucesso!", "success"); } catch (error) { console.error("Erro ao aprovar fatura:", error); @@ -1073,6 +1111,7 @@ const issue = issues.find(i => i.id === id); if (issue) { await set(ref(db, `manutencao/${id}`), { ...issue, status: 'Resolvido' }); + sendSystemNotification(`A manutenção "${issue.title}" foi concluída com sucesso.`, 'success', 'todos'); showNotification('Ocorrência resolvida com sucesso'); } } catch (error) { @@ -1108,6 +1147,12 @@ desc: `Reserva por ${bookingData.resident}` }); } + + sendSystemNotification(`Nova reserva: ${bookingData.facilityName} a ${bookingData.date}`, 'info', 'admin'); + if (userRole !== 'admin') { + sendSystemNotification(`A sua reserva para ${bookingData.facilityName} foi confirmada.`, 'success', currentUserId); + } + showNotification(`Reserva confirmada para ${bookingData.facilityName}`); handleCloseModal(); } catch (error) { @@ -1131,6 +1176,10 @@ date: new Date().toISOString().split('T')[0], status: 'Emitida' }); + + sendSystemNotification(`Foi emitida uma nova fatura instantânea no valor de ${Number(resident.pending).toFixed(2)}€`, 'warning', resident.id); + sendSystemNotification(`Fatura instantânea gerada para a fração ${resident.unit} no valor de ${Number(resident.pending).toFixed(2)}€`, 'info', 'admin'); + showNotification(`Fatura instantânea gerada para a fração ${resident.unit}`, 'success'); } catch (error) { console.error("Erro ao faturar:", error); @@ -1699,7 +1748,14 @@

{notif.message}

-

{notif.time}

+
+

{notif.time}

+ {!notif.read && ( + + )} +
@@ -1846,6 +1902,8 @@