diff --git a/web_app/src/Librarian/LibrarianAddBook.tsx b/web_app/src/Librarian/LibrarianAddBook.tsx index 4da762ee..490f718b 100644 --- a/web_app/src/Librarian/LibrarianAddBook.tsx +++ b/web_app/src/Librarian/LibrarianAddBook.tsx @@ -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; @@ -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]); diff --git a/web_app/src/Librarian/LibrarianHomePage.tsx b/web_app/src/Librarian/LibrarianHomePage.tsx index 4785dd63..f352a040 100644 --- a/web_app/src/Librarian/LibrarianHomePage.tsx +++ b/web_app/src/Librarian/LibrarianHomePage.tsx @@ -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; @@ -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(); @@ -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'); @@ -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(); @@ -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()}`, { @@ -265,6 +277,7 @@ const LibrarianHomePage: React.FC = () => { console.error("Error: ", error); } finally { setIsLoading(false); + isFetchingRef.current = false; } }; diff --git a/web_app/src/Librarian/LibrarianLogin.tsx b/web_app/src/Librarian/LibrarianLogin.tsx index 381c2d26..245adf7c 100644 --- a/web_app/src/Librarian/LibrarianLogin.tsx +++ b/web_app/src/Librarian/LibrarianLogin.tsx @@ -142,7 +142,7 @@ const LibrarianLogin: React.FC = () => {
+ 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)]">

Logowanie bibliotekarza

diff --git a/web_app/src/Librarian/LibrarianOrders.tsx b/web_app/src/Librarian/LibrarianOrders.tsx index 9c2a398a..c5afcee7 100644 --- a/web_app/src/Librarian/LibrarianOrders.tsx +++ b/web_app/src/Librarian/LibrarianOrders.tsx @@ -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; @@ -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([]); const [showRejectionInput, setShowRejectionInput] = useState(false); @@ -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(); @@ -234,7 +243,7 @@ const LibrarianOrders: React.FC = () => { }; return ( -
+
diff --git a/web_app/src/Librarian/LibrarianReaders.tsx b/web_app/src/Librarian/LibrarianReaders.tsx index d19570d5..806db30c 100644 --- a/web_app/src/Librarian/LibrarianReaders.tsx +++ b/web_app/src/Librarian/LibrarianReaders.tsx @@ -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; @@ -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'); diff --git a/web_app/src/Librarian/LibrarianSettings.tsx b/web_app/src/Librarian/LibrarianSettings.tsx index bc6bcd53..ab5131c1 100644 --- a/web_app/src/Librarian/LibrarianSettings.tsx +++ b/web_app/src/Librarian/LibrarianSettings.tsx @@ -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 = () => { @@ -50,10 +111,59 @@ const LibrarianSettings: React.FC = () => {
-
-

Ustawienia będą tutaj

-
+
+
+

Zmień hasło

+ +
+ + 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 + /> +
+
+ + 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 + /> +
+ +
{/* ✅ NEW FIELD */} + + 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 + /> +
+ +
+ +
+ + {message &&

{message}

} + {error &&

{error}

} +
+
);