Update FamilyList.tsx
This commit is contained in:
@@ -18,9 +18,12 @@ export const FamilyList: React.FC = () => {
|
|||||||
// User Dashboard Data
|
// User Dashboard Data
|
||||||
const [myTickets, setMyTickets] = useState<Ticket[]>([]);
|
const [myTickets, setMyTickets] = useState<Ticket[]>([]);
|
||||||
const [myExtraExpenses, setMyExtraExpenses] = useState<any[]>([]);
|
const [myExtraExpenses, setMyExtraExpenses] = useState<any[]>([]);
|
||||||
const [myRegularDebt, setMyRegularDebt] = useState<number>(0);
|
|
||||||
const [myFamily, setMyFamily] = useState<Family | null>(null);
|
const [myFamily, setMyFamily] = useState<Family | null>(null);
|
||||||
|
|
||||||
|
// Payment Status State
|
||||||
|
const [regularPaymentStatus, setRegularPaymentStatus] = useState<'OK' | 'PENDING' | 'OVERDUE'>('OK');
|
||||||
|
const [regularDebtAmount, setRegularDebtAmount] = useState<number>(0);
|
||||||
|
|
||||||
const currentUser = CondoService.getCurrentUser();
|
const currentUser = CondoService.getCurrentUser();
|
||||||
const isPrivileged = currentUser?.role === 'admin' || currentUser?.role === 'poweruser';
|
const isPrivileged = currentUser?.role === 'admin' || currentUser?.role === 'poweruser';
|
||||||
|
|
||||||
@@ -54,34 +57,68 @@ export const FamilyList: React.FC = () => {
|
|||||||
setMyExtraExpenses(extra);
|
setMyExtraExpenses(extra);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. Calculate Regular Debt (Current Year)
|
// 4. Calculate Regular Payment Status (Logic Update)
|
||||||
const payments = await CondoService.getPaymentsByFamily(currentUser.familyId);
|
const payments = await CondoService.getPaymentsByFamily(currentUser.familyId);
|
||||||
const currentYear = appSettings.currentYear;
|
const currentYear = appSettings.currentYear;
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
const currentMonth = now.getFullYear() === currentYear ? now.getMonth() + 1 : (now.getFullYear() > currentYear ? 12 : 0);
|
const currentRealYear = now.getFullYear();
|
||||||
|
const currentRealMonth = now.getMonth() + 1; // 1-12
|
||||||
let debt = 0;
|
const currentDay = now.getDate();
|
||||||
|
const dueDay = condo.dueDay || 10;
|
||||||
const quota = me?.customMonthlyQuota ?? condo.defaultMonthlyQuota;
|
const quota = me?.customMonthlyQuota ?? condo.defaultMonthlyQuota;
|
||||||
|
|
||||||
for (let m = 1; m <= currentMonth; m++) {
|
let totalDebt = 0;
|
||||||
|
let status: 'OK' | 'PENDING' | 'OVERDUE' = 'OK';
|
||||||
|
|
||||||
|
// Check previous months first (Always Overdue if unpaid)
|
||||||
|
for (let m = 1; m < currentRealMonth; m++) {
|
||||||
|
// Simplified: assuming user only cares about current active year for dashboard alert
|
||||||
|
// In a real app, check past years too.
|
||||||
const isPaid = payments.some(p => p.forMonth === m && p.forYear === currentYear);
|
const isPaid = payments.some(p => p.forMonth === m && p.forYear === currentYear);
|
||||||
if (!isPaid) debt += quota;
|
if (!isPaid) {
|
||||||
|
totalDebt += quota;
|
||||||
|
status = 'OVERDUE';
|
||||||
}
|
}
|
||||||
setMyRegularDebt(debt);
|
}
|
||||||
|
|
||||||
|
// Check current month
|
||||||
|
const isCurrentMonthPaid = payments.some(p => p.forMonth === currentRealMonth && p.forYear === currentYear);
|
||||||
|
if (!isCurrentMonthPaid) {
|
||||||
|
// If today > dueDay -> Overdue
|
||||||
|
if (currentDay > dueDay) {
|
||||||
|
totalDebt += quota;
|
||||||
|
status = 'OVERDUE';
|
||||||
|
}
|
||||||
|
// If today is within 10 days before dueDay -> Pending
|
||||||
|
else if (currentDay >= (dueDay - 10)) {
|
||||||
|
totalDebt += quota; // It's due soon, so we count it
|
||||||
|
if (status !== 'OVERDUE') status = 'PENDING';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setRegularDebtAmount(totalDebt);
|
||||||
|
setRegularPaymentStatus(status);
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- NOTICE LOGIC ---
|
// --- NOTICE LOGIC ---
|
||||||
|
// Ensure notices are loaded even if user has no family yet (but belongs to condo)
|
||||||
if (condo && currentUser && appSettings.features.notices) {
|
if (condo && currentUser && appSettings.features.notices) {
|
||||||
const condoNotices = allNotices.filter(n => {
|
const condoNotices = allNotices.filter(n => {
|
||||||
if (n.condoId !== condo.id || !n.active) return false;
|
if (n.condoId !== condo.id || !n.active) return false;
|
||||||
|
|
||||||
|
// Admin sees all active
|
||||||
if (isPrivileged) return true;
|
if (isPrivileged) return true;
|
||||||
const hasTargets = n.targetFamilyIds && n.targetFamilyIds.length > 0;
|
|
||||||
if (!hasTargets) return true;
|
// Public notices (targetFamilyIds is null/empty)
|
||||||
return currentUser.familyId && n.targetFamilyIds?.includes(currentUser.familyId);
|
if (!n.targetFamilyIds || n.targetFamilyIds.length === 0) return true;
|
||||||
|
|
||||||
|
// Targeted notices
|
||||||
|
return currentUser.familyId && n.targetFamilyIds.includes(currentUser.familyId);
|
||||||
});
|
});
|
||||||
|
|
||||||
setNotices(condoNotices);
|
setNotices(condoNotices);
|
||||||
|
|
||||||
|
// Check read status
|
||||||
const readStatuses = await Promise.all(condoNotices.map(n => CondoService.getNoticeReadStatus(n.id)));
|
const readStatuses = await Promise.all(condoNotices.map(n => CondoService.getNoticeReadStatus(n.id)));
|
||||||
const readIds: string[] = [];
|
const readIds: string[] = [];
|
||||||
readStatuses.forEach((reads, idx) => {
|
readStatuses.forEach((reads, idx) => {
|
||||||
@@ -99,7 +136,7 @@ export const FamilyList: React.FC = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
fetchData();
|
fetchData();
|
||||||
}, [currentUser?.id, isPrivileged]); // Dependencies
|
}, [currentUser?.id, isPrivileged]);
|
||||||
|
|
||||||
const filteredFamilies = families.filter(f =>
|
const filteredFamilies = families.filter(f =>
|
||||||
f.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
f.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||||
@@ -115,7 +152,6 @@ export const FamilyList: React.FC = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Dashboard Calculations
|
|
||||||
const activeTicketsCount = myTickets.filter(t => t.status !== TicketStatus.RESOLVED && t.status !== TicketStatus.CLOSED).length;
|
const activeTicketsCount = myTickets.filter(t => t.status !== TicketStatus.RESOLVED && t.status !== TicketStatus.CLOSED).length;
|
||||||
const extraDebt = myExtraExpenses.reduce((acc, exp) => acc + Math.max(0, exp.myShare.amountDue - exp.myShare.amountPaid), 0);
|
const extraDebt = myExtraExpenses.reduce((acc, exp) => acc + Math.max(0, exp.myShare.amountDue - exp.myShare.amountPaid), 0);
|
||||||
|
|
||||||
@@ -136,7 +172,7 @@ export const FamilyList: React.FC = () => {
|
|||||||
return (
|
return (
|
||||||
<div className="space-y-8 pb-12 animate-fade-in">
|
<div className="space-y-8 pb-12 animate-fade-in">
|
||||||
|
|
||||||
{/* 1. NOTICES (Bacheca) - High Priority */}
|
{/* 1. NOTICES (Bacheca) - ALWAYS VISIBLE IF ENABLED */}
|
||||||
{settings?.features.notices && notices.length > 0 && (
|
{settings?.features.notices && notices.length > 0 && (
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<h3 className="font-bold text-slate-700 flex items-center gap-2">
|
<h3 className="font-bold text-slate-700 flex items-center gap-2">
|
||||||
@@ -182,24 +218,37 @@ export const FamilyList: React.FC = () => {
|
|||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||||
|
|
||||||
{/* Regular Payments Widget */}
|
{/* Regular Payments Widget */}
|
||||||
<div className={`p-5 rounded-xl border shadow-sm flex flex-col justify-between ${myRegularDebt > 0 ? 'bg-white border-red-200' : 'bg-white border-slate-200'}`}>
|
<div className={`p-5 rounded-xl border shadow-sm flex flex-col justify-between ${
|
||||||
|
regularPaymentStatus === 'OVERDUE' ? 'bg-white border-red-300 ring-1 ring-red-100' :
|
||||||
|
regularPaymentStatus === 'PENDING' ? 'bg-white border-yellow-300 ring-1 ring-yellow-100' :
|
||||||
|
'bg-white border-slate-200'
|
||||||
|
}`}>
|
||||||
<div>
|
<div>
|
||||||
<div className="flex justify-between items-start mb-2">
|
<div className="flex justify-between items-start mb-2">
|
||||||
<div className={`p-2 rounded-lg ${myRegularDebt > 0 ? 'bg-red-50 text-red-600' : 'bg-green-50 text-green-600'}`}>
|
<div className={`p-2 rounded-lg ${
|
||||||
|
regularPaymentStatus === 'OVERDUE' ? 'bg-red-50 text-red-600' :
|
||||||
|
regularPaymentStatus === 'PENDING' ? 'bg-yellow-50 text-yellow-600' :
|
||||||
|
'bg-green-50 text-green-600'
|
||||||
|
}`}>
|
||||||
<Wallet className="w-6 h-6" />
|
<Wallet className="w-6 h-6" />
|
||||||
</div>
|
</div>
|
||||||
{myRegularDebt > 0 && <span className="bg-red-100 text-red-700 text-[10px] font-bold px-2 py-1 rounded uppercase">Insoluto</span>}
|
{regularPaymentStatus === 'OVERDUE' && <span className="bg-red-100 text-red-700 text-[10px] font-bold px-2 py-1 rounded uppercase">Insoluto</span>}
|
||||||
|
{regularPaymentStatus === 'PENDING' && <span className="bg-yellow-100 text-yellow-700 text-[10px] font-bold px-2 py-1 rounded uppercase">In Scadenza</span>}
|
||||||
</div>
|
</div>
|
||||||
<p className="text-slate-500 text-xs font-bold uppercase tracking-wide">Rate Condominiali {settings?.currentYear}</p>
|
<p className="text-slate-500 text-xs font-bold uppercase tracking-wide">Rate Condominiali {settings?.currentYear}</p>
|
||||||
<h4 className={`text-2xl font-bold mt-1 ${myRegularDebt > 0 ? 'text-red-600' : 'text-slate-800'}`}>
|
<h4 className={`text-2xl font-bold mt-1 ${
|
||||||
{myRegularDebt > 0 ? `€ -${myRegularDebt.toFixed(2)}` : 'In Regola'}
|
regularPaymentStatus === 'OVERDUE' ? 'text-red-600' :
|
||||||
|
regularPaymentStatus === 'PENDING' ? 'text-yellow-600' :
|
||||||
|
'text-slate-800'
|
||||||
|
}`}>
|
||||||
|
{regularDebtAmount > 0 ? `€ -${regularDebtAmount.toFixed(2)}` : 'In Regola'}
|
||||||
</h4>
|
</h4>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={() => navigate(`/family/${myFamily.id}`)}
|
onClick={() => navigate(`/family/${myFamily.id}`)}
|
||||||
className="mt-4 w-full py-2 bg-slate-50 hover:bg-slate-100 text-slate-700 rounded-lg text-sm font-medium flex items-center justify-center gap-2 transition-colors"
|
className="mt-4 w-full py-2 bg-slate-50 hover:bg-slate-100 text-slate-700 rounded-lg text-sm font-medium flex items-center justify-center gap-2 transition-colors"
|
||||||
>
|
>
|
||||||
{myRegularDebt > 0 ? 'Paga Ora' : 'Vedi Storico'} <ArrowRight className="w-4 h-4"/>
|
{regularDebtAmount > 0 ? 'Paga Ora' : 'Vedi Storico'} <ArrowRight className="w-4 h-4"/>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user