fix: Improve condo ID handling and add error message
Prevents client-side generation of condo IDs, delegating this to the backend/service for consistency. Introduces an alert for save condo errors and ensures the active condo is correctly updated or set. Adds logic to auto-select the first condo if none is active.
This commit is contained in:
BIN
.dockerignore
BIN
.dockerignore
Binary file not shown.
15
Dockerfile
15
Dockerfile
@@ -1,15 +0,0 @@
|
|||||||
# Stage 1: Build del frontend
|
|
||||||
FROM node:20-alpine as build
|
|
||||||
WORKDIR /app
|
|
||||||
COPY package*.json ./
|
|
||||||
RUN npm install
|
|
||||||
COPY . .
|
|
||||||
RUN npm run build
|
|
||||||
|
|
||||||
# Stage 2: Serve con Nginx
|
|
||||||
FROM nginx:alpine
|
|
||||||
COPY --from=build /app/dist /usr/share/nginx/html
|
|
||||||
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
|
||||||
EXPOSE 80
|
|
||||||
CMD ["nginx", "-g", "daemon off;"]
|
|
||||||
|
|
||||||
|
|||||||
23
nginx.conf
23
nginx.conf
@@ -1,22 +1 @@
|
|||||||
server {
|
<EFBFBD><EFBFBD><EFBFBD>z
|
||||||
listen 80;
|
|
||||||
|
|
||||||
# Serve i file statici del frontend
|
|
||||||
location / {
|
|
||||||
root /usr/share/nginx/html;
|
|
||||||
index index.html index.htm;
|
|
||||||
# Fondamentale per il routing client-side di React (SPA)
|
|
||||||
try_files $uri $uri/ /index.html;
|
|
||||||
}
|
|
||||||
|
|
||||||
# Proxy verso il backend
|
|
||||||
location /api {
|
|
||||||
# 'backend' è il nome del servizio definito nel docker-compose.yml
|
|
||||||
proxy_pass http://backend:3001;
|
|
||||||
proxy_http_version 1.1;
|
|
||||||
proxy_set_header Upgrade $http_upgrade;
|
|
||||||
proxy_set_header Connection 'upgrade';
|
|
||||||
proxy_set_header Host $host;
|
|
||||||
proxy_cache_bypass $http_upgrade;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -224,20 +224,30 @@ export const SettingsPage: React.FC = () => {
|
|||||||
const handleCondoSubmit = async (e: React.FormEvent) => {
|
const handleCondoSubmit = async (e: React.FormEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
try {
|
try {
|
||||||
|
// FIX: Do not generate ID for new condo, let backend/service handle it (POST vs PUT check)
|
||||||
const payload: Condo = {
|
const payload: Condo = {
|
||||||
id: editingCondo ? editingCondo.id : crypto.randomUUID(),
|
id: editingCondo ? editingCondo.id : '',
|
||||||
name: condoForm.name,
|
name: condoForm.name,
|
||||||
address: condoForm.address,
|
address: condoForm.address,
|
||||||
defaultMonthlyQuota: condoForm.defaultMonthlyQuota
|
defaultMonthlyQuota: condoForm.defaultMonthlyQuota
|
||||||
};
|
};
|
||||||
|
|
||||||
await CondoService.saveCondo(payload);
|
const savedCondo = await CondoService.saveCondo(payload);
|
||||||
const list = await CondoService.getCondos();
|
const list = await CondoService.getCondos();
|
||||||
setCondos(list);
|
setCondos(list);
|
||||||
if (activeCondo?.id === payload.id) setActiveCondo(payload);
|
|
||||||
|
if (activeCondo?.id === savedCondo.id) {
|
||||||
|
setActiveCondo(savedCondo);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auto-select if it's the first one or none selected
|
||||||
|
if (!activeCondo && list.length === 1) {
|
||||||
|
CondoService.setActiveCondo(savedCondo.id);
|
||||||
|
}
|
||||||
|
|
||||||
setShowCondoModal(false);
|
setShowCondoModal(false);
|
||||||
window.dispatchEvent(new Event('condo-updated'));
|
window.dispatchEvent(new Event('condo-updated'));
|
||||||
} catch (e) { console.error(e); }
|
} catch (e) { console.error(e); alert("Errore nel salvataggio del condominio"); }
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDeleteCondo = async (id: string) => {
|
const handleDeleteCondo = async (id: string) => {
|
||||||
@@ -300,7 +310,10 @@ export const SettingsPage: React.FC = () => {
|
|||||||
setFamilies([...families, newFamily]);
|
setFamilies([...families, newFamily]);
|
||||||
}
|
}
|
||||||
setShowFamilyModal(false);
|
setShowFamilyModal(false);
|
||||||
} catch (e) { console.error(e); }
|
} catch (e: any) {
|
||||||
|
console.error(e);
|
||||||
|
alert(`Errore: ${e.message || "Impossibile salvare la famiglia"}`);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- User Handlers ---
|
// --- User Handlers ---
|
||||||
@@ -359,7 +372,7 @@ export const SettingsPage: React.FC = () => {
|
|||||||
...noticeForm,
|
...noticeForm,
|
||||||
date: editingNotice ? editingNotice.date : new Date().toISOString()
|
date: editingNotice ? editingNotice.date : new Date().toISOString()
|
||||||
};
|
};
|
||||||
const saved = await CondoService.saveNotice(payload);
|
await CondoService.saveNotice(payload);
|
||||||
setNotices(await CondoService.getNotices());
|
setNotices(await CondoService.getNotices());
|
||||||
setShowNoticeModal(false);
|
setShowNoticeModal(false);
|
||||||
} catch (e) { console.error(e); }
|
} catch (e) { console.error(e); }
|
||||||
@@ -383,7 +396,7 @@ export const SettingsPage: React.FC = () => {
|
|||||||
setShowReadDetailsModal(true);
|
setShowReadDetailsModal(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- Alert Handlers (Placeholder for brevity, logic same as before) ---
|
// --- Alert Handlers ---
|
||||||
const openAddAlertModal = () => { setEditingAlert(null); setAlertForm({ subject: '', body: '', daysOffset: 1, offsetType: 'before_next_month', sendHour: 9, active: true }); setShowAlertModal(true); };
|
const openAddAlertModal = () => { setEditingAlert(null); setAlertForm({ subject: '', body: '', daysOffset: 1, offsetType: 'before_next_month', sendHour: 9, active: true }); setShowAlertModal(true); };
|
||||||
const openEditAlertModal = (alert: AlertDefinition) => { setEditingAlert(alert); setAlertForm(alert); setShowAlertModal(true); };
|
const openEditAlertModal = (alert: AlertDefinition) => { setEditingAlert(alert); setAlertForm(alert); setShowAlertModal(true); };
|
||||||
const handleAlertSubmit = async (e: React.FormEvent) => {
|
const handleAlertSubmit = async (e: React.FormEvent) => {
|
||||||
@@ -463,7 +476,7 @@ export const SettingsPage: React.FC = () => {
|
|||||||
{isAdmin && activeTab === 'general' && (
|
{isAdmin && activeTab === 'general' && (
|
||||||
<div className="space-y-6 animate-fade-in">
|
<div className="space-y-6 animate-fade-in">
|
||||||
{!activeCondo ? (
|
{!activeCondo ? (
|
||||||
<div className="bg-amber-50 border border-amber-200 text-amber-800 p-4 rounded-lg">Nessun condominio selezionato.</div>
|
<div className="bg-amber-50 border border-amber-200 text-amber-800 p-4 rounded-lg">Nessun condominio selezionato. Crea un condominio nella sezione "Lista Condomini".</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="bg-white rounded-xl shadow-sm border border-slate-200 p-6 md:p-8 max-w-2xl">
|
<div className="bg-white rounded-xl shadow-sm border border-slate-200 p-6 md:p-8 max-w-2xl">
|
||||||
<h3 className="text-lg font-bold text-slate-800 mb-4 flex items-center gap-2"><Building className="w-5 h-5 text-blue-600" /> Dati Condominio Corrente</h3>
|
<h3 className="text-lg font-bold text-slate-800 mb-4 flex items-center gap-2"><Building className="w-5 h-5 text-blue-600" /> Dati Condominio Corrente</h3>
|
||||||
@@ -514,8 +527,14 @@ export const SettingsPage: React.FC = () => {
|
|||||||
{/* Families Tab */}
|
{/* Families Tab */}
|
||||||
{isAdmin && activeTab === 'families' && (
|
{isAdmin && activeTab === 'families' && (
|
||||||
<div className="space-y-4 animate-fade-in">
|
<div className="space-y-4 animate-fade-in">
|
||||||
|
{!activeCondo ? (
|
||||||
|
<div className="p-8 text-center bg-slate-50 rounded-xl border border-dashed border-slate-300">
|
||||||
|
<p className="text-slate-500">Seleziona o crea un condominio per gestire le famiglie.</p>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
<div className="flex justify-between items-center">
|
<div className="flex justify-between items-center">
|
||||||
<div className="text-sm text-slate-500">Famiglie in: <span className="font-bold text-slate-800">{activeCondo?.name}</span></div>
|
<div className="text-sm text-slate-500">Famiglie in: <span className="font-bold text-slate-800">{activeCondo.name}</span></div>
|
||||||
<button onClick={openAddFamilyModal} className="bg-blue-600 text-white px-4 py-2 rounded-lg font-medium flex items-center gap-2"><Plus className="w-4 h-4" /> Aggiungi</button>
|
<button onClick={openAddFamilyModal} className="bg-blue-600 text-white px-4 py-2 rounded-lg font-medium flex items-center gap-2"><Plus className="w-4 h-4" /> Aggiungi</button>
|
||||||
</div>
|
</div>
|
||||||
<div className="bg-white rounded-xl shadow-sm border border-slate-200 overflow-hidden">
|
<div className="bg-white rounded-xl shadow-sm border border-slate-200 overflow-hidden">
|
||||||
@@ -531,7 +550,7 @@ export const SettingsPage: React.FC = () => {
|
|||||||
{family.customMonthlyQuota ? (
|
{family.customMonthlyQuota ? (
|
||||||
<span className="font-bold text-blue-600">€ {family.customMonthlyQuota}</span>
|
<span className="font-bold text-blue-600">€ {family.customMonthlyQuota}</span>
|
||||||
) : (
|
) : (
|
||||||
<span className="text-slate-400 italic">Default (€ {activeCondo?.defaultMonthlyQuota})</span>
|
<span className="text-slate-400 italic">Default (€ {activeCondo.defaultMonthlyQuota})</span>
|
||||||
)}
|
)}
|
||||||
</td>
|
</td>
|
||||||
<td className="px-6 py-4 text-right"><div className="flex justify-end gap-2"><button onClick={() => openEditFamilyModal(family)} className="text-blue-600"><Pencil className="w-4 h-4" /></button><button onClick={() => handleDeleteFamily(family.id)} className="text-red-600"><Trash2 className="w-4 h-4" /></button></div></td>
|
<td className="px-6 py-4 text-right"><div className="flex justify-end gap-2"><button onClick={() => openEditFamilyModal(family)} className="text-blue-600"><Pencil className="w-4 h-4" /></button><button onClick={() => handleDeleteFamily(family.id)} className="text-red-600"><Trash2 className="w-4 h-4" /></button></div></td>
|
||||||
@@ -540,6 +559,8 @@ export const SettingsPage: React.FC = () => {
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
FROM node:20-alpine
|
|
||||||
WORKDIR /app
|
|
||||||
COPY package*.json ./
|
|
||||||
RUN npm install
|
|
||||||
COPY . .
|
|
||||||
EXPOSE 3001
|
|
||||||
CMD ["npm", "start"]
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user