diff --git a/web_app/package.json b/web_app/package.json index c81d3bb5..15273324 100644 --- a/web_app/package.json +++ b/web_app/package.json @@ -17,7 +17,8 @@ "react-router-dom": "^7.0.2", "react-toastify": "^11.0.5", "swagger-ui": "^5.18.2", - "swagger-ui-react": "^5.18.2" + "swagger-ui-react": "^5.18.2", + "sweetalert2": "^11.26.17" }, "devDependencies": { "@eslint/js": "^9.13.0", diff --git a/web_app/src/Librarian/LibrarianReaders.tsx b/web_app/src/Librarian/LibrarianReaders.tsx index 0b538789..a1e2d06c 100644 --- a/web_app/src/Librarian/LibrarianReaders.tsx +++ b/web_app/src/Librarian/LibrarianReaders.tsx @@ -12,7 +12,7 @@ const LibrarianReaders: React.FC = () => { const [cardId, setCardId] = useState(''); const [firstName, setFirstName] = useState(''); const [lastName, setLastName] = useState(''); - const [expirationDate, setExpirationDate] = useState('2025-01-01'); + const [expirationDate, setExpirationDate] = useState(new Date().toLocaleDateString('en-CA')); const [libraryCardSearchId, setLibraryCardSearchId] = useState(''); // User @@ -28,6 +28,10 @@ const LibrarianReaders: React.FC = () => { const navigate = useNavigate(); + const [message, setMessage] = useState(null); + const [messageSearch, setMessageSearch] = useState(null); + const [messageType, setMessageType] = useState<'success' | 'error' | null>(null); + useWebSocketNotification('librarian/orders/pending', () => { toast.info("Otrzymano nowe zamówienie!", { position: "bottom-right", @@ -60,17 +64,20 @@ const LibrarianReaders: React.FC = () => { }); if (response.ok) { - alert('Library card created successfully'); + setMessage('Konto czytelnika zostało pomyślnie dodane.'); + setMessageType('success'); setUserId(''); setCardId(''); setFirstName(''); setLastName(''); - setExpirationDate('2025-01-01'); + setExpirationDate(new Date().toLocaleDateString('en-CA')); } else { throw new Error('Error creating library card'); } } catch (error) { console.error('Error creating library card: ', error); + setMessage('Nastąpił błąd podczas dodawania czytelnika.'); + setMessageType('error'); } }; @@ -99,6 +106,8 @@ const LibrarianReaders: React.FC = () => { } catch (error) { console.error('Error searching for library card: ', error); setLibraryCardDetails(null); + setMessageSearch('Nie znaleziono czytelnika o podanym numerze ID.'); + setMessageType('error'); } }; @@ -221,6 +230,17 @@ const LibrarianReaders: React.FC = () => { + {message && ( +
+ {message} +
+ )} @@ -252,6 +272,17 @@ const LibrarianReaders: React.FC = () => { + {messageSearch && ( +
+ {messageSearch} +
+ )} {libraryCardDetails && ( @@ -268,13 +299,6 @@ const LibrarianReaders: React.FC = () => { )} - - {/*{activeSection === 'settings' && (*/} - {/*
*/} - {/*

Ustawienia

*/} - {/*
*/} - {/*)}*/} - ); diff --git a/web_app/src/Librarian/LibrarianReturns.tsx b/web_app/src/Librarian/LibrarianReturns.tsx index a885d8b7..300772cc 100644 --- a/web_app/src/Librarian/LibrarianReturns.tsx +++ b/web_app/src/Librarian/LibrarianReturns.tsx @@ -108,15 +108,15 @@ const LibrarianReturns: React.FC = () => { if (response.ok) { //setReturnStatus('COMPLETED'); - setMessage('Return completed successfully.'); + //setMessage('Return completed successfully.'); setMessageType('success'); } else { - setMessage('Failed to complete the return.'); + //setMessage('Failed to complete the return.'); setMessageType('error'); } } catch (error) { console.error('Error:', error); - setMessage('Error completing the return.'); + setMessage('Podczas realizacji zwrotu nastąpił błąd.'); setMessageType('error'); } }; diff --git a/web_app/src/LibraryAdmin/LibraryAdminHomePage.tsx b/web_app/src/LibraryAdmin/LibraryAdminHomePage.tsx index cd02983a..8bf15f24 100644 --- a/web_app/src/LibraryAdmin/LibraryAdminHomePage.tsx +++ b/web_app/src/LibraryAdmin/LibraryAdminHomePage.tsx @@ -1,5 +1,7 @@ import React, { useEffect, useState } from 'react'; import {Link, useNavigate} from "react-router-dom"; +import Swal from 'sweetalert2'; +import 'sweetalert2/dist/sweetalert2.min.css'; const API_BASE_URL = import.meta.env.VITE_API_BASE_URL; @@ -20,8 +22,10 @@ interface PasswordResetResponse { const LibraryAdminHomePage: React.FC = () => { const [usernameSearch, setUsernameSearch] = useState(''); - const [librarians, setLibrarians] = useState([]); + const [librarians, setLibrarians] = useState([]); // displayed list (filtered) + const [allLibrarians, setAllLibrarians] = useState([]); // master list (real) const [message, setMessage] = useState(''); + const [notif, setNotif] = useState(''); const navigate = useNavigate(); @@ -31,12 +35,11 @@ const LibraryAdminHomePage: React.FC = () => { try { const response = await fetch(`${API_BASE_URL}/api/library-admins/librarians`, { method: 'GET', - headers: { - Authorization: `Bearer ${token}`, - }, + headers: { Authorization: `Bearer ${token}` }, }); if (response.ok) { const data = await response.json(); + setAllLibrarians(data); setLibrarians(data); setMessage(''); } else { @@ -47,40 +50,52 @@ const LibraryAdminHomePage: React.FC = () => { setMessage('Error fetching librarian list'); } }; + fetchAll(); + }, []); - if (usernameSearch.trim() === '') { - fetchAll(); + const handleSearch = () => { + const term = usernameSearch.trim().toLowerCase(); + + if (!term) { + setLibrarians(allLibrarians); + setMessage(''); + return; } - }, [usernameSearch]); - const fetchLibrarian = async () => { - const token = localStorage.getItem('access_token'); - if (!usernameSearch.trim()) return; + const filtered = allLibrarians.filter(lib => + lib.username.toLowerCase().includes(term) + ); - try { - const response = await fetch(`${API_BASE_URL}/api/library-admins/librarians?username=${usernameSearch}`, { - method: 'GET', - headers: { - Authorization: `Bearer ${token}`, - }, - }); + setLibrarians(filtered); - if (response.ok) { - const data = await response.json(); - setLibrarians(Array.isArray(data) ? data : [data]); - setMessage(''); - } else { - setLibrarians([]); - setMessage('Nie znaleziono bibliotekarza.'); - } - } catch (err) { - console.error(err); - setMessage('Error fetching librarian'); + if (filtered.length === 0) { + setNotif('Bibliotekarz o podanej nazwie użytkownika nie istnieje.'); + } else { + setNotif(''); } }; + useEffect(() => { + if (usernameSearch === '') { + setLibrarians(allLibrarians); + setMessage(''); + } + }, [usernameSearch, allLibrarians]); + const resetPassword = async (username: string) => { - if (!window.confirm(`Czy na pewno chcesz zresetować hasło dla użytkownika "${username}"?`)) { + const result = await Swal.fire({ + title: 'Na pewno?', + text: `Czy na pewno chcesz zresetować hasło dla użytkownika "${username}"?`, + icon: 'warning', + showCancelButton: true, + confirmButtonColor: '#3B576C', + cancelButtonColor: '#d33', + confirmButtonText: 'Tak, zresetuj', + cancelButtonText: 'Anuluj' + }); + + // if cancelled + if (!result.isConfirmed) { return; } @@ -97,18 +112,65 @@ const LibraryAdminHomePage: React.FC = () => { const data: PasswordResetResponse = await res.json(); const newPassword = data.tempPassword; - setMessage(`Hasło zresetowane dla ${username}. Nowe hasło: ${newPassword}`); + const reset = await Swal.fire({ + title: 'Hasło zostało zresetowane', + text: `Hasło zresetowane dla ${username}. Nowe hasło: ${newPassword}`, + icon: 'info', + showCancelButton: false, + confirmButtonColor: '#3B576C', + confirmButtonText: 'OK', + }); + + if (!reset.isConfirmed) { + return; + } + } else { - setMessage('Nie udało się zresetować hasła bibliotekarza.'); + const reset = await Swal.fire({ + title: 'Nie udało się zresetować hasła', + text: `Spróbuj ponownie później.`, + icon: 'warning', + showCancelButton: false, + confirmButtonColor: '#3B576C', + confirmButtonText: 'OK', + }); + + if (!reset.isConfirmed) { + return; + } } } catch (err) { console.error(err); - setMessage('Error resetting password'); + + const reset = await Swal.fire({ + title: 'Nie udało się zresetować hasła', + text: `Spróbuj ponownie później.`, + icon: 'warning', + showCancelButton: false, + confirmButtonColor: '#3B576C', + confirmButtonText: 'OK', + }); + + if (!reset.isConfirmed) { + return; + } } }; const deleteLibrarian = async (username: string) => { - if (!window.confirm(`Czy na pewno chcesz konto użytkownika "${username}"? Tej operacji nie można cofnąć.`)) { + const result = await Swal.fire({ + title: 'Na pewno?', + text: `Czy na pewno chcesz usunąć konto użytkownika "${username}"? Tej operacji nie można cofnąć.`, + icon: 'warning', + showCancelButton: true, + confirmButtonColor: '#3B576C', + cancelButtonColor: '#d33', + confirmButtonText: 'Tak, usuń', + cancelButtonText: 'Anuluj' + }); + + // if cancelled + if (!result.isConfirmed) { return; } @@ -121,14 +183,14 @@ const LibraryAdminHomePage: React.FC = () => { }, }); if (res.ok) { - setLibrarians(librarians.filter(lib => lib.username !== username)); + setAllLibrarians(prev => prev.filter(lib => lib.username !== username)); + setLibrarians(prev => prev.filter(lib => lib.username !== username)); + setMessage('Usunięto bibliotekarza o nazwie użytkownika ' + username + '.'); - } else { - setMessage('Nie udało się usunąć bibliotekarza.'); } } catch (err) { console.error(err); - setMessage('Error deleting librarian'); + setMessage('Usuwanie biblkiotekarza nie powiodło się.'); } }; @@ -184,20 +246,23 @@ const LibraryAdminHomePage: React.FC = () => { placeholder="Nazwa użytkownika" value={usernameSearch} onChange={(e) => setUsernameSearch(e.target.value)} + onKeyDown={(e) => e.key === 'Enter' && handleSearch()} className="w-full p-2 rounded-lg border-2 outline-none bg-white text-[#3b4248] focus:outline-none focus:ring-2 focus:ring-[#3B576C]" /> {usernameSearch && ( )} @@ -217,14 +282,12 @@ const LibraryAdminHomePage: React.FC = () => {
@@ -235,6 +298,7 @@ const LibraryAdminHomePage: React.FC = () => { )} {message &&

{message}

} + {notif &&

{notif}

} diff --git a/web_app/src/SystemAdmin/SystemAdminHomePage.tsx b/web_app/src/SystemAdmin/SystemAdminHomePage.tsx index 5e08e861..d266696e 100644 --- a/web_app/src/SystemAdmin/SystemAdminHomePage.tsx +++ b/web_app/src/SystemAdmin/SystemAdminHomePage.tsx @@ -97,7 +97,13 @@ const SystemAdminDashboard: React.FC = () => { } try { - const response = await fetch(`${API_BASE_URL}/api/driver-applications?statuses=PENDING,UNDER_REVIEW&page=${page}&size=1`, { + const params = new URLSearchParams(); + params.append('statuses', 'PENDING'); + params.append('statuses', 'UNDER_REVIEW'); + params.append('page', page.toString()); + params.append('size', '5'); + + const response = await fetch(`${API_BASE_URL}/api/driver-applications?${params.toString()}`, { method: 'GET', headers: { 'Authorization': `Bearer ${token}`, @@ -113,7 +119,7 @@ const SystemAdminDashboard: React.FC = () => { if (data.length > 0) { setDriverApplications((prev) => [...prev, ...data]); - setDriverPage(page + 1); + setDriverPage(page + 5); } else { setDriverHasMore(false); } @@ -128,7 +134,13 @@ const SystemAdminDashboard: React.FC = () => { if (!token) return; try { - const response = await fetch(`${API_BASE_URL}/api/library-requests?statuses=PENDING,UNDER_REVIEW&page=${page}&size=1`, { + const params = new URLSearchParams(); + params.append('statuses', 'PENDING'); + params.append('statuses', 'UNDER_REVIEW'); + params.append('page', page.toString()); + params.append('size', '5'); + + const response = await fetch(`${API_BASE_URL}/api/library-requests?${params.toString()}`, { method: 'GET', headers: { 'Authorization': `Bearer ${token}`, @@ -142,7 +154,7 @@ const SystemAdminDashboard: React.FC = () => { if (data.length > 0) { setLibraryRequests((prev) => [...prev, ...data]); - setLibraryPage(page + 1); + setLibraryPage(page + 2); } else { setLibraryHasMore(false); }