Skip to content

Commit 78ed0e7

Browse files
committed
fixed calendar on /povijest, added filters to all tables
1 parent 2b31cd9 commit 78ed0e7

3 files changed

Lines changed: 130 additions & 50 deletions

File tree

frontend/src/pages/Racuni.jsx

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ export default function Racuni() {
66
const [receipts, setReceipts] = useState([]);
77
const [loading, setLoading] = useState(true);
88
const [searchTerm, setSearchTerm] = useState("");
9+
const [filterPayment, setFilterPayment] = useState('');
10+
const [filterStatus, setFilterStatus] = useState('');
911
const [printData, setPrintData] = useState(null);
1012

1113
useEffect(() => {
@@ -34,14 +36,21 @@ export default function Racuni() {
3436
}
3537
};
3638

39+
const uniquePaymentTypes = [...new Set(receipts.map(r => r.paymentType).filter(Boolean))];
40+
3741
const filteredReceipts = receipts.filter(receipt => {
3842
const searchLower = searchTerm.toLowerCase();
39-
return (
43+
const matchesSearch = (
4044
receipt.invoiceNumber?.toLowerCase().includes(searchLower) ||
4145
receipt.paymentType.toLowerCase().includes(searchLower) ||
4246
receipt.user?.name?.toLowerCase().includes(searchLower) ||
4347
receipt.brutto.toString().includes(searchLower)
4448
);
49+
if (!matchesSearch) return false;
50+
if (filterPayment && receipt.paymentType !== filterPayment) return false;
51+
if (filterStatus === 'STORNO_OTKAZANO' && receipt.status !== 'STORNO' && receipt.status !== 'RACUN_STORNIRAN') return false;
52+
else if (filterStatus && filterStatus !== 'STORNO_OTKAZANO' && receipt.status !== filterStatus) return false;
53+
return true;
4554
});
4655

4756
const handleStorno = async (receiptId) => {
@@ -94,7 +103,7 @@ export default function Racuni() {
94103
</div>
95104

96105
<div style={{ background: '#f8f9fa', padding: '10px', borderRadius: '5px', marginBottom: '20px', fontSize: '13px', color: '#666', border: '1px solid #eee' }}>
97-
Prikazuju se računi od <strong>06:00h</strong> danas do kraja smjene.
106+
Prikazuju se računi u periodu od <strong>06:00h</strong> do sutradan u <strong>06:00h</strong>.
98107
</div>
99108

100109
<input
@@ -113,8 +122,27 @@ export default function Racuni() {
113122
}}
114123
/>
115124

116-
<div style={{color: '#666', marginBottom: '15px'}}>
117-
Pronađeno: {filteredReceipts.length} računa
125+
<div style={{ display: 'flex', gap: '12px', marginBottom: '10px', alignItems: 'center', flexWrap: 'wrap' }}>
126+
<select value={filterPayment} onChange={e => setFilterPayment(e.target.value)}
127+
style={{ padding: '6px 10px', borderRadius: '4px', border: '1px solid #ddd', fontSize: '14px' }}>
128+
<option value="">Sva plaćanja</option>
129+
{uniquePaymentTypes.map(pt => <option key={pt} value={pt}>{pt}</option>)}
130+
</select>
131+
<select value={filterStatus} onChange={e => setFilterStatus(e.target.value)}
132+
style={{ padding: '6px 10px', borderRadius: '4px', border: '1px solid #ddd', fontSize: '14px' }}>
133+
<option value="">Svi statusi</option>
134+
<option value="RACUN">AKTIVAN</option>
135+
<option value="STORNO">STORNO</option>
136+
<option value="RACUN_STORNIRAN">OTKAZANO</option>
137+
<option value="STORNO_OTKAZANO">STORNO + OTKAZANO</option>
138+
</select>
139+
{(filterPayment || filterStatus) && (
140+
<button onClick={() => { setFilterPayment(''); setFilterStatus(''); }}
141+
style={{ padding: '6px 10px', borderRadius: '4px', border: '1px solid #ccc', background: '#f5f5f5', cursor: 'pointer', fontSize: '13px' }}>
142+
Resetiraj filtere
143+
</button>
144+
)}
145+
<span style={{ color: '#666', fontSize: '13px' }}>{filteredReceipts.length} / {receipts.length} računa</span>
118146
</div>
119147

120148
<div className="table-container">

frontend/src/pages/admin/UkupniIzvjestaj.jsx

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,8 @@ export default function UkupniIzvjestaj() {
101101
const [dateRange, setDateRange] = useState([null, null]);
102102
const [firstDateClick, setFirstDateClick] = useState(null); // Track first click for range selection
103103
const receiptRef = useRef();
104-
104+
const [filterPayment, setFilterPayment] = useState('');
105+
const [filterStatus, setFilterStatus] = useState('');
105106

106107

107108
const handleCalendarDayClick = (date) => {
@@ -276,6 +277,14 @@ export default function UkupniIzvjestaj() {
276277
const grandTotal = Object.values(paymentTotals).reduce((sum, total) => sum + total, 0);
277278
const reportData = { startTime, endTime, positiveReceipts, minReceiptNum, maxReceiptNum, allArticles, articlesByPayment, paymentTotals, paymentCounts, grandTotal };
278279

280+
const uniquePaymentTypes = [...new Set(dayReceipts.map(r => r.paymentType).filter(Boolean))];
281+
const filteredReceipts = dayReceipts.filter(r => {
282+
if (filterPayment && r.paymentType !== filterPayment) return false;
283+
if (filterStatus === 'STORNO_OTKAZANO' && r.status !== 'STORNO' && r.status !== 'RACUN_STORNIRAN') return false;
284+
else if (filterStatus && filterStatus !== 'STORNO_OTKAZANO' && r.status !== filterStatus) return false;
285+
return true;
286+
});
287+
279288
const handlePrint = () => {
280289
const w = window.open("", "_blank");
281290
w.document.write(`<!DOCTYPE html><html><head><meta charset="utf-8"><style>
@@ -481,6 +490,8 @@ export default function UkupniIzvjestaj() {
481490
setSelectedDate(null);
482491
setDateRange([null, null]);
483492
setFirstDateClick(null); // Reset first date click tracking
493+
setFilterPayment('');
494+
setFilterStatus('');
484495
}}
485496
style={{ background: '#666', color: 'white', border: 'none', padding: '10px 20px', borderRadius: '5px', cursor: 'pointer', fontWeight: 'bold' }}
486497
>
@@ -563,6 +574,28 @@ export default function UkupniIzvjestaj() {
563574
</div>
564575

565576
<h3 style={{ marginTop: '30px', marginBottom: '10px', color: '#333', paddingLeft: '5px' }}>Popis svih računa</h3>
577+
<div style={{ display: 'flex', gap: '12px', marginBottom: '10px', alignItems: 'center', flexWrap: 'wrap' }}>
578+
<select value={filterPayment} onChange={e => setFilterPayment(e.target.value)}
579+
style={{ padding: '6px 10px', borderRadius: '4px', border: '1px solid #ccc', fontSize: '14px' }}>
580+
<option value="">Sva plaćanja</option>
581+
{uniquePaymentTypes.map(pt => <option key={pt} value={pt}>{pt}</option>)}
582+
</select>
583+
<select value={filterStatus} onChange={e => setFilterStatus(e.target.value)}
584+
style={{ padding: '6px 10px', borderRadius: '4px', border: '1px solid #ccc', fontSize: '14px' }}>
585+
<option value="">Svi statusi</option>
586+
<option value="RACUN">AKTIVAN</option>
587+
<option value="STORNO">STORNO</option>
588+
<option value="RACUN_STORNIRAN">OTKAZANO</option>
589+
<option value="STORNO_OTKAZANO">STORNO + OTKAZANO</option>
590+
</select>
591+
{(filterPayment || filterStatus) && (
592+
<button onClick={() => { setFilterPayment(''); setFilterStatus(''); }}
593+
style={{ padding: '6px 10px', borderRadius: '4px', border: '1px solid #ccc', background: '#f5f5f5', cursor: 'pointer', fontSize: '13px' }}>
594+
Resetiraj filtere
595+
</button>
596+
)}
597+
<span style={{ color: '#666', fontSize: '13px' }}>{filteredReceipts.length} / {dayReceipts.length} računa</span>
598+
</div>
566599
<table style={{ width: '100%', borderCollapse: 'collapse', background: 'white', marginBottom: '40px', boxShadow: '0 2px 5px rgba(0,0,0,0.05)' }}>
567600
<thead>
568601
<tr style={{ background: '#eee', borderBottom: '2px solid #ccc' }}>
@@ -574,7 +607,7 @@ export default function UkupniIzvjestaj() {
574607
</tr>
575608
</thead>
576609
<tbody>
577-
{dayReceipts.map((r) => {
610+
{filteredReceipts.map((r) => {
578611
const isStorno = r.status === 'STORNO';
579612
const isCancelled = r.status === 'RACUN_STORNIRAN';
580613

@@ -595,7 +628,7 @@ export default function UkupniIzvjestaj() {
595628
<span style={{color: '#d32f2f', fontWeight: 'bold'}}>STORNO</span>
596629
) : (
597630
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', gap: '10px' }}>
598-
<span style={{color: '#388e3c'}}>OK</span>
631+
<span style={{color: '#388e3c'}}>AKTIVAN</span>
599632
<button
600633
onClick={() => handleStorno(r.id)}
601634
style={{

frontend/src/pages/admin/povijest.jsx

Lines changed: 62 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ export default function Povijest() {
88
const [loading, setLoading] = useState(true);
99
const [, setError] = useState(null);
1010
const [filter, setFilter] = useState("");
11+
const [filterPayment, setFilterPayment] = useState('');
12+
const [filterStatus, setFilterStatus] = useState('');
1113
const [reportType, setReportType] = useState("transactions");
1214
const [showDatePicker, setShowDatePicker] = useState(false);
1315

@@ -226,12 +228,19 @@ export default function Povijest() {
226228
}
227229
};
228230

231+
const uniquePaymentTypes = [...new Set(transactions.map(t => t.receipt?.paymentType).filter(Boolean))];
232+
229233
const filteredTransactions = transactions.filter(t => {
230234
const transactionSessionDay = getSessionDate(t.createdAt);
231-
const matchesSearch = !filter ||
235+
const matchesSearch = !filter ||
232236
t.receipt?.invoiceNumber?.toLowerCase().includes(filter.toLowerCase()) ||
233237
t.user?.name?.toLowerCase().includes(filter.toLowerCase());
234-
return matchesSearch && transactionSessionDay >= startDate && transactionSessionDay <= endDate;
238+
if (!matchesSearch) return false;
239+
if (transactionSessionDay < startDate || transactionSessionDay > endDate) return false;
240+
if (filterPayment && t.receipt?.paymentType !== filterPayment) return false;
241+
if (filterStatus === 'STORNO_OTKAZANO' && t.receipt?.status !== 'STORNO' && t.receipt?.status !== 'RACUN_STORNIRAN') return false;
242+
else if (filterStatus && filterStatus !== 'STORNO_OTKAZANO' && t.receipt?.status !== filterStatus) return false;
243+
return true;
235244
});
236245

237246
const articlesReport = getArticlesReport();
@@ -306,7 +315,7 @@ export default function Povijest() {
306315
boxShadow: '0 10px 40px rgba(0,0,0,0.15)',
307316
padding: '20px',
308317
zIndex: 1000,
309-
minWidth: '520px'
318+
minWidth: '320px'
310319
}} ref={datePickerRef}>
311320
<div style={{ marginBottom: '15px', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
312321
<h3 style={{ margin: 0, color: '#1a202c', fontSize: '16px' }}>Odaberi razdoblje</h3>
@@ -318,41 +327,28 @@ export default function Povijest() {
318327
</button>
319328
</div>
320329

321-
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '15px', marginBottom: '15px' }}>
322-
<div>
323-
<label style={{ fontSize: '12px', fontWeight: 'bold', color: '#718096', display: 'block', marginBottom: '8px' }}>POČETNI DATUM</label>
324-
<Calendar
325-
value={tempStartDate}
326-
onChange={handleDateSelection}
327-
tileClassName={({ date }) => {
328-
if (tempStartDate && tempEndDate) {
329-
if (date.toDateString() === tempStartDate.toDateString()) return 'date-range-start';
330-
if (date.toDateString() === tempEndDate.toDateString()) return 'date-range-end';
331-
if (date > tempStartDate && date < tempEndDate) return 'date-range-middle';
332-
} else if (tempStartDate && date.toDateString() === tempStartDate.toDateString()) {
333-
return 'date-range-start';
334-
}
335-
return '';
336-
}}
337-
/>
338-
</div>
339-
<div>
340-
<label style={{ fontSize: '12px', fontWeight: 'bold', color: '#718096', display: 'block', marginBottom: '8px' }}>ZAVRŠNI DATUM</label>
341-
<Calendar
342-
value={tempEndDate}
343-
onChange={handleDateSelection}
344-
tileClassName={({ date }) => {
345-
if (tempStartDate && tempEndDate) {
346-
if (date.toDateString() === tempStartDate.toDateString()) return 'date-range-start';
347-
if (date.toDateString() === tempEndDate.toDateString()) return 'date-range-end';
348-
if (date > tempStartDate && date < tempEndDate) return 'date-range-middle';
349-
} else if (tempStartDate && date.toDateString() === tempStartDate.toDateString()) {
350-
return 'date-range-start';
351-
}
352-
return '';
353-
}}
354-
/>
355-
</div>
330+
<div style={{ marginBottom: '10px' }}>
331+
<label style={{ fontSize: '12px', fontWeight: 'bold', color: '#718096', display: 'block', marginBottom: '8px' }}>
332+
{!tempStartDate || (tempStartDate && tempEndDate) ? 'KLIKNI POČETNI DATUM' : 'KLIKNI ZAVRŠNI DATUM'}
333+
</label>
334+
<Calendar
335+
value={tempStartDate}
336+
onChange={handleDateSelection}
337+
tileClassName={({ date }) => {
338+
if (tempStartDate && tempEndDate) {
339+
if (date.toDateString() === tempStartDate.toDateString()) return 'date-range-start';
340+
if (date.toDateString() === tempEndDate.toDateString()) return 'date-range-end';
341+
if (date > tempStartDate && date < tempEndDate) return 'date-range-middle';
342+
} else if (tempStartDate && date.toDateString() === tempStartDate.toDateString()) {
343+
return 'date-range-start';
344+
}
345+
return '';
346+
}}
347+
/>
348+
</div>
349+
<div style={{ fontSize: '13px', color: '#4a5568', marginBottom: '15px' }}>
350+
{tempStartDate && <span>Od: <strong>{tempStartDate.toISOString().split('T')[0]}</strong></span>}
351+
{tempStartDate && tempEndDate && <span> → Do: <strong>{tempEndDate.toISOString().split('T')[0]}</strong></span>}
356352
</div>
357353

358354
<div style={{ display: 'flex', gap: '10px', justifyContent: 'flex-end' }}>
@@ -476,15 +472,38 @@ export default function Povijest() {
476472
placeholder="Pretraži po broju računa ili prodavaču..."
477473
value={filter}
478474
onChange={(e) => setFilter(e.target.value)}
479-
style={{
480-
width: '100%',
481-
padding: '12px 15px',
482-
borderRadius: '8px',
475+
style={{
476+
width: '100%',
477+
padding: '12px 15px',
478+
borderRadius: '8px',
483479
border: '1px solid #e2e8f0',
484480
fontSize: '15px',
485-
outline: 'none'
481+
outline: 'none',
482+
marginBottom: '10px'
486483
}}
487484
/>
485+
<div style={{ display: 'flex', gap: '12px', alignItems: 'center', flexWrap: 'wrap' }}>
486+
<select value={filterPayment} onChange={e => setFilterPayment(e.target.value)}
487+
style={{ padding: '8px 12px', borderRadius: '6px', border: '1px solid #e2e8f0', fontSize: '14px' }}>
488+
<option value="">Sva plaćanja</option>
489+
{uniquePaymentTypes.map(pt => <option key={pt} value={pt}>{pt}</option>)}
490+
</select>
491+
<select value={filterStatus} onChange={e => setFilterStatus(e.target.value)}
492+
style={{ padding: '8px 12px', borderRadius: '6px', border: '1px solid #e2e8f0', fontSize: '14px' }}>
493+
<option value="">Svi statusi</option>
494+
<option value="RACUN">AKTIVAN</option>
495+
<option value="STORNO">STORNO</option>
496+
<option value="RACUN_STORNIRAN">OTKAZANO</option>
497+
<option value="STORNO_OTKAZANO">STORNO + OTKAZANO</option>
498+
</select>
499+
{(filterPayment || filterStatus) && (
500+
<button onClick={() => { setFilterPayment(''); setFilterStatus(''); }}
501+
style={{ padding: '8px 12px', borderRadius: '6px', border: '1px solid #e2e8f0', background: '#f7fafc', cursor: 'pointer', fontSize: '13px' }}>
502+
Resetiraj filtere
503+
</button>
504+
)}
505+
<span style={{ color: '#718096', fontSize: '13px' }}>{filteredTransactions.length} / {transactions.filter(t => { const d = getSessionDate(t.createdAt); return d >= startDate && d <= endDate; }).length} transakcija</span>
506+
</div>
488507
</div>
489508
)}
490509

0 commit comments

Comments
 (0)