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
10 changes: 10 additions & 0 deletions web_app/src/Librarian/LibrarianAddBook.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import React, { useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import {useWebSocketNewOrderNotification} from './useWebSocketNewOrderNotification.tsx';
import {toast} from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';

const API_BASE_URL = import.meta.env.VITE_API_BASE_URL;

Expand Down Expand Up @@ -83,6 +86,13 @@ const LibrarianAddBook: React.FC = () => {
fetchDropdownData();
}, []);

useWebSocketNewOrderNotification('librarian/orders/pending', () => {
toast.info("Otrzymano nowe zamówienie!", {
position: "bottom-right",
});
console.log("New order received!");
});

const handleAuthorSelect = async (author: string) => {
if (!authors.includes(author)) {
setAuthors([...authors, author]);
Expand Down
19 changes: 16 additions & 3 deletions web_app/src/Librarian/LibrarianHomePage.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import React, {useEffect, useState} from 'react';
import React, {useEffect, useState, useRef} from 'react';
import {Link, useNavigate} from 'react-router-dom';
import {useWebSocketNewOrderNotification} from './useWebSocketNewOrderNotification.tsx';
import {toast} from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';

const API_BASE_URL = import.meta.env.VITE_API_BASE_URL;

Expand Down Expand Up @@ -72,6 +75,7 @@ const LibrarianHomePage: React.FC = () => {
const [page, setPage] = useState(0);
const [hasMore, setHasMore] = useState(true);
const [isLoading, setIsLoading] = useState(false);
const isFetchingRef = useRef(false);

useEffect(() => {
fetchAssignedLibrary();
Expand Down Expand Up @@ -131,6 +135,13 @@ const LibrarianHomePage: React.FC = () => {
return () => window.removeEventListener('scroll', handleScroll);
}, [isLoading, hasMore, page, isUserLibraryChecked]);

useWebSocketNewOrderNotification('librarian/orders/pending', () => {
toast.info("Otrzymano nowe zamówienie!", {
position: "bottom-right",
});
console.log("New order received!");
});

const fetchDropdownData = async () => {
const token = localStorage.getItem('access_token');

Expand Down Expand Up @@ -221,8 +232,9 @@ const LibrarianHomePage: React.FC = () => {

const fetchBooks = async (filterByLibrary: boolean, pageToFetch: number = 0) => {
const token = localStorage.getItem('access_token');
if (!token || isLoading || !hasMore) return;
if (!token || isFetchingRef.current || isLoading || !hasMore) return;

isFetchingRef.current = true;
setIsLoading(true);

const queryParams = new URLSearchParams();
Expand All @@ -241,7 +253,7 @@ const LibrarianHomePage: React.FC = () => {
if (releaseYearTo) queryParams.append("releaseYearTo", releaseYearTo.toString());

queryParams.append("page", pageToFetch.toString());
queryParams.append("size", "3");
queryParams.append("size", "2");

try {
const response = await fetch(`${API_BASE_URL}/api/books/search?${queryParams.toString()}`, {
Expand All @@ -265,6 +277,7 @@ const LibrarianHomePage: React.FC = () => {
console.error("Error: ", error);
} finally {
setIsLoading(false);
isFetchingRef.current = false;
}
};

Expand Down
2 changes: 1 addition & 1 deletion web_app/src/Librarian/LibrarianLogin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ const LibrarianLogin: React.FC = () => {
</Link>
</div>
<div
className="absolute top-[22%] left-1/2 transform -translate-x-1/2 bg-white p-10 rounded-xl shadow-2xl w-[clamp(280px,30vw,400px)] h-[clamp(505px,55vh,90vh)]">
className="absolute top-[22%] left-1/2 transform -translate-x-1/2 bg-white p-10 rounded-xl shadow-2xl w-[clamp(280px,400px,400px)] h-[clamp(550px,55px,90px)]">
<h2 className="text-center mb-6 text-3xl font-semibold text-[#2c3e50]">
Logowanie bibliotekarza
</h2>
Expand Down
13 changes: 11 additions & 2 deletions web_app/src/Librarian/LibrarianOrders.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import React, {useState, useEffect } from 'react';
import {Link, useNavigate} from 'react-router-dom';
import {useWebSocketNewOrderNotification} from './useWebSocketNewOrderNotification.tsx';
import {toast} from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';

const API_BASE_URL = import.meta.env.VITE_API_BASE_URL;

Expand All @@ -26,7 +29,6 @@ const LibrarianOrders: React.FC = () => {
const [realizationMessage, setRealizationMessage] = React.useState('');
const [realizationMessageType, setRealizationMessageType] = React.useState<'success' | 'error' | ''>('');


// Orders
const [orderDetails, setOrderDetails] = useState<OrderDetails[]>([]);
const [showRejectionInput, setShowRejectionInput] = useState(false);
Expand Down Expand Up @@ -67,6 +69,13 @@ const LibrarianOrders: React.FC = () => {

const navigate = useNavigate();

useWebSocketNewOrderNotification('librarian/orders/pending', () => {
toast.info("Otrzymano nowe zamówienie!", {
position: "bottom-right",
});
console.log("New order received!");
});

// Orders ----------------------------------------------------------------------------------------------------------
useEffect(() => {
fetchOrderDetails();
Expand Down Expand Up @@ -234,7 +243,7 @@ const LibrarianOrders: React.FC = () => {
};

return (
<div className="bg-[#314757] min-h-screen">
<div className="bg-[#314757] min-h-screen h-screen overflow-hidden">
<header
className="flex justify-around p-1 pr-4 space-x-2.5 bg-[#3B576C] text-white sticky top-0 z-[1000] shadow-md">
<div>
Expand Down
10 changes: 10 additions & 0 deletions web_app/src/Librarian/LibrarianReaders.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import React, {useState} from 'react';
import {Link, useNavigate} from 'react-router-dom';
import {useWebSocketNewOrderNotification} from './useWebSocketNewOrderNotification.tsx';
import {toast} from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';

const API_BASE_URL = import.meta.env.VITE_API_BASE_URL;

Expand All @@ -25,6 +28,13 @@ const LibrarianReaders: React.FC = () => {

const navigate = useNavigate();

useWebSocketNewOrderNotification('librarian/orders/pending', () => {
toast.info("Otrzymano nowe zamówienie!", {
position: "bottom-right",
});
console.log("New order received!");
});

// Readers ---------------------------------------------------------------------------------------------------------
const handleCreateLibraryCard = async () => {
const token = localStorage.getItem('access_token');
Expand Down
120 changes: 115 additions & 5 deletions web_app/src/Librarian/LibrarianSettings.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,70 @@
import React from 'react';
import React, { useState } from 'react';
import {Link, useNavigate} from 'react-router-dom';
import {useWebSocketNewOrderNotification} from './useWebSocketNewOrderNotification.tsx';
import {toast} from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';

// const API_BASE_URL = import.meta.env.VITE_API_BASE_URL;
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL;

const LibrarianSettings: React.FC = () => {
const [oldPassword, setOldPassword] = useState('');
const [newPassword, setNewPassword] = useState('');
const [repeatPassword, setRepeatPassword] = useState('');
const [message, setMessage] = useState('');
const [error, setError] = useState('');

useWebSocketNewOrderNotification('librarian/orders/pending', () => {
toast.info("Otrzymano nowe zamówienie!", {
position: "bottom-right",
});
console.log("New order received!");
});

const handleChangePassword = async (e: React.FormEvent) => {
e.preventDefault();
setMessage('');
setError('');

if (newPassword !== repeatPassword) {
setError('Hasła nie pasują do siebie.');
return;
}

try {
const token = localStorage.getItem('access_token');
if (!token) throw new Error('Brak tokenu autoryzacyjnego.');

const response = await fetch(`${API_BASE_URL}/api/users/change-password`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`,
},
body: JSON.stringify({
oldPassword,
newPassword,
}),
});

const data = await response.json();

if (!response.ok) {
throw new Error(data.message || 'Nie udało się zmienić hasła.');
}

setMessage('Hasło zostało zmienione.');
setOldPassword('');
setNewPassword('');
setRepeatPassword('');
} catch (err: unknown) {
if (err instanceof Error) {
setError(err.message);
} else {
setError('Wystąpił nieznany błąd.');
}
}
};

const navigate = useNavigate();

const handleLogout = () => {
Expand Down Expand Up @@ -50,10 +111,59 @@ const LibrarianSettings: React.FC = () => {
</header>

<main className="flex justify-center items-center p-9 w-full max-w-[800vw]">
<div>
<h1>Ustawienia będą tutaj</h1>
</div>
<div className="bg-white p-9 rounded-2xl shadow-md h-[80%] max-h-[90%] w-[65%]">
<form
onSubmit={handleChangePassword}
className="w-full"
>
<h2 className="text-3xl p-4 font-semibold mb-10 text-gray-600 text-center">Zmień hasło</h2>

<div className="mb-4">
<label className="block mb-1 text-gray-600 text-xl">Stare hasło:</label>
<input
type="password"
value={oldPassword}
onChange={(e) => setOldPassword(e.target.value)}
className="text-lg w-full p-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-[#3B576C]"
required
/>
</div>

<div className="block mb-5 text-gray-600 text-lg">
<label className="block mb-1">Nowe hasło:</label>
<input
type="password"
value={newPassword}
onChange={(e) => setNewPassword(e.target.value)}
className="text-lg text-black w-full p-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-[#3B576C]"
required
/>
</div>

<div className="block mb-5 text-gray-600 text-lg"> {/* ✅ NEW FIELD */}
<label className="block mb-1">Powtórz hasło:</label>
<input
type="password"
value={repeatPassword}
onChange={(e) => setRepeatPassword(e.target.value)}
className="text-lg text-black w-full p-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-[#3B576C]"
required
/>
</div>

<div className="flex justify-center mt-4">
<button
type="submit"
className="mb-5 mt-5 w-[15vw] py-2 px-4 bg-[#3B576C] text-white rounded-md hover:bg-[#314757] ease-out duration-300"
>
Zmień hasło
</button>
</div>

{message && <p className="text-green-400 mt-4">{message}</p>}
{error && <p className="text-red-400 mt-4">{error}</p>}
</form>
</div>
</main>
</div>
);
Expand Down