diff --git a/.vscode/settings.json b/.vscode/settings.json index 6f3a291..f673a71 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,3 @@ { - "liveServer.settings.port": 5501 + "liveServer.settings.port": 5502 } \ No newline at end of file diff --git a/README.md b/README.md index 321c6b6..da444cc 100644 --- a/README.md +++ b/README.md @@ -1 +1,78 @@ -# GestorCondominio +# CondoMaster Pro + +![CondoMaster Pro Preview](https://via.placeholder.com/800x400.png?text=CondoMaster+Pro+-+Gestão+de+Condomínios) + +O **CondoMaster Pro** é uma aplicação web moderna e responsiva (Single Page Application - SPA) desenvolvida para simplificar e digitalizar a gestão de condomínios. Desenhado a pensar tanto na entidade gestora (Administradores) como nos habitantes (Moradores), o sistema integra todas as comunicações, finanças e ocorrências do dia a dia. + +--- + +## ✨ Funcionalidades Principais + +### Acesso Baseado em Perfis (Role-Based Access Control) +* **🧑‍💻 Administradores (`admin`)**: Visão 360º. Podem gerir moradores, registar receitas e despesas globais, cobrar dívidas, enviar faturas manuais (com um clique) e alterar papéis de acesso ("promover" ou "despromover"). +* **🏠 Moradores (`morador`)**: Painel simplificado desenhado para transparência. Permite verificar as próprias quotas em atraso, reportar danos/anomalias (manutenção) e reservar espaços comuns. + +### 💰 Faturação e Gestão Financeira (Exclusivo Admins) +- Visão geral completa de Fluxo de Caixa (Despesas vs. Receitas). +- Emissão instantânea de recibos avulso. +- Notificações de dívidas encaminhadas com apenas um clique na tabela integrada de **Faturação**. + +### 📅 Gestão de Reservas +* **Lista e Mapa**: Sistema visual de reservas em três ginásios, salões de festas e parques de jogos. +* **Histórico Completo**: Página exclusiva para listagem de todas as reservas agendadas, acessível a todas as entidades. + +### 🛠️ Ocorrências e Manutenção +- Secção para os condóminos relatarem problemas no edifício (ex: candeeiros partidos, problemas de elevador) indicando o grau de severidade. +- Os administradores avaliam a prioridade, resolvem as ocorrências digitalmente e mantêm os residentes notificados do estado. + +### 🎨 Design Moderno & UI Inteligente +* Compatível com **Mobile e Desktop**. +* Inclui um switch suave para **Modo Escuro (Dark Mode)**, Modo Claro e deteção por Sistema, integrados perfeitamente no menu de perfil. +* Sistema de notificações do tipo Themed/Toasts para validações imediatas (Confirmações, Erros, Avisos). + +--- + +## 💻 Stack Tecnológica + +O projeto foi construído usando uma arquitetura modular moderna num formato de ficheiro de entrada principal que integra os ecossistemas: + +* **React**: Implementado diretamente do navegador (sem build step local). Geração de componentes declarativos (UI Dinâmica). +* **Tailwind CSS**: Carregado dinamicamente para aplicar estilos sofisticados e reativos, acelerando o desenvolvimento visual da interface. +* **Lucide React**: Biblioteca adotada inteiramente para a vasta panóplia de ícones (`lucide-react`). + +--- + +## 🚀 Como Iniciar (Quick Start) + +Visto que o projeto já traz toda a lógica baseada na Web injetada, não é precisa uma instalação exaustiva na máquina. + +1. **Baixar o Projeto:** + Basta que tenhas o ficheiro principal (geralmente `index.html`) e o ambiente disponível na mesma pasta (neste caso `GestorCondominio`). + +2. **Abrir a Aplicação:** + - Para pré-visualizar rapidamente a aplicação, podes apenas fazer duplo-clique no **`index.html`** para abrir o sistema num browser moderno. + - Alternativamente, podes hospedar este ficheiro num serviço de Live Server ou num host online (ex: Vercel, Netlify, Github Pages), não existindo configuração complexa. + +--- + +## 🔐 Credenciais de Base (Ambiente de Testes) + +Neste momento as credenciais estão pré-programadas para experimentação do comportamento do sistema: + +**Acesso de Administrador:** +- **Email:** `administradores@gmail.com` +- **Palavra-passe:** `admin123` + +**Acesso de Morador:** +- **Email:** `moradores@gmail.com` +- **Palavra-passe:** `moradores123` + +*(Nota: Alguns moradores registados na base de dados fictícia no "Estado" da app podem aceder através da palavra-passe padrão de morador ou usando o respetivo contacto telefónico)*. + +--- + +## 👨‍🔧 Desenvolvimento e Melhorias Mapeadas +* Ligação completa de base de dados escalável com a inicialização nativa contida do **Firebase**. +* Emissão e importação de documentos faturação automatizados PDF. + +***Desenvolvido para criar comunidades perfeitamente ligadas.*** diff --git a/index.html b/index.html index 2c37329..2d085bc 100644 --- a/index.html +++ b/index.html @@ -98,13 +98,13 @@ Dumbbell, PartyPopper, Trophy, Map, Calendar, MapPin, Info } from 'lucide-react'; - + const INITIAL_RESIDENTS = [ - { id: 1, unit: '1º Esq', name: 'Ana Silva', contact: '912 345 678', email: 'ana.silva@email.com', status: 'Pago', pending: 0 }, - { id: 2, unit: '1º Dto', name: 'Carlos Santos', contact: '965 432 109', email: 'carlos.s@email.com', status: 'Pendente', pending: 45.00 }, - { id: 3, unit: '2º Esq', name: 'Maria Pereira', contact: '933 221 110', email: 'maria.p@email.com', status: 'Pago', pending: 0 }, - { id: 4, unit: '2º Dto', name: 'João Ferreira', contact: '918 765 432', email: 'joao.f@email.com', status: 'Atrasado', pending: 135.00 }, - { id: 5, unit: '3º Esq', name: 'Sofia Costa', contact: '922 334 455', email: 'sofia.c@email.com', status: 'Pago', pending: 0 }, + { id: 1, unit: '1º Esq', name: 'Ana Silva', contact: '912 345 678', email: 'ana.silva@email.com', status: 'Pago', pending: 0, role: 'morador' }, + { id: 2, unit: '1º Dto', name: 'Carlos Santos', contact: '965 432 109', email: 'carlos.s@email.com', status: 'Pendente', pending: 45.00, role: 'morador' }, + { id: 3, unit: '2º Esq', name: 'Maria Pereira', contact: '933 221 110', email: 'maria.p@email.com', status: 'Pago', pending: 0, role: 'morador' }, + { id: 4, unit: '2º Dto', name: 'João Ferreira', contact: '918 765 432', email: 'joao.f@email.com', status: 'Atrasado', pending: 135.00, role: 'morador' }, + { id: 5, unit: '3º Esq', name: 'Sofia Costa', contact: '922 334 455', email: 'sofia.c@email.com', status: 'Pago', pending: 0, role: 'morador' }, ]; const INITIAL_FINANCES = [ @@ -258,12 +258,9 @@ const handleSubmit = (e) => { e.preventDefault(); - if (email === 'admin@gmail.com' && password === 'admin123') { - onLogin({ role: 'admin', email }); - } else if (email === 'moradores@gmail.com' && password === 'moradores123') { - onLogin({ role: 'resident', email }); - } else { - setError('Credenciais incorretas'); + const success = onLogin(email, password); + if (!success) { + setError('Email ou Palavra-passe incorreta'); } }; @@ -280,13 +277,13 @@
- + setEmail(e.target.value)} className="w-full px-4 py-3 rounded-lg border border-slate-300 dark:border-dark-border focus:ring-2 focus:ring-blue-500 bg-white dark:bg-dark-card text-slate-900 dark:text-white transition-colors" - placeholder="Endereço de e-mail" + placeholder="Endereço de email" autoFocus required /> @@ -321,22 +318,36 @@ const [activeTab, setActiveTab] = useState('dashboard'); const [isSidebarOpen, setSidebarOpen] = useState(false); const [searchQuery, setSearchQuery] = useState(''); - const [theme, setTheme] = useState('system'); + const [theme, setTheme] = useState('system'); const [isAuthenticated, setIsAuthenticated] = useState(() => { return sessionStorage.getItem('condo_auth') === 'true'; }); const [userRole, setUserRole] = useState(() => { - return sessionStorage.getItem('condo_role') || 'admin'; + return sessionStorage.getItem('condo_role') || 'morador'; }); - const handleLogin = (userData) => { - sessionStorage.setItem('condo_auth', 'true'); - if (userData && userData.role) { - sessionStorage.setItem('condo_role', userData.role); - setUserRole(userData.role); + const handleLogin = (email, password) => { + let role = null; + if (email === 'administradores@gmail.com' && password === 'admin123') { + role = 'admin'; + } else if (email === 'moradores@gmail.com' && password === 'moradores123') { + role = 'morador'; + } else { + const residentUser = residents.find(r => r.email && r.email.toLowerCase() === email.toLowerCase()); + if (residentUser && (password === residentUser.contact || password === '1234')) { + role = residentUser.role || 'morador'; + } } - setIsAuthenticated(true); + + if (role) { + sessionStorage.setItem('condo_auth', 'true'); + sessionStorage.setItem('condo_role', role); + setIsAuthenticated(true); + setUserRole(role); + return true; + } + return false; }; const handleLogout = () => { @@ -344,6 +355,7 @@ sessionStorage.removeItem('condo_auth'); sessionStorage.removeItem('condo_role'); setIsAuthenticated(false); + setUserRole(null); setActiveTab('dashboard'); } }; @@ -362,7 +374,7 @@ const notificationRef = useRef(null); - const initialResidentForm = { unit: '', name: '', contact: '', email: '', status: 'Pago', pending: 0 }; + const initialResidentForm = { unit: '', name: '', contact: '', email: '', status: 'Pago', pending: 0, role: 'morador' }; const initialFinanceForm = { type: 'expense', category: '', amount: '', desc: '', date: new Date().toISOString().split('T')[0] }; const initialIssueForm = { title: '', location: '', priority: 'Média', status: 'Novo', date: new Date().toISOString().split('T')[0] }; const initialBookingForm = { facility: 'gym', date: new Date().toISOString().split('T')[0], time: '', resident: '', cost: 0 }; @@ -462,6 +474,11 @@ }); }; + 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) { @@ -533,7 +550,11 @@ const DashboardView = () => (
- = 0 ? 'up' : 'down'} trendValue="Atual" color="bg-blue-500" /> + {userRole === 'admin' ? ( + = 0 ? 'up' : 'down'} trendValue="Atual" color="bg-blue-500" /> + ) : ( + + )}
@@ -542,7 +563,7 @@

Próximas Reservas

- +
{bookings.slice(0, 4).map(booking => ( @@ -568,7 +589,7 @@

Quadro de Avisos

- +
{issues.slice(0, 3).map((issue) => ( @@ -752,7 +773,7 @@ Prioridade {issue.priority} - {issue.status !== 'Resolvido' && ( + {userRole === 'admin' && issue.status !== 'Resolvido' && ( - + {userRole === 'admin' && ( + + )}
-
@@ -842,8 +865,8 @@

Autenticação de Dois Fatores (2FA)

-

Recomendamos ativar o 2FA para maior segurança da sua conta de administrador.

- +

Recomendamos ativar o 2FA para maior segurança da sua conta.

+
@@ -854,7 +877,7 @@
-
@@ -976,13 +999,11 @@
Geral
{ setActiveTab('dashboard'); setSidebarOpen(false); }} /> - {userRole === 'admin' && ( - <> - { setActiveTab('residents'); setSidebarOpen(false); }} /> - { setActiveTab('finance'); setSidebarOpen(false); }} /> - { setActiveTab('maintenance'); setSidebarOpen(false); }} /> - - )} + {userRole === 'admin' && { setActiveTab('residents'); setSidebarOpen(false); }} />} + {userRole === 'admin' && { setActiveTab('finance'); setSidebarOpen(false); }} />} + {userRole === 'admin' && { setActiveTab('billing'); setSidebarOpen(false); }} />} + { setActiveTab('maintenance'); setSidebarOpen(false); }} /> + { setActiveTab('messages'); setSidebarOpen(false); }} /> { setActiveTab('map'); setSidebarOpen(false); }} />
Espaços Comuns
@@ -1012,12 +1033,15 @@ activeTab === 'dashboard' ? 'Visão Geral' : activeTab === 'residents' ? 'Condóminos' : activeTab === 'finance' ? 'Gestão Financeira' : - activeTab === 'maintenance' ? 'Ocorrências e Manutenção' : - activeTab === 'map' ? 'Mapa do Condomínio' : - activeTab === 'gym' ? 'Ginásio' : - activeTab === 'hall' ? 'Salão de Festas' : - activeTab === 'park' ? 'Parque de Jogos' : - activeTab === 'profile' ? 'O Meu Perfil' : activeTab + activeTab === 'billing' ? 'Faturação e Cobranças' : + activeTab === 'maintenance' ? 'Ocorrências e Manutenção' : + activeTab === 'messages' ? 'Mensagens e Fórum' : + activeTab === 'map' ? 'Mapa do Condomínio' : + activeTab === 'all_bookings' ? 'Todas as Reservas' : + activeTab === 'gym' ? 'Ginásio' : + activeTab === 'hall' ? 'Salão de Festas' : + activeTab === 'park' ? 'Parque de Jogos' : + activeTab === 'profile' ? 'O Meu Perfil' : activeTab }
@@ -1075,7 +1099,7 @@ onClick={() => setActiveTab('profile')} title="Meu Perfil" > - AD + {userRole === 'admin' ? 'AD' : 'MO'}
@@ -1126,6 +1150,49 @@ /> )} + {/* --- ALL BOOKINGS --- */} + {activeTab === 'all_bookings' && ( +
+
+
+

Histórico de Reservas

+

Lista completa de agendamentos em todos os espaços de lazer

+
+ +
+
+ {bookings.map(booking => ( +
+
+
+ {booking.facility === 'gym' ? : booking.facility === 'hall' ? : } +
+
+

{booking.facilityName}

+

{booking.date} • {booking.time}

+
+
+
+

{booking.resident}

+
+ +
+
+
+ ))} + {bookings.length === 0 && ( +
+ +

Sem reservas

+

Ainda não existem agendamentos no condomínio.

+
+ )} +
+
+ )} + {/* --- RESIDENTS --- */} {activeTab === 'residents' && (
@@ -1161,6 +1228,7 @@ Proprietário Contacto Estado Quotas + Acesso Em Dívida Ações @@ -1177,6 +1245,15 @@ {resident.contact} + + + 0 ? 'text-red-600 dark:text-red-400' : 'text-slate-600 dark:text-slate-400'}`}> {Number(resident.pending).toFixed(2)}€ @@ -1204,6 +1281,65 @@
)} + {/* --- BILLING / COBRANÇAS --- */} + {activeTab === 'billing' && userRole === 'admin' && ( +
+
+
+

Avisos de Cobrança

+

Emita faturas ou avise condóminos individualmente

+
+ +
+
+ + + + + + + + + + + {residents.map((resident) => ( + + + + + + + ))} + +
FraçãoCondóminoQuotas em AtrasoAções
{resident.unit}{resident.name} 0 ? 'text-red-600 dark:text-red-400' : 'text-green-600 dark:text-green-500'}`}> + {resident.pending > 0 ? `${Number(resident.pending).toFixed(2)}€` : 'Regularizado'} + + + + +
+
+
+ )} + {/* --- FINANCES --- */} {/* --- FINANCES --- */} {activeTab === 'finance' && ( @@ -1272,7 +1408,7 @@ {item.type === 'income' ? '+' : '-'}{Number(item.amount).toFixed(2)}€ - @@ -1285,6 +1421,118 @@
)} + {/* --- MESSAGES --- */} + {activeTab === 'messages' && ( +
+ {/* Contact List */} +
+
+

Conversas

+ +
+
+
+ + +
+
+
+
+
+
+ +
+
+
+

Fórum do Condomínio

+ Agora +
+

Administração: Reunião na próxima sexta.

+
+
+
+ {residents.slice(0, 4).map(res => ( +
+
+
+ {res.name.substring(0, 2).toUpperCase()} +
+
+
+

{res.name} ({res.unit})

+ Ontem +
+

Tudo bem, tratamos disso!

+
+
+
+ ))} +
+
+ + {/* Chat Area */} +
+
+
+
+ +
+
+

Fórum do Condomínio

+

Todos os moradores

+
+
+ +
+ +
+
Hoje
+ +
+
+

Administração

+

Bom dia a todos. Relembramos que a manutenção dos elevadores ocorrerá amanhã às 10h.

+ 09:00 +
+
+ +
+
+

Obrigado pelo aviso!

+ 09:15 +
+
+ +
+
+

Maria Pereira (2º Esq)

+

Alguém encontrou um porta-chaves com formato de gato na entrada do prédio?

+ 11:32 +
+
+
+ +
+ { e.preventDefault(); showNotification('A sua mensagem foi enviada!', 'success'); e.target.reset(); }} className="flex gap-2"> + + + + +
+
+
+ )} + {/* --- MAINTENANCE --- */} {activeTab === 'maintenance' && } @@ -1382,22 +1630,22 @@ root.render(); - const app = initializeApp(firebaseConfig); - const analytics = getAnalytics(app); - - \ No newline at end of file