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
2 changes: 1 addition & 1 deletion web_app/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<html lang="pl">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/book-rider-high-resolution-logo.png" />
<link rel="icon" type="image/svg+xml" href="/book-high-res.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>BookRider</title>
</head>
Expand Down
111 changes: 67 additions & 44 deletions web_app/src/Librarian/LibrarianHomePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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"
});
}
Expand All @@ -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"
});
}
Expand Down
8 changes: 4 additions & 4 deletions web_app/src/LibraryAdmin/LibraryAdminAddLibrarian.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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.');
Expand Down Expand Up @@ -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]"
/>
<input
type="text"
placeholder="Imię"
value={newLibrarian.firstName}
onChange={(e) => 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]"
/>
<input
type="text"
placeholder="Nazwisko"
value={newLibrarian.lastName}
onChange={(e) => 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]"
/>
</div>
<div className="relative flex justify-center items-center">
Expand Down
32 changes: 28 additions & 4 deletions web_app/src/LibraryAdmin/LibraryAdminHomePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@ interface Librarian {
lastName: string;
}

interface PasswordResetResponse {
id: string;
username: string;
firstName: string;
lastName: string;
tempPassword: string;
}

const LibraryAdminHomePage: React.FC = () => {
const [usernameSearch, setUsernameSearch] = useState('');
const [librarians, setLibrarians] = useState<Librarian[]>([]);
Expand Down Expand Up @@ -72,6 +80,10 @@ const LibraryAdminHomePage: React.FC = () => {
};

const resetPassword = async (username: string) => {
if (!window.confirm(`Czy na pewno chcesz zresetować hasło dla użytkownika "${username}"?`)) {
return;
}

const token = localStorage.getItem('access_token');
try {
const res = await fetch(`${API_BASE_URL}/api/library-admins/librarians/reset-password/${username}`, {
Expand All @@ -80,14 +92,26 @@ const LibraryAdminHomePage: React.FC = () => {
Authorization: `Bearer ${token}`,
},
});
setMessage(res.ok ? 'Hasło bibliotekarza zresetowano pomyślnie.' : 'Nie udało się zresetować hasła bibliotekarza.');

if (res.ok) {
const data: PasswordResetResponse = await res.json();
const newPassword = data.tempPassword;

setMessage(`Hasło zresetowane dla ${username}. Nowe hasło: ${newPassword}`);
} else {
setMessage('Nie udało się zresetować hasła bibliotekarza.');
}
} catch (err) {
console.error(err);
setMessage('Error resetting password');
}
};

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}`, {
Expand All @@ -98,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.');
}
Expand Down Expand Up @@ -160,7 +184,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 && (
<button
Expand Down Expand Up @@ -210,7 +234,7 @@ const LibraryAdminHomePage: React.FC = () => {
</div>
)}

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