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:
2025-12-07 12:54:51 +01:00
parent ce53d2ea09
commit 9459834b0e
6 changed files with 56 additions and 80 deletions

Binary file not shown.

View File

@@ -1 +0,0 @@
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

View File

@@ -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;"]

View File

@@ -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;
}
}

View File

@@ -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>
)} )}

View File

@@ -1,8 +0,0 @@
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3001
CMD ["npm", "start"]