// Transactions page — full list with filters, search, sort, group-by-day
function TxnsPage() {
const { setDrawerTxn, dataVersion } = useApp();
const [q, setQ] = useState('');
const [catFilter, setCatFilter] = useState('all');
const [sourceFilter, setSourceFilter] = useState('all');
const [sort, setSort] = useState('date-desc');
// Default to current month index (the most recent in D.months[])
const [monthIdx, setMonthIdx] = useState(() => Math.max(0, (D.months?.length || 1) - 1));
// When data refreshes (e.g. after a scrape), keep monthIdx in range and
// jump to the most recent month so the user sees freshly-pulled data.
useEffect(() => {
const len = D.months?.length || 0;
if (len === 0) return;
if (monthIdx >= len) setMonthIdx(len - 1);
}, [dataVersion]);
const months = D.months || [];
const activeMonth = months[monthIdx] || D.current;
const txns = useMemo(() => {
let arr = (activeMonth?.txns || []).slice();
if (q) {
const lq = q.toLowerCase();
arr = arr.filter(t => t.description.toLowerCase().includes(lq) || t.category.includes(q));
}
if (catFilter !== 'all') arr = arr.filter(t => t.category === catFilter);
if (sourceFilter !== 'all') arr = arr.filter(t => t.source === sourceFilter);
if (sort === 'date-desc') arr.sort((a,b) => b.date.localeCompare(a.date));
if (sort === 'date-asc') arr.sort((a,b) => a.date.localeCompare(b.date));
if (sort === 'amount-desc') arr.sort((a,b) => a.amount - b.amount);
if (sort === 'amount-asc') arr.sort((a,b) => b.amount - a.amount);
return arr;
}, [q, catFilter, sourceFilter, sort, monthIdx, dataVersion]);
const grouped = useMemo(() => {
const g = {};
txns.forEach(t => { (g[t.date] ||= []).push(t); });
return Object.entries(g);
}, [txns]);
const totalShown = txns.filter(t => t.amount < 0).reduce((s,t) => s + (-t.amount), 0);
return (
עסקאות
{activeMonth?.label || ''} · {txns.length} עסקאות · סך {fmtILS(totalShown)}
{/* Month picker — horizontal scroll of all loaded months */}
{months.map((m, i) => {
const active = i === monthIdx;
const isEmpty = (m.txns?.length || 0) === 0;
return (
);
})}
{/* Filter bar */}
⌕
setQ(e.target.value)} placeholder="חיפוש לפי שם, סכום, קטגוריה…"
style={{ width: '100%', padding: '8px 32px 8px 12px', border: '0.5px solid var(--rule)',
borderRadius: 9, background: 'var(--bg-deep)', fontSize: 13, outline: 'none' }} />
({value:c, label:c}))]} />
{grouped.length === 0 && }
{grouped.map(([date, items]) => {
const dayTotal = items.filter(t => t.amount < 0).reduce((s,t) => s + (-t.amount), 0);
return (
{fmtDate(date)}
{items.length} · {fmtILS(-dayTotal)}
{items.map(t => {
const c = D.categories[t.category] || {};
return (
setDrawerTxn(t)} style={{
display: 'grid', gridTemplateColumns: '40px 1fr 130px 80px 100px',
gap: 14, alignItems: 'center', padding: '10px 22px',
borderBottom: '0.5px solid var(--rule)', cursor: 'pointer',
}} onMouseEnter={(e) => e.currentTarget.style.background = 'var(--bg-deep)'}
onMouseLeave={(e) => e.currentTarget.style.background = 'transparent'}>
{c.icon}
{t.anomaly && !}
{t.description}
{t.recurring && '↻ קבוע · '}{t.id}
{t.category}
{t.source}
0 ? 'var(--positive)' : 'var(--ink)' }}>
{fmtILS(t.amount)}
);
})}
);
})}
);
}
function FilterSelect({ value, onChange, options }) {
return (
);
}
window.TxnsPage = TxnsPage;