Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion web_app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
44 changes: 34 additions & 10 deletions web_app/src/Librarian/LibrarianReaders.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -28,6 +28,10 @@ const LibrarianReaders: React.FC = () => {

const navigate = useNavigate();

const [message, setMessage] = useState<string | null>(null);
const [messageSearch, setMessageSearch] = useState<string | null>(null);
const [messageType, setMessageType] = useState<'success' | 'error' | null>(null);

useWebSocketNotification('librarian/orders/pending', () => {
toast.info("Otrzymano nowe zamówienie!", {
position: "bottom-right",
Expand Down Expand Up @@ -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');
}
};

Expand Down Expand Up @@ -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');
}
};

Expand Down Expand Up @@ -221,6 +230,17 @@ const LibrarianReaders: React.FC = () => {
</button>
</div>
</div>
{message && (
<div
className={`mt-4 p-4 rounded-md ${
messageType === 'success'
? 'bg-green-100 text-[#3B576C]'
: 'bg-red-100 text-red-800'
}`}
>
{message}
</div>
)}
</div>


Expand Down Expand Up @@ -252,6 +272,17 @@ const LibrarianReaders: React.FC = () => {
</button>
</div>
</div>
{messageSearch && (
<div
className={`mt-8 p-4 rounded-md ${
messageType === 'success'
? 'bg-green-100 text-[#3B576C]'
: 'bg-red-100 text-red-800'
}`}
>
{messageSearch}
</div>
)}
</div>

{libraryCardDetails && (
Expand All @@ -268,13 +299,6 @@ const LibrarianReaders: React.FC = () => {
</div>
)}
</div>

{/*{activeSection === 'settings' && (*/}
{/* <section className="p-5 rounded-md mb-[400px] h-[80%] max-h-[90%] w-[65%]">*/}
{/* <h2 className="text-center text-white">Ustawienia</h2>*/}
{/* </section>*/}
{/*)}*/}

</main>
</div>
);
Expand Down
6 changes: 3 additions & 3 deletions web_app/src/Librarian/LibrarianReturns.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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');
}
};
Expand Down
156 changes: 110 additions & 46 deletions web_app/src/LibraryAdmin/LibraryAdminHomePage.tsx
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -20,8 +22,10 @@ interface PasswordResetResponse {

const LibraryAdminHomePage: React.FC = () => {
const [usernameSearch, setUsernameSearch] = useState('');
const [librarians, setLibrarians] = useState<Librarian[]>([]);
const [librarians, setLibrarians] = useState<Librarian[]>([]); // displayed list (filtered)
const [allLibrarians, setAllLibrarians] = useState<Librarian[]>([]); // master list (real)
const [message, setMessage] = useState('');
const [notif, setNotif] = useState('');

const navigate = useNavigate();

Expand All @@ -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 {
Expand All @@ -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;
}

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

Expand All @@ -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ę.');
}
};

Expand Down Expand Up @@ -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 && (
<button
onClick={() => setUsernameSearch('')}
className="absolute right-28 top-1/2 transform -translate-y-1/2 text-gray-500 hover:text-gray-700 px-2"
onClick={() => {
setUsernameSearch('');
setLibrarians(allLibrarians);
}}
className="absolute right-24 top-1/2 transform -translate-y-1/2 text-gray-500 hover:text-gray-700 px-2"
>
</button>
)}
<button
onClick={fetchLibrarian}
className="bg-[#3B576C] text-white px-4 py-2 rounded-md whitespace-nowrap"
>
onClick={handleSearch}
className="bg-[#3B576C] text-white px-4 py-2 rounded-md whitespace-nowrap">
Szukaj
</button>
</div>
Expand All @@ -217,14 +282,12 @@ const LibraryAdminHomePage: React.FC = () => {
<div className="flex gap-2">
<button
onClick={() => resetPassword(lib.username)}
className="bg-[#5B7F9A] text-white px-9 py-2 rounded-md"
>
className="bg-[#5B7F9A] text-white px-9 py-2 rounded-md">
Zresetuj hasło
</button>
<button
onClick={() => deleteLibrarian(lib.username)}
className="bg-red-600 text-white px-8 py-2 rounded-md"
>
className="bg-red-600 text-white px-8 py-2 rounded-md">
Usuń
</button>
</div>
Expand All @@ -235,6 +298,7 @@ const LibraryAdminHomePage: React.FC = () => {
)}

{message && <p className="mt-4 p-4 rounded-md bg-green-100 text-[#3B576C]">{message}</p>}
{notif && <p className="mt-4 text-lg p-4 rounded-md bg-gray-100 text-[#3B576C]">{notif}</p>}
</div>
</div>
</section>
Expand Down
Loading