notificações
This commit is contained in:
84
index.html
84
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 @@
|
||||
<div className={`mt-1 w-2 h-2 rounded-full flex-shrink-0 ${notif.type === 'info' ? 'bg-blue-500' : notif.type === 'success' ? 'bg-green-500' : 'bg-red-500'}`}></div>
|
||||
<div>
|
||||
<p className="text-sm text-slate-700 dark:text-slate-200 leading-tight">{notif.message}</p>
|
||||
<p className="text-xs text-slate-400 dark:text-slate-500 mt-1">{notif.time}</p>
|
||||
<div className="flex justify-between items-center mt-1">
|
||||
<p className="text-xs text-slate-400 dark:text-slate-500">{notif.time}</p>
|
||||
{!notif.read && (
|
||||
<button onClick={() => handleMarkAsRead(notif.id)} className="text-[10px] text-blue-600 dark:text-blue-400 hover:underline">
|
||||
Marcar como lida
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1846,6 +1902,8 @@
|
||||
<button onClick={() => {
|
||||
if(window.confirm('Aprovar este morador?')) {
|
||||
set(ref(db, `condominos/${req.id}/status`), 'aprovado');
|
||||
sendSystemNotification(`O registo do morador ${req.name} (${req.unit || ''}) foi aprovado.`, 'success', 'admin');
|
||||
sendSystemNotification(`A sua conta foi aprovada pela administração! Bem-vindo(a).`, 'success', req.id);
|
||||
showNotification('Morador aprovado com sucesso!', 'success');
|
||||
}
|
||||
}} className="p-2 bg-green-100 text-green-600 rounded hover:bg-green-200 dark:bg-green-900/30 dark:text-green-400" title="Aprovar">
|
||||
|
||||
Reference in New Issue
Block a user