From b534957c971108f75ee3c3bb1b00c457ad2f15ac Mon Sep 17 00:00:00 2001
From: jpie02 <128189069+jpie02@users.noreply.github.com>
Date: Tue, 16 Dec 2025 17:33:35 +0100
Subject: [PATCH 1/5] Input bar active outline fixes.
---
web_app/src/LibraryAdmin/LibraryAdminAddLibrarian.tsx | 6 +++---
web_app/src/LibraryAdmin/LibraryAdminHomePage.tsx | 2 +-
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/web_app/src/LibraryAdmin/LibraryAdminAddLibrarian.tsx b/web_app/src/LibraryAdmin/LibraryAdminAddLibrarian.tsx
index ccfd4905..2f54f157 100644
--- a/web_app/src/LibraryAdmin/LibraryAdminAddLibrarian.tsx
+++ b/web_app/src/LibraryAdmin/LibraryAdminAddLibrarian.tsx
@@ -85,21 +85,21 @@ const LibraryAdminHomePage: React.FC = () => {
placeholder="Nazwa użytkownika"
value={newLibrarian.username}
onChange={(e) => setNewLibrarian({...newLibrarian, username: e.target.value})}
- className="border border-gray-300 rounded p-2"
+ className="w-full p-2 rounded-lg border-2 outline-none bg-white text-[#3b4248] focus:outline-none focus:ring-2 focus:ring-[#3B576C]"
/>
setNewLibrarian({...newLibrarian, firstName: e.target.value})}
- className="border border-gray-300 rounded p-2"
+ className="w-full p-2 rounded-lg border-2 outline-none bg-white text-[#3b4248] focus:outline-none focus:ring-2 focus:ring-[#3B576C]"
/>
setNewLibrarian({...newLibrarian, lastName: e.target.value})}
- className="border border-gray-300 rounded p-2"
+ className="w-full p-2 rounded-lg border-2 outline-none bg-white text-[#3b4248] focus:outline-none focus:ring-2 focus:ring-[#3B576C]"
/>
diff --git a/web_app/src/LibraryAdmin/LibraryAdminHomePage.tsx b/web_app/src/LibraryAdmin/LibraryAdminHomePage.tsx
index d4db28b2..f6bbaa7d 100644
--- a/web_app/src/LibraryAdmin/LibraryAdminHomePage.tsx
+++ b/web_app/src/LibraryAdmin/LibraryAdminHomePage.tsx
@@ -160,7 +160,7 @@ const LibraryAdminHomePage: React.FC = () => {
placeholder="Nazwa użytkownika"
value={usernameSearch}
onChange={(e) => setUsernameSearch(e.target.value)}
- className="border border-gray-300 rounded p-2 w-full pr-10"
+ 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 && (
)}
- {message && {message}
}
+ {message && {message}
}
From b448a79e89092f706b9f9351b573530d8e7424f9 Mon Sep 17 00:00:00 2001
From: jpie02 <128189069+jpie02@users.noreply.github.com>
Date: Tue, 16 Dec 2025 19:23:23 +0100
Subject: [PATCH 3/5] Fix the delete librarian functionality of
LibraryAdminHomePage.tsx to display an alert window for confirmation. Add
additional username information in success messages both
LibraryAdminHomePage.tsx ans LibraryAdminAddLibrarian.tsx
---
web_app/src/LibraryAdmin/LibraryAdminAddLibrarian.tsx | 2 +-
web_app/src/LibraryAdmin/LibraryAdminHomePage.tsx | 6 +++++-
2 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/web_app/src/LibraryAdmin/LibraryAdminAddLibrarian.tsx b/web_app/src/LibraryAdmin/LibraryAdminAddLibrarian.tsx
index 2f54f157..1beb5da4 100644
--- a/web_app/src/LibraryAdmin/LibraryAdminAddLibrarian.tsx
+++ b/web_app/src/LibraryAdmin/LibraryAdminAddLibrarian.tsx
@@ -21,7 +21,7 @@ const LibraryAdminHomePage: React.FC = () => {
body: JSON.stringify(newLibrarian),
});
if (res.ok) {
- setMessage('Dodano bibliotekarza.');
+ setMessage('Dodano bibliotekarza o nazwie użytkownika ' + newLibrarian.username + '.' );
setNewLibrarian({ username: '', firstName: '', lastName: '' });
} else {
setMessage('Nie udało się dodać bibliotekarza.');
diff --git a/web_app/src/LibraryAdmin/LibraryAdminHomePage.tsx b/web_app/src/LibraryAdmin/LibraryAdminHomePage.tsx
index 655082de..cd02983a 100644
--- a/web_app/src/LibraryAdmin/LibraryAdminHomePage.tsx
+++ b/web_app/src/LibraryAdmin/LibraryAdminHomePage.tsx
@@ -108,6 +108,10 @@ const LibraryAdminHomePage: React.FC = () => {
};
const deleteLibrarian = async (username: string) => {
+ if (!window.confirm(`Czy na pewno chcesz konto użytkownika "${username}"? Tej operacji nie można cofnąć.`)) {
+ return;
+ }
+
const token = localStorage.getItem('access_token');
try {
const res = await fetch(`${API_BASE_URL}/api/library-admins/librarians/${username}`, {
@@ -118,7 +122,7 @@ const LibraryAdminHomePage: React.FC = () => {
});
if (res.ok) {
setLibrarians(librarians.filter(lib => lib.username !== username));
- setMessage('Usunięto bibliotekarza.');
+ setMessage('Usunięto bibliotekarza o nazwie użytkownika ' + username + '.');
} else {
setMessage('Nie udało się usunąć bibliotekarza.');
}
From 9a459a10bf9ed79827f353def1e522baac7a74dd Mon Sep 17 00:00:00 2001
From: jpie02 <128189069+jpie02@users.noreply.github.com>
Date: Wed, 17 Dec 2025 11:49:47 +0100
Subject: [PATCH 4/5] Replace the tab icon image.
---
web_app/index.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/web_app/index.html b/web_app/index.html
index 2992e560..682cf7ec 100644
--- a/web_app/index.html
+++ b/web_app/index.html
@@ -2,7 +2,7 @@
-
+
BookRider
From 9522f47a20c26b2729799440275eea618ccd6f12 Mon Sep 17 00:00:00 2001
From: jpie02 <128189069+jpie02@users.noreply.github.com>
Date: Thu, 8 Jan 2026 16:37:54 +0100
Subject: [PATCH 5/5] Fix the race condition created when multiple books are
selected to be added/removed from a given library in the librarian dashboard.
---
web_app/src/Librarian/LibrarianHomePage.tsx | 111 ++++++++++++--------
1 file changed, 67 insertions(+), 44 deletions(-)
diff --git a/web_app/src/Librarian/LibrarianHomePage.tsx b/web_app/src/Librarian/LibrarianHomePage.tsx
index 085ff501..06748921 100644
--- a/web_app/src/Librarian/LibrarianHomePage.tsx
+++ b/web_app/src/Librarian/LibrarianHomePage.tsx
@@ -344,36 +344,47 @@ const LibrarianHomePage: React.FC = () => {
return;
}
- try {
- const addRequests = selectedBooks.map(id =>
- fetch(`${API_BASE_URL}/api/books/add-existing/${id}?libraryId=${assignedLibrary.id}`, {
+ let successCount = 0;
+ let failCount = 0;
+ const successfulIds: number[] = [];
+
+ // preventing race conditions on the server by ensuring that the requests are sent sequentially (one by one)
+ for (const id of selectedBooks) {
+ try {
+ const response = await fetch(`${API_BASE_URL}/api/books/add-existing/${id}?libraryId=${assignedLibrary.id}`, {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
},
- })
- );
-
- const responses = await Promise.all(addRequests);
-
- const allSuccessful = responses.every(response => response.ok);
-
- if (allSuccessful) {
- setAddBooksMessage({
- text: "Pomyślnie dodano książki do biblioteki.",
- type: "success"
- });
- } else {
- setAddBooksMessage({
- text: "Niektóre z książek już wcześniej zostały przypisane do Twojej biblioteki.",
- type: "error"
});
+
+ if (response.ok) {
+ successCount++;
+ successfulIds.push(id);
+ } else {
+ failCount++;
+ }
+ } catch (error) {
+ console.error(`Error adding book ${id}:`, error);
+ failCount++;
}
+ }
- setSelectedBooks([]);
- } catch {
+ setSelectedBooks((prev) => prev.filter((id) => !successfulIds.includes(id)));
+
+ if (successCount > 0 && failCount === 0) {
+ setAddBooksMessage({
+ text: "Pomyślnie dodano wszystkie wybrane książki do biblioteki.",
+ type: "success"
+ });
+ } else if (successCount > 0 && failCount > 0) {
+ setAddBooksMessage({
+ text: `Dodano ${successCount} książek. ${failCount} nie udało się dodać (mogą już istnieć w bibliotece).`,
+ type: "error"
+ });
+ } else {
setAddBooksMessage({
- text: "Wystąpił błąd podczas dodawania książek.",
+ text: "Nie udało się dodać wybranych książek (mogą już być w bibliotece).",
type: "error"
});
}
@@ -383,37 +394,49 @@ const LibrarianHomePage: React.FC = () => {
const token = localStorage.getItem('access_token');
if (!token) return;
- try {
- const deleteRequests = selectedBooks.map(id =>
- fetch(`${API_BASE_URL}/api/books/my-library/${id}`, {
+ let successCount = 0;
+ let failCount = 0;
+ const successfulIds: number[] = [];
+
+ // preventing race conditions on the server by ensuring that the requests are sent sequentially (one by one)
+ for (const id of selectedBooks) {
+ try {
+ const response = await fetch(`${API_BASE_URL}/api/books/my-library/${id}`, {
method: "DELETE",
headers: {
Authorization: `Bearer ${token}`,
},
- })
- );
+ });
- const responses = await Promise.all(deleteRequests);
+ if (response.ok) {
+ successCount++;
+ successfulIds.push(id);
+ } else {
+ failCount++;
+ }
+ } catch (error) {
+ console.error(`Error deleting book ${id}:`, error);
+ failCount++;
+ }
+ }
- const allSuccessful = responses.every(response => response.ok);
+ setBookSearchResults(prev => prev.filter(book => !successfulIds.includes(book.id)));
- if (allSuccessful) {
- setBookSearchResults(prev => prev.filter(book => !selectedBooks.includes(book.id)));
+ setSelectedBooks((prev) => prev.filter((id) => !successfulIds.includes(id)));
- setSelectedBooks([]);
- setDeleteBooksMessage({
- text: "Wybrane książki zostały usunięte z biblioteki.",
- type: "success"
- });
- } else {
- setDeleteBooksMessage({
- text: "Niektórych książek nie udało się usunąć.",
- type: "error"
- });
- }
- } catch {
+ if (successCount > 0 && failCount === 0) {
+ setDeleteBooksMessage({
+ text: "Wybrane książki zostały usunięte z biblioteki.",
+ type: "success"
+ });
+ } else if (successCount > 0 && failCount > 0) {
+ setDeleteBooksMessage({
+ text: `Usunięto ${successCount} książek. ${failCount} nie udało się usunąć.`,
+ type: "error"
+ });
+ } else {
setDeleteBooksMessage({
- text: "Wystąpił błąd podczas usuwania książek.",
+ text: "Nie udało się usunąć wybranych książek.",
type: "error"
});
}