ajustes
This commit is contained in:
@@ -1,28 +1,32 @@
|
||||
test_nif.js,1777390754764,b50e0b4fe7732186ea056d183a0f2bbe7026cf79d384ce66391f746fe6969af6
|
||||
sw.js,1770114976862,b003899c6761f1320628a7e6048429714950195393f134df16159399586d3ca4
|
||||
temp_script.jsx,1778062694175,e61487a0533f953dd5040c3f8bfba051119ee86ea855d2baf353bbe20f7abd86
|
||||
sw.js,1778065253229,20540da27a8005d1e7a54a576607c687217feb037aedb26e5d8dab53d69deb34
|
||||
style.css,1770114976862,4c2e2686b637f6f2f060298dfbabf690219284ff4c5c027711c5b443dde07332
|
||||
script.js,1776937115126,4b08e5f41663ef287d352039798448e36a3c52c60014d58f7ed31471dab4066d
|
||||
manifest.json,1770114976862,eb6e5b596d2a562026e361e5ee5bd1f4c3fc94a5e3b8cfc9d8761c6d21b2b991
|
||||
index.html,1778067423164,0896158ce7fa3228f5ba10a60ab661d8dc55b4bb492c835399ce9626c8f40318
|
||||
firebase.js,1776936516051,37feb64e428313ec44afdabc4a2a348f29aae1ce0893c8099205750b1b5faf87
|
||||
firebase,1777902366124,9d8f53c2037285ddb56fad26e9a581980370cec0dae5bfae0e91e5a87e8b96b2
|
||||
RELATORIO_TECNICO.md,1777995323646,3219fcffa736181cafc9e9b13f0068017db9fdb7458f644973090e6b7b4b66e5
|
||||
README.md,1777387819540,1b62e096bfae58e52be118bb6e3ed79117a3c919af031d924821394e605dece4
|
||||
.vscode/settings.json,1776788233398,a44051331ea8dca3e97ad536007d1fb294c28038b178012e6fa9dc481db05ce9
|
||||
.git/index,1777903218134,26e327830d8918bcfe11e89809b61d4101f5a1860f26fd5d958f736719e1e629
|
||||
RELATORIO_TECNICO.md,1778058174796,fad35f12b1f2d062f72e7a448bb643fd3cfdd24423eaacc14cbbb20172ead7be
|
||||
README.md,1778057411525,cc32fa073114e014f06988652ebe4c6af9ba3dbf913b9f25d38a16ce3d700605
|
||||
.vscode/settings.json,1778056161665,3a247752ccf28f259e2e604bf44311ab91b6db3864b120e08f2823951d1c55d8
|
||||
.git/index,1777997762141,9c98b1a4efc883a6a6bb5d798da2aa83b7b0e07655aec8c43f71ba041de76b86
|
||||
.git/description,1773160274654,3cdc7b6a29de07f63b76d16b9911d93468000346945f759d4f6456660b5c113b
|
||||
.git/config,1773914677241,cb4cd1c7c28ac13d2dccf79f667245402cf551c998ba1f6b58abc28f3ae11e7f
|
||||
.git/ORIG_HEAD,1777392979044,2612c449de4f930bfb197ddb13780ca77cbb7bf6db7e91493d412b634ddcdebd
|
||||
.git/HEAD,1773160809135,a39dc51e21d1523cdef2091e7c7ab30a33ad42a7cd5da1f45139746e5c24b667
|
||||
.git/COMMIT_EDITMSG,1777903218135,bf4bd4dfbb9f6c9351b2313e5df24a395e7a43208ff8922572237c166422cbcf
|
||||
.git/refs/remotes/origin/main,1777903229622,30437e7b9aa4378a80a4ab39abb8a4925238b5ab69719fac495bdb8f4ee17616
|
||||
.git/COMMIT_EDITMSG,1777997762142,47e4b83fa796b7965feed27fd3d7017275b18a8518ff0cbc20bf12dcc0ff7e36
|
||||
.git/refs/remotes/origin/main,1777997762539,07acf1e52d80759a7a23b8c54921216d19f042b468bed7e35eecff2180712b24
|
||||
.git/refs/remotes/origin/HEAD,1773830493701,0f5d56efe56c5dcabb387d965aad58d0f60a3b7485cb9b04bef04b93bebf911e
|
||||
.git/refs/heads/main,1777903218136,30437e7b9aa4378a80a4ab39abb8a4925238b5ab69719fac495bdb8f4ee17616
|
||||
.git/refs/heads/main,1777997762143,07acf1e52d80759a7a23b8c54921216d19f042b468bed7e35eecff2180712b24
|
||||
.git/objects/fd/3d3838a9118dc446e6ab65d38a5ce1747fd0d6,1777393032091,b7fcc251a3edcfc6eb1d7a0ea70a10d16f297520eb5e95c822ab5f754da8dc4a
|
||||
.git/objects/f6/73a71b7a275609030462c0278586d61e5f3a00,1773830457776,6ef355c279049956337049bd863d18f205f75ec1bcaee8c82bf26a7d2a65af72
|
||||
.git/objects/f4/577dec9e03ee9efa3af7ee05d60576b07f8d99,1773161014724,c78b5a6ceff9eb62984db81b7041efbdcac774d45a96d7536ede20a9a6ba10c5
|
||||
.git/objects/f3/7d7211c51f051db56b0e67a1bfca55f649e1e5,1773161195360,a0961540cd8d800dd424b0eb7d503bb35012ad90477c1ec10b9e4b34e1102027
|
||||
.git/objects/ed/cc9b9ffb9c172f4579f71d3e10414f58ea26d0,1777997762125,439ee4c3ff19456eafa85052089df8c4ea4c00c1c293fd4c639377d8d125ebb9
|
||||
.git/objects/ea/2dee2f517449595d633889a410cc2aefa2b513,1776937200147,9392a6f5dd0687166fd9b60a4f28176604046d293196fe53038e9d2e456d5336
|
||||
.git/objects/e8/5b891f05021af7f78059851bac361ba110e459,1777903218123,c0684017afd5d4cb4234a4191773d088b5ae747e88b477cabcd08fff53e24450
|
||||
.git/objects/e7/1629b1f461748a0642a397d9aa94d4425d3a6a,1777997762126,7511a853da08dcde2f169bc9afe5b92eb9028dc27052c95a436b62368897e4ad
|
||||
.git/objects/e0/6cdd4ddfacadcef9577916c84406559b985623,1773161195362,d89c95c83f040e788bd182a67bc3cbb9afdf7d3740a388cc4d43d2514b80e556
|
||||
.git/objects/da/444cce2c8426f321e774e9bc8134642bae50ae,1773830457778,293cb0fb7b39d4ffbece90d8f57bcd730c29d754a284c7d83436a489661e507e
|
||||
.git/objects/d3/f58873ab4f70d24d92349c7e85c7df5c7bb7c2,1776937200165,ca8deaee745fd2e87b4804183acc8c484a36b88db729b2c4cb52f17f99ffb747
|
||||
@@ -37,6 +41,7 @@ README.md,1777387819540,1b62e096bfae58e52be118bb6e3ed79117a3c919af031d924821394e
|
||||
.git/objects/94/5d8710c7134fdf4feaa701af8d88bda61db300,1776957069564,b68f1087c81f6a22ae676c795169f34674865c85b4e9631b82db50a73cb190d5
|
||||
.git/objects/92/d9d123244957aa1b1dd1a6f54b9899ec28def8,1773161195399,3f3e3c1f4e739595d33d89aef665c3933ec1af7914020cf0ff6de3e3d41d0109
|
||||
.git/objects/8e/7980160dd6e37a0edf181cb022b86334e205fd,1777393032077,921856f2b98e34016c387ef1f73025bba7ed846c64d8017ec6d2e6e0e9f7c586
|
||||
.git/objects/8d/053f517afe910c5933a43eca4e7dad2fe5edf1,1777997762122,e3802ac195f07bda67d59b5754f2254282916e9d5277388af9751fc1cb511723
|
||||
.git/objects/8c/b6ffc314647094561a18f0af805412260abc3b,1776326595546,a256a28d0e28ac76431ae5406923461a7b4fd7aaac40f5dae947377abc2ca830
|
||||
.git/objects/84/2dd08f73738644fe58eecb5409d5ebd1544efb,1776326595582,b5b51d6c09e2f4e9767489040755ab4e1c4cdce30f46567e4391e0e228f04395
|
||||
.git/objects/7f/0d5ab4d6ff0318cbd6a9c3f8eb57aaac4634a4,1777542176251,16d9b4319d629d55eb92c57e1070cca1671b642d2c780895fc600570fcb5b895
|
||||
@@ -56,7 +61,10 @@ README.md,1777387819540,1b62e096bfae58e52be118bb6e3ed79117a3c919af031d924821394e
|
||||
.git/objects/40/4bcf86370b3aa54e5c09e50a7be47241fe63ee,1773830230299,f9bae290124afebf31f114d71dc73d407a04a248fff87a729abfa68e807e6cf9
|
||||
.git/objects/3b/cafc02b8f3692fbb5b6d93debe13bb50563c2a,1776937200150,70a72e7716314bf0c914dd3d802a5a0ba4c93a23c179aaa86fb8c660f039e344
|
||||
.git/objects/38/465e5ae301b3ddefabfe22b188c4fee52182c0,1773161195398,dbcf76217184aff41336a8f6530dc63b073bb4235b0fec701dfa33df27a0b402
|
||||
.git/objects/33/28ec86365d3f1a608e8dd533291ff447c97d00,1777997762141,34991e7e8382ce6d32b8a5a1ee47d64613523696d9a8b5ae59cbf5375155559d
|
||||
.git/objects/32/1c6b6ef5843bf86598659bb85cf0ef3d63ccc8,1773161000773,7781172d118bad5f4282ecc246a2e432d07e682ebb5652c325a8d26d272f804c
|
||||
.git/objects/31/fb5e7eceb602bd43982765eed9f3848c86990a,1777997762121,2ad3979c7c6f61f78c82c768b402e59a806f9b3fea4acb9065548670b7b8a235
|
||||
.git/objects/30/9efec640a08f35557a83afabc2329354be495b,1777997762140,a5f9548d56a5a654f7c326a001e360dd12bf0ac74382d02f98023faa30b1484e
|
||||
.git/objects/2d/a32767f43e7735bca583bf1aa5c7436ae485fe,1773830457795,f1cc7925dc66329b987ae56725551624b57040a581eedabe08e1eb66dbe6e6c2
|
||||
.git/objects/2d/085bcbe50700bbcff79f9186c9c317d9bb1ab4,1773830457780,b8a511b75f969777c2fb6d5e3e0538da8888a4817ae34d0e3192c7fe2695538a
|
||||
.git/objects/2c/fea43d887fc1e25adba99d01fc094e26bb25ae,1777903218123,3ff6da2a0ec8c954f94eeb6423bc392ec995d9b56198c375ad2ac417943dc43c
|
||||
@@ -64,6 +72,7 @@ README.md,1777387819540,1b62e096bfae58e52be118bb6e3ed79117a3c919af031d924821394e
|
||||
.git/objects/2b/d5891243d72a793a41ea51994856446a350f55,1777387356904,0547ff6336463e58b4d266d7a08388b2ee53c501b37d95bf19846f896a3bd1a2
|
||||
.git/objects/2a/f13184106a656f511871672a98dca04e58f04b,1776937200165,207a73dfdc380c9ff77147f77f094d28c1e6471f74fc37175c9cc6dde8ff2b3b
|
||||
.git/objects/2a/a7aa1819e7c69a73c81dadbf3643a3aec6fed5,1776183305313,9e31ebefd7d25152cd322bd673d587b1e1367ae75e01aed02e86d6b58f5a430a
|
||||
.git/objects/27/85c3386d34cce3cafd66e5892675c61548c3df,1777997762124,9362c43fef34a74a522bf592f011f778c530bba1edbf624926f84d7f9bf54183
|
||||
.git/objects/25/b0e88e2c1a6db6b52948a4d2fc649e06de8cb2,1776957069549,67160c871e47bc652ab9b8a11b65efe4a559f4dc7a84c16e9aeed10cd44d41b0
|
||||
.git/objects/25/5c1a39f46ad00812b25cf59858aabb19ebfa18,1776957069566,c328ecb00d90ba099ec4534307c0f3177478656f0fc2d21b4e02f6e743cf1773
|
||||
.git/objects/23/cec1dc9936a6d9be11078377c6a7685b26373f,1776183305295,d709b165a1199d66da18ba51afe1471aa7b068d4066284bf28d333f09e0c9670
|
||||
@@ -73,10 +82,11 @@ README.md,1777387819540,1b62e096bfae58e52be118bb6e3ed79117a3c919af031d924821394e
|
||||
.git/objects/0f/e52b062580722aedaf1e72d0873dfcd52ae1d0,1777393032092,cda51340d22051dc215c44006c12ac911bfac5c0a0e9535fb2f5acb8fd2a62e5
|
||||
.git/objects/0d/982210a52ba6078db202a8b2ac2d04e8d4ac41,1776326595581,c4c80ce646b15b1aa2e9811149739253f98987c4eb84bdf21d21410d902b2ee4
|
||||
.git/objects/0b/fc5efae1d163add058f1940feea6649f5da1b1,1777393032074,f19ecb9e3799eb6cd2f62e145b429f8c7c48fe8ffda704c49d6d925e9818c000
|
||||
.git/logs/HEAD,1777903218136,0e77927c7f96859d395e49b6ea9f8d7f54c6e6c60d00fef26fbd90459c5fe112
|
||||
.git/logs/refs/remotes/origin/main,1777903229624,d2f8ee46f879dca77a59471f0657aca5ce92f42b34044ae02702a1f788e79305
|
||||
.git/objects/00/0c1cd721b99d14281c3724f5488b040c152515,1777997762142,7d131dd9c4c33d6e017428809d3b927f369ee8fa38c7d6ae79d01bc10561e22b
|
||||
.git/logs/HEAD,1777997762143,f1258009669fe6da86a2aee96326d56f7d477b661aad447ff3bdaabbf9701c91
|
||||
.git/logs/refs/remotes/origin/main,1777997762540,e34bfe04d74b4940da23b0edef8c5d374a152af44181fc4904f4f67a9711c253
|
||||
.git/logs/refs/remotes/origin/HEAD,1773830493702,1eba2cff5035849e216a15d3b6013593fa5ef345a8d76bb2881d83b3cb247576
|
||||
.git/logs/refs/heads/main,1777903218136,0e77927c7f96859d395e49b6ea9f8d7f54c6e6c60d00fef26fbd90459c5fe112
|
||||
.git/logs/refs/heads/main,1777997762143,f1258009669fe6da86a2aee96326d56f7d477b661aad447ff3bdaabbf9701c91
|
||||
.git/info/exclude,1773160274653,a362e375cc3330f10d115cfeb0f90a325219d80a764d57e2c4873f78d1d0b4f5
|
||||
.git/hooks/update.sample,1773160274656,2b0a4f42fa30a128b46ad80e89c1f73b89d58b8abb9e92aee1c35625baccb584
|
||||
.git/hooks/sendemail-validate.sample,1773160274654,4d0768bc11017be6b99d4bb4d34b4c8b2fd7ae8a93d42727591afb6737577db2
|
||||
@@ -92,5 +102,4 @@ README.md,1777387819540,1b62e096bfae58e52be118bb6e3ed79117a3c919af031d924821394e
|
||||
.git/hooks/fsmonitor-watchman.sample,1773160274655,d366d691e33458260d77c44be36050a3faf0aa12760955cc8ca85ee88389c400
|
||||
.git/hooks/commit-msg.sample,1773160274654,4df962ba3955944bec38b211351c73f083d7b0e5360a5d3d76a49548e7314f9e
|
||||
.git/hooks/applypatch-msg.sample,1773160274655,91b94f5feaf0e4d2e6e7808a9188384a4300adf024fa24c48547ee87c64d6558
|
||||
.git/FETCH_HEAD,1777997077083,143159fa1a2fe0eca23473e5ac2923426b9a541be39e3eda8059a80f58991b05
|
||||
index.html,1777996984662,b1d6a4fbac086eece329ec7f5243c5d834ee206096ecd3730ae7f35817b41c1b
|
||||
.git/FETCH_HEAD,1778067520987,8dd09ee3bc8bc73b170ad713c1b297d159c64332b8fc924178df7c320512fa9a
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"condomaster-pro-ed9af": {
|
||||
"hosting": {
|
||||
"condomaster": [
|
||||
"condomaster-pro-web"
|
||||
"mycondominium-web"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
@@ -1,3 +1,3 @@
|
||||
{
|
||||
"liveServer.settings.port": 5504
|
||||
"liveServer.settings.port": 5505
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
# CondoMasterResults
|
||||
# MyCondominium
|
||||
|
||||
Uma plataforma moderna de gestão de condomínios focada em transparência, comunicação em tempo real e facilidade de uso, tanto para moradores como para a administração.
|
||||
|
||||
## 🎯 Sobre o Projeto
|
||||
|
||||
O **CondoMasterResults** é uma *Single Page Application* concebida para digitalizar a gestão do dia a dia num condomínio. Permite aos moradores consultar despesas, reservar espaços comuns e reportar ocorrências, enquanto oferece aos administradores um painel de controlo completo sobre as finanças e os utilizadores.
|
||||
O **MyCondominium** é uma *Single Page Application* concebida para digitalizar a gestão do dia a dia num condomínio. Permite aos moradores consultar despesas, reservar espaços comuns e reportar ocorrências, enquanto oferece aos administradores um painel de controlo completo sobre as finanças e os utilizadores.
|
||||
|
||||
## 🚀 Funcionalidades Chave
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Relatório Técnico - CondoMaster Pro
|
||||
# Relatório Técnico - MyCondominium
|
||||
|
||||
## 1. Visão Geral do Projeto
|
||||
O **CondoMaster Pro** é uma aplicação web moderna dedicada à gestão de condomínios. Permite uma interação contínua entre administradores e moradores, fornecendo ferramentas para gestão de quotas, ocorrências de manutenção, reservas de espaços comuns, faturação e comunicação em tempo real (chat privado, global e em grupo).
|
||||
O **MyCondominium** é uma aplicação web moderna dedicada à gestão de condomínios. Permite uma interação contínua entre administradores e moradores, fornecendo ferramentas para gestão de quotas, ocorrências de manutenção, reservas de espaços comuns, faturação e comunicação em tempo real (chat privado, global e em grupo).
|
||||
|
||||
---
|
||||
|
||||
@@ -46,7 +46,7 @@ Gere os logins e registos. Garante que apenas utilizadores validados acedem à p
|
||||
|
||||
A aplicação está hospedada no **Firebase Hosting**, uma rede de distribuição global (CDN) ultrarrápida.
|
||||
|
||||
* **URL Oficial Atual:** [https://condomaster-pro-web.web.app](https://condomaster-pro-web.web.app)
|
||||
* **URL Oficial Atual:** [https://mycondominium-web.web.app](https://mycondominium-web.web.app)
|
||||
* **Configuração de Segurança e Performance (`firebase.json`):**
|
||||
* **Cache-Control:** Os recursos estáticos têm instruções para ficar na cache dos browsers durante 1 ano, permitindo que a app carregue quase instantaneamente após a primeira visita.
|
||||
* **Rewrites (SPA):** Qualquer URL acedido é reencaminhado para o `index.html`. Isto previne que ocorra um Erro 404 se um utilizador fizer "Refresh" no browser.
|
||||
|
||||
@@ -14,6 +14,15 @@
|
||||
}
|
||||
],
|
||||
"headers": [
|
||||
{
|
||||
"source": "**/*.html",
|
||||
"headers": [
|
||||
{
|
||||
"key": "Cache-Control",
|
||||
"value": "no-cache, no-store, must-revalidate"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"source": "**/*.@(js|css|png|jpg|jpeg|gif|svg|woff|woff2)",
|
||||
"headers": [
|
||||
|
||||
151
index.html
151
index.html
@@ -5,7 +5,7 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="theme-color" content="#0f172a">
|
||||
<title>CondoMaster Pro</title>
|
||||
<title>MyCondominium</title>
|
||||
<link rel="manifest" href="./manifest.json">
|
||||
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
@@ -100,10 +100,41 @@
|
||||
Dumbbell, PartyPopper, Trophy, Map, Calendar, MapPin, Info,
|
||||
MessageCircle, Paperclip, Send
|
||||
} 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, update } from 'https://www.gstatic.com/firebasejs/12.1.0/firebase-database.js';
|
||||
|
||||
class ErrorBoundary extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = { hasError: false, error: null, errorInfo: null };
|
||||
}
|
||||
static getDerivedStateFromError(error) {
|
||||
return { hasError: true };
|
||||
}
|
||||
componentDidCatch(error, errorInfo) {
|
||||
this.setState({ error, errorInfo });
|
||||
console.error("ErrorBoundary caught an error:", error, errorInfo);
|
||||
}
|
||||
render() {
|
||||
if (this.state.hasError) {
|
||||
return (
|
||||
<div style={{ padding: '20px', backgroundColor: '#fee2e2', color: '#991b1b', fontFamily: 'sans-serif', height: '100vh' }}>
|
||||
<h1 style={{ fontSize: '24px', fontWeight: 'bold' }}>Algo correu mal (Erro na Aplicação)</h1>
|
||||
<pre style={{ marginTop: '20px', whiteSpace: 'pre-wrap', backgroundColor: '#fef2f2', padding: '15px', border: '1px solid #f87171' }}>
|
||||
{this.state.error && this.state.error.toString()}
|
||||
<br />
|
||||
{this.state.errorInfo && this.state.errorInfo.componentStack}
|
||||
</pre>
|
||||
<button onClick={() => window.location.reload()} style={{ marginTop: '20px', padding: '10px 20px', backgroundColor: '#dc2626', color: 'white', border: 'none', borderRadius: '5px', cursor: 'pointer' }}>Recarregar Página</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return this.props.children;
|
||||
}
|
||||
}
|
||||
|
||||
const auth = getAuth(app);
|
||||
const db = getDatabase(app);
|
||||
|
||||
@@ -326,7 +357,7 @@
|
||||
<div className="inline-flex p-3 bg-blue-100 dark:bg-blue-900/30 rounded-full mb-4 text-blue-600 dark:text-blue-400">
|
||||
<Building2 size={32} />
|
||||
</div>
|
||||
<h1 className="text-2xl font-bold text-slate-800 dark:text-white">CondoMaster<span className="text-blue-600">Pro</span></h1>
|
||||
<h1 className="text-2xl font-bold text-slate-800 dark:text-white">MyCondominium</h1>
|
||||
<p className="text-slate-500 dark:text-gray-400 mt-2">Portal de Gestão</p>
|
||||
</div>
|
||||
|
||||
@@ -501,7 +532,17 @@
|
||||
return onValue(ref(db, path), (snapshot) => {
|
||||
const data = snapshot.val();
|
||||
if (data) {
|
||||
let parsed = Object.entries(data).map(([id, val]) => ({ id, ...val }));
|
||||
let parsed = Object.entries(data).map(([id, val]) => {
|
||||
if (path === 'faturas' && val.status === 'Em Validação') {
|
||||
return { id, ...val, status: 'Pago' };
|
||||
}
|
||||
return { id, ...val };
|
||||
});
|
||||
|
||||
if (userRole !== 'admin' && (path === 'manutencao' || path === 'reservas')) {
|
||||
parsed = parsed.filter(item => item.moradorId === currentUserId);
|
||||
}
|
||||
|
||||
if (sortFunc) parsed = parsed.sort(sortFunc);
|
||||
setter(parsed);
|
||||
} else {
|
||||
@@ -799,7 +840,7 @@
|
||||
}
|
||||
try {
|
||||
const newIssueRef = push(ref(db, 'manutencao'));
|
||||
await set(newIssueRef, { ...formData });
|
||||
await set(newIssueRef, { ...formData, moradorId: currentUserId });
|
||||
|
||||
sendSystemNotification(`Nova ocorrência reportada: ${formData.title} (${formData.location})`, 'warning', 'admin');
|
||||
if (userRole !== 'admin') {
|
||||
@@ -853,10 +894,17 @@
|
||||
|
||||
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");
|
||||
await set(ref(db, `faturas/${fatura.id}/status`), 'Pago');
|
||||
|
||||
const morador = residents.find(r => r.id === fatura.moradorId);
|
||||
if (morador) {
|
||||
let newPending = (Number(morador.pending) || 0) - Number(fatura.valor);
|
||||
if (newPending < 0) newPending = 0;
|
||||
await set(ref(db, `condominos/${morador.id}/pending`), newPending);
|
||||
}
|
||||
sendSystemNotification(`O pagamento da sua fatura de ${fatura.categoria} foi concluído!`, 'success', fatura.moradorId);
|
||||
sendSystemNotification(`Pagamento registado para a fatura de ${fatura.categoria} da fração ${morador?.unit || fatura.fracao}.`, 'success', 'admin');
|
||||
showNotification("Pagamento efetuado com sucesso!", "success");
|
||||
} catch (error) {
|
||||
console.error("Erro ao pagar fatura:", error);
|
||||
showNotification("Erro ao processar pagamento.", "error");
|
||||
@@ -907,7 +955,8 @@
|
||||
const bookingData = {
|
||||
...formData,
|
||||
facilityName: facilityNames[formData.facility],
|
||||
status: 'Confirmado'
|
||||
status: 'Confirmado',
|
||||
moradorId: currentUserId
|
||||
};
|
||||
|
||||
const newBookingRef = push(ref(db, 'reservas'));
|
||||
@@ -1269,7 +1318,7 @@
|
||||
<InputGroup label="Cargo" value="Síndico / Gestor" disabled />
|
||||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<InputGroup label="Email" value="admin@condomaster.pt" type="email" />
|
||||
<InputGroup label="Email" value="admin@mycondominium.pt" type="email" />
|
||||
<InputGroup label="Telefone" value="+351 912 345 678" />
|
||||
</div>
|
||||
<InputGroup label="Morada (Sede)" value="Rua das Flores, nº 123, Escritório 2B" />
|
||||
@@ -1422,7 +1471,7 @@
|
||||
<Building2 size={24} />
|
||||
</div>
|
||||
<div>
|
||||
<h1 className="text-xl font-bold text-slate-800 dark:text-white">CondoMaster<span className="text-blue-600 dark:text-blue-400">Pro</span></h1>
|
||||
<h1 className="text-xl font-bold text-slate-800 dark:text-white">MyCondominium</h1>
|
||||
<p className="text-xs text-slate-400 dark:text-dark-mute">Portal de Gestão</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1639,8 +1688,8 @@
|
||||
{activeTab === 'approvals' && userRole === 'admin' && (
|
||||
<div className="p-6">
|
||||
<div className="mb-6">
|
||||
<h2 className="text-2xl font-bold text-slate-800 dark:text-white">Aprovações de Pagamentos</h2>
|
||||
<p className="text-slate-500 dark:text-dark-mute">Valide ou rejeite pagamentos de faturas enviados pelos condóminos.</p>
|
||||
<h2 className="text-2xl font-bold text-slate-800 dark:text-white">Pagamentos Concluídos</h2>
|
||||
<p className="text-slate-500 dark:text-dark-mute">Consulte o histórico de todos os pagamentos concluídos pelos condóminos.</p>
|
||||
</div>
|
||||
<div className="bg-white dark:bg-dark-surface rounded-xl shadow-sm border border-slate-100 dark:border-dark-border overflow-hidden">
|
||||
<div className="overflow-x-auto">
|
||||
@@ -1649,12 +1698,12 @@
|
||||
<tr className="bg-slate-50 dark:bg-dark-bg border-b border-slate-100 dark:border-dark-border text-sm font-semibold text-slate-500 dark:text-slate-400">
|
||||
<th className="p-4">Morador</th>
|
||||
<th className="p-4">Fatura</th>
|
||||
<th className="p-4">Valor</th>
|
||||
<th className="p-4 text-center">Ações</th>
|
||||
<th className="p-4">Estado</th>
|
||||
<th className="p-4 text-right">Valor</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{faturas.filter(f => f.status === 'Em Validação').map(fatura => (
|
||||
{faturas.filter(f => f.status === 'Pago').map(fatura => (
|
||||
<tr key={fatura.id} className="border-b border-slate-50 dark:border-dark-border hover:bg-slate-50/50 dark:hover:bg-dark-bg/50">
|
||||
<td className="p-4">
|
||||
<p className="font-semibold text-slate-700 dark:text-slate-200">{fatura.nomeMorador}</p>
|
||||
@@ -1662,32 +1711,18 @@
|
||||
</td>
|
||||
<td className="p-4 text-slate-600 dark:text-slate-400">
|
||||
<p className="text-sm">{fatura.categoria}</p>
|
||||
<p className="text-xs">Vence: {fatura.dataVencimento}</p>
|
||||
<p className="text-xs">Venceu a: {fatura.dataVencimento}</p>
|
||||
</td>
|
||||
<td className="p-4 font-bold text-slate-800 dark:text-slate-200">{Number(fatura.valor).toFixed(2)}€</td>
|
||||
<td className="p-4">
|
||||
<div className="flex justify-center gap-2">
|
||||
<button onClick={() => {
|
||||
if(window.confirm('Aprovar o pagamento desta fatura?')) {
|
||||
handleApproveFatura(fatura);
|
||||
}
|
||||
}} 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 Pagamento">
|
||||
<CheckCircle size={18} />
|
||||
</button>
|
||||
<button onClick={() => {
|
||||
if(window.confirm('Rejeitar este pagamento?')) {
|
||||
set(ref(db, `faturas/${fatura.id}/status`), 'Pendente');
|
||||
showNotification('Pagamento rejeitado.', 'warning');
|
||||
}
|
||||
}} className="p-2 bg-red-100 text-red-600 rounded hover:bg-red-200 dark:bg-red-900/30 dark:text-red-400" title="Rejeitar Pagamento">
|
||||
<X size={18} />
|
||||
</button>
|
||||
<td className="p-4 text-slate-600 dark:text-slate-400">
|
||||
<div className="inline-flex items-center gap-1.5 px-2 py-1 rounded bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400 text-xs font-medium">
|
||||
<CheckCircle size={14} /> Pago
|
||||
</div>
|
||||
</td>
|
||||
<td className="p-4 font-bold text-green-600 dark:text-green-400 text-right">{Number(fatura.valor).toFixed(2)}€</td>
|
||||
</tr>
|
||||
))}
|
||||
{faturas.filter(f => f.status === 'Em Validação').length === 0 && (
|
||||
<tr><td colSpan="4" className="p-8 text-center text-slate-500">Nenhum pagamento pendente de aprovação.</td></tr>
|
||||
{faturas.filter(f => f.status === 'Pago').length === 0 && (
|
||||
<tr><td colSpan="4" className="p-8 text-center text-slate-500">Nenhum pagamento concluído encontrado.</td></tr>
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -1915,10 +1950,6 @@
|
||||
>
|
||||
Pagar
|
||||
</button>
|
||||
) : fatura.status === 'Em Validação' ? (
|
||||
<span className="text-orange-500 text-xs font-bold flex items-center justify-center gap-1">
|
||||
<Clock size={14} /> Em Validação
|
||||
</span>
|
||||
) : (
|
||||
<span className="text-slate-400 text-xs font-bold flex items-center justify-center gap-1">
|
||||
<CheckCircle size={14} /> Pago
|
||||
@@ -1971,12 +2002,32 @@
|
||||
<h3 className="font-bold text-lg text-slate-800 dark:text-white">Diário Financeiro</h3>
|
||||
<span className="text-xs bg-slate-100 dark:bg-dark-bg text-slate-600 dark:text-slate-400 px-2 py-1 rounded-full">{finances.length} movimentos</span>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => handleOpenModal('finance')}
|
||||
className="bg-slate-900 dark:bg-slate-700 text-white px-4 py-2 rounded-lg text-sm font-medium hover:bg-slate-800 dark:hover:bg-slate-600 flex items-center gap-2 shadow-lg hover:shadow-xl transition-all"
|
||||
>
|
||||
<Plus size={18} /> Novo Registo
|
||||
</button>
|
||||
<div className="flex items-center gap-3">
|
||||
{finances.length === 0 && (
|
||||
<button
|
||||
onClick={async () => {
|
||||
try {
|
||||
for (const item of INITIAL_FINANCES) {
|
||||
await set(push(ref(db, 'financas')), item);
|
||||
}
|
||||
showNotification("Dados de exemplo restaurados com sucesso!", "success");
|
||||
} catch (error) {
|
||||
console.error("Erro ao restaurar:", error);
|
||||
showNotification("Erro ao restaurar.", "error");
|
||||
}
|
||||
}}
|
||||
className="bg-orange-500 text-white px-4 py-2 rounded-lg text-sm font-medium hover:bg-orange-600 transition-colors shadow-sm flex items-center gap-2"
|
||||
>
|
||||
<Wrench size={16} /> Restaurar Base de Dados
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
onClick={() => handleOpenModal('finance')}
|
||||
className="bg-slate-900 dark:bg-slate-700 text-white px-4 py-2 rounded-lg text-sm font-medium hover:bg-slate-800 dark:hover:bg-slate-600 flex items-center gap-2 shadow-lg hover:shadow-xl transition-all"
|
||||
>
|
||||
<Plus size={18} /> Novo Registo
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="overflow-auto flex-1">
|
||||
<table className="w-full text-sm text-left">
|
||||
@@ -2350,7 +2401,11 @@
|
||||
}
|
||||
|
||||
const root = createRoot(document.getElementById('root'));
|
||||
root.render(<App />);
|
||||
root.render(
|
||||
<ErrorBoundary>
|
||||
<App />
|
||||
</ErrorBoundary>
|
||||
);
|
||||
</script>
|
||||
<!-- Firebase configs moved to top in React Module -->
|
||||
</body>
|
||||
|
||||
38
sw.js
38
sw.js
@@ -1,4 +1,4 @@
|
||||
const CACHE_NAME = 'condopro-v1';
|
||||
const CACHE_NAME = 'mycondominium-v3';
|
||||
const ASSETS_TO_CACHE = [
|
||||
'./',
|
||||
'./index.html',
|
||||
@@ -14,15 +14,43 @@ const ASSETS_TO_CACHE = [
|
||||
];
|
||||
|
||||
self.addEventListener('install', (event) => {
|
||||
self.skipWaiting(); // Force the waiting service worker to become the active service worker.
|
||||
event.waitUntil(
|
||||
caches.open(CACHE_NAME)
|
||||
.then((cache) => cache.addAll(ASSETS_TO_CACHE))
|
||||
);
|
||||
});
|
||||
|
||||
self.addEventListener('fetch', (event) => {
|
||||
event.respondWith(
|
||||
caches.match(event.request)
|
||||
.then((response) => response || fetch(event.request))
|
||||
self.addEventListener('activate', (event) => {
|
||||
event.waitUntil(clients.claim()); // Claim clients immediately so updates are visible without reloading all tabs
|
||||
event.waitUntil(
|
||||
caches.keys().then((cacheNames) => {
|
||||
return Promise.all(
|
||||
cacheNames.map((cacheName) => {
|
||||
if (cacheName !== CACHE_NAME) {
|
||||
return caches.delete(cacheName);
|
||||
}
|
||||
})
|
||||
);
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
self.addEventListener('fetch', (event) => {
|
||||
// Network First strategy: try network, if it fails, fallback to cache
|
||||
event.respondWith(
|
||||
fetch(event.request).then((networkResponse) => {
|
||||
// If request is successful, update the cache
|
||||
if (networkResponse && networkResponse.status === 200 && event.request.method === 'GET') {
|
||||
const responseToCache = networkResponse.clone();
|
||||
caches.open(CACHE_NAME).then((cache) => {
|
||||
cache.put(event.request, responseToCache);
|
||||
});
|
||||
}
|
||||
return networkResponse;
|
||||
}).catch(() => {
|
||||
// If network fails (offline), return cached version
|
||||
return caches.match(event.request);
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
2335
temp_script.jsx
Normal file
2335
temp_script.jsx
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user