From f8c4b8b6fd24d9f0c6a2c1a00a362df96a70e968 Mon Sep 17 00:00:00 2001 From: jpie02 <128189069+jpie02@users.noreply.github.com> Date: Thu, 31 Jul 2025 17:08:14 +0200 Subject: [PATCH 1/4] Add password change page to the Librarian Dashboard. --- web_app/src/Librarian/LibrarianSettings.tsx | 9 +-- .../useWebSocketNewLibraryNotification.tsx | 57 +++++++++++++++++++ 2 files changed, 62 insertions(+), 4 deletions(-) create mode 100644 web_app/src/SystemAdmin/useWebSocketNewLibraryNotification.tsx diff --git a/web_app/src/Librarian/LibrarianSettings.tsx b/web_app/src/Librarian/LibrarianSettings.tsx index ab5131c1..11b5d8e7 100644 --- a/web_app/src/Librarian/LibrarianSettings.tsx +++ b/web_app/src/Librarian/LibrarianSettings.tsx @@ -46,10 +46,11 @@ const LibrarianSettings: React.FC = () => { }), }); - const data = await response.json(); + const text = await response.text(); if (!response.ok) { - throw new Error(data.message || 'Nie udało się zmienić hasła.'); + const errorData = text ? JSON.parse(text) : { message: 'Błąd zmiany hasła' }; + throw new Error(errorData.message); } setMessage('Hasło zostało zmienione.'); @@ -160,8 +161,8 @@ const LibrarianSettings: React.FC = () => { - {message &&

{message}

} - {error &&

{error}

} + {message &&

{message}

} + {error &&

{error}

} diff --git a/web_app/src/SystemAdmin/useWebSocketNewLibraryNotification.tsx b/web_app/src/SystemAdmin/useWebSocketNewLibraryNotification.tsx new file mode 100644 index 00000000..b0ccb047 --- /dev/null +++ b/web_app/src/SystemAdmin/useWebSocketNewLibraryNotification.tsx @@ -0,0 +1,57 @@ +import { useEffect, useRef } from "react"; + +export function useWebSocketNewLibraryNotification( + channel: string, + onMessage: (msg: string) => void +) { + const wsRef = useRef(null); + const keepAliveRef = useRef(null); + + useEffect(() => { + const token = localStorage.getItem("access_token"); + if (!token) return; + + const apiBaseUrl = import.meta.env.VITE_API_BASE_URL?.replace(/^https?:\/\//, "") || "localhost"; + const wsUrl = `wss://${apiBaseUrl}/ws?token=${encodeURIComponent(token)}&channel=${encodeURIComponent(channel)}`; + + console.log("Connecting to:", wsUrl); + + const ws = new WebSocket(wsUrl); + wsRef.current = ws; + + ws.onopen = () => { + console.log("WebSocket opened:", wsUrl); + + // Keep-alive ping for the established WS connection + keepAliveRef.current = setInterval(() => { + if (ws.readyState === WebSocket.OPEN) { + ws.send("ping"); + } + }, 30000); + }; + + ws.onmessage = (event) => { + onMessage(event.data); + }; + + ws.onerror = (err) => { + console.error("WebSocket error:", err); + }; + + ws.onclose = (event) => { + console.warn("WebSocket closed", event); + if (keepAliveRef.current) { + clearInterval(keepAliveRef.current); + } + }; + + return () => { + console.log("Cleaning up WebSocket connection"); + if (keepAliveRef.current) { + clearInterval(keepAliveRef.current); + } + ws.close(); + wsRef.current = null; + }; + }, [channel]); +} From 8cf75171bdb22cbf36e44703c50446f825aedbd8 Mon Sep 17 00:00:00 2001 From: jpie02 <128189069+jpie02@users.noreply.github.com> Date: Thu, 31 Jul 2025 17:27:19 +0200 Subject: [PATCH 2/4] Add password change page to the Library Administrator Dashboard. --- .../LibraryAdmin/LibraryAdminAddLibrary.tsx | 10 -- .../src/LibraryAdmin/LibraryAdminSettings.tsx | 122 +++++++++++++++++- 2 files changed, 117 insertions(+), 15 deletions(-) diff --git a/web_app/src/LibraryAdmin/LibraryAdminAddLibrary.tsx b/web_app/src/LibraryAdmin/LibraryAdminAddLibrary.tsx index b7250c9f..0bf996e0 100644 --- a/web_app/src/LibraryAdmin/LibraryAdminAddLibrary.tsx +++ b/web_app/src/LibraryAdmin/LibraryAdminAddLibrary.tsx @@ -70,10 +70,6 @@ const LibraryAdminAddLibrary: React.FC = () => { } }; - const handleSettings = () => { - alert('Ustawienia'); - }; - return (
{ src="/book-rider-high-resolution-logo.png" />
-
-
-

Ustawienia

-
+
+
+

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}

} +
+
); From ea71d43b808718fcd47dcfdd343aa6e661334787 Mon Sep 17 00:00:00 2001 From: jpie02 <128189069+jpie02@users.noreply.github.com> Date: Thu, 31 Jul 2025 17:53:12 +0200 Subject: [PATCH 3/4] Add password change page to the System Administrator Dashboard. --- web_app/src/App.tsx | 2 + .../src/SystemAdmin/SystemAdminHomePage.tsx | 16 +- web_app/src/SystemAdmin/SystemAdminLogin.tsx | 2 +- .../src/SystemAdmin/SystemAdminSettings.tsx | 147 ++++++++++++++++++ 4 files changed, 155 insertions(+), 12 deletions(-) create mode 100644 web_app/src/SystemAdmin/SystemAdminSettings.tsx diff --git a/web_app/src/App.tsx b/web_app/src/App.tsx index ecfcf6fd..95a155e3 100644 --- a/web_app/src/App.tsx +++ b/web_app/src/App.tsx @@ -9,6 +9,7 @@ import SysAdminLogin from "./SystemAdmin/SystemAdminLogin.tsx"; import SystemAdminDashboard from './SystemAdmin/SystemAdminHomePage'; import SubmissionDetailsLibrary from './SystemAdmin/SubmissionDetailsLibrary.tsx'; import SubmissionDetailsDriver from './SystemAdmin/SubmissionDetailsDriver.tsx'; +import SystemAdminSettings from './SystemAdmin/SystemAdminSettings.tsx'; // Library Administrator import RegistrationForm from './LibraryAdmin/LibraryAdminRegisterForm.tsx'; @@ -66,6 +67,7 @@ const App: React.FC = () => { } /> } /> } /> + } /> { }; const SystemAdminDashboard: React.FC = () => { - const [email, setEmail] = useState(null); const [activeSection, setActiveSection] = useState('librarySubmissions'); const [driverApplications, setDriverApplications] = useState([]); const [libraryRequests, setLibraryRequests] = useState([]); @@ -42,8 +41,8 @@ const SystemAdminDashboard: React.FC = () => { const firstLoad = useRef(true); - const getEmail = () => { - return localStorage.getItem('email'); + const handleChangePassword = ()=> { + navigate('/system-admin-settings'); } const handleLogout = () => { @@ -60,11 +59,6 @@ const SystemAdminDashboard: React.FC = () => { return; } - const userEmail = getEmail(); - if (userEmail) { - setEmail(userEmail); - } - if (activeSection === 'driverSubmissions') { setDriverApplications([]); setDriverPage(0); @@ -178,9 +172,9 @@ const SystemAdminDashboard: React.FC = () => {
- {email && ( - {email} - )} + diff --git a/web_app/src/SystemAdmin/SystemAdminLogin.tsx b/web_app/src/SystemAdmin/SystemAdminLogin.tsx index f3d51e52..6dd50cae 100644 --- a/web_app/src/SystemAdmin/SystemAdminLogin.tsx +++ b/web_app/src/SystemAdmin/SystemAdminLogin.tsx @@ -153,7 +153,7 @@ const SysAdminLogin: React.FC = () => {
+ className="absolute top-[25%] left-1/2 transform -translate-x-1/2 bg-white p-10 rounded-xl shadow-2xl w-[clamp(280px,30vw,400px)] h-[clamp(500px,55vh,90vh)]">

Logowanie
administratora systemów

diff --git a/web_app/src/SystemAdmin/SystemAdminSettings.tsx b/web_app/src/SystemAdmin/SystemAdminSettings.tsx new file mode 100644 index 00000000..d0da3cde --- /dev/null +++ b/web_app/src/SystemAdmin/SystemAdminSettings.tsx @@ -0,0 +1,147 @@ +import React, {useState} from 'react'; +import {useNavigate} from 'react-router-dom'; + +const API_BASE_URL = import.meta.env.VITE_API_BASE_URL; + +const SystemAdminSettings: React.FC = () => { + const [oldPassword, setOldPassword] = useState(''); + const [newPassword, setNewPassword] = useState(''); + const [repeatPassword, setRepeatPassword] = useState(''); + const [message, setMessage] = useState(''); + const [error, setError] = useState(''); + + 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 text = await response.text(); + + if (!response.ok) { + const errorData = text ? JSON.parse(text) : { message: 'Błąd zmiany hasła' }; + throw new Error(errorData.message); + } + + 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(); + + function handleReturn() { + navigate('/system-admin-dashboard'); + } + + return ( +
+
+
+ Book Rider Logo +
+
+
+
+
+
+

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}

} +
+
+
+
+
+ ); +}; + +export default SystemAdminSettings; From cee8615fba575ac0f83beaaef6b5180bca6ec235 Mon Sep 17 00:00:00 2001 From: jpie02 <128189069+jpie02@users.noreply.github.com> Date: Mon, 22 Sep 2025 16:49:40 +0200 Subject: [PATCH 4/4] Fix the cloned useWebSocketNotification.tsx file issue. --- web_app/src/Librarian/LibrarianAddBook.tsx | 4 +- web_app/src/Librarian/LibrarianHomePage.tsx | 4 +- web_app/src/Librarian/LibrarianOrders.tsx | 4 +- web_app/src/Librarian/LibrarianReaders.tsx | 4 +- web_app/src/Librarian/LibrarianReturns.tsx | 4 +- web_app/src/Librarian/LibrarianSettings.tsx | 4 +- .../src/LibraryAdmin/LibraryAdminSettings.tsx | 4 +- .../src/SystemAdmin/SystemAdminHomePage.tsx | 17 ++++++ .../useWebSocketNewLibraryNotification.tsx | 57 ------------------- .../useWebSocketNotification.tsx} | 2 +- 10 files changed, 32 insertions(+), 72 deletions(-) delete mode 100644 web_app/src/SystemAdmin/useWebSocketNewLibraryNotification.tsx rename web_app/src/{Librarian/useWebSocketNewOrderNotification.tsx => Utils/useWebSocketNotification.tsx} (97%) diff --git a/web_app/src/Librarian/LibrarianAddBook.tsx b/web_app/src/Librarian/LibrarianAddBook.tsx index 490f718b..4898b6c3 100644 --- a/web_app/src/Librarian/LibrarianAddBook.tsx +++ b/web_app/src/Librarian/LibrarianAddBook.tsx @@ -1,6 +1,6 @@ import React, { useState, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; -import {useWebSocketNewOrderNotification} from './useWebSocketNewOrderNotification.tsx'; +import {useWebSocketNotification} from '../Utils/useWebSocketNotification.tsx'; import {toast} from 'react-toastify'; import 'react-toastify/dist/ReactToastify.css'; @@ -86,7 +86,7 @@ const LibrarianAddBook: React.FC = () => { fetchDropdownData(); }, []); - useWebSocketNewOrderNotification('librarian/orders/pending', () => { + useWebSocketNotification('librarian/orders/pending', () => { toast.info("Otrzymano nowe zamówienie!", { position: "bottom-right", }); diff --git a/web_app/src/Librarian/LibrarianHomePage.tsx b/web_app/src/Librarian/LibrarianHomePage.tsx index f352a040..c8c16357 100644 --- a/web_app/src/Librarian/LibrarianHomePage.tsx +++ b/web_app/src/Librarian/LibrarianHomePage.tsx @@ -1,6 +1,6 @@ import React, {useEffect, useState, useRef} from 'react'; import {Link, useNavigate} from 'react-router-dom'; -import {useWebSocketNewOrderNotification} from './useWebSocketNewOrderNotification.tsx'; +import {useWebSocketNotification} from '../Utils/useWebSocketNotification.tsx'; import {toast} from 'react-toastify'; import 'react-toastify/dist/ReactToastify.css'; @@ -135,7 +135,7 @@ const LibrarianHomePage: React.FC = () => { return () => window.removeEventListener('scroll', handleScroll); }, [isLoading, hasMore, page, isUserLibraryChecked]); - useWebSocketNewOrderNotification('librarian/orders/pending', () => { + useWebSocketNotification('librarian/orders/pending', () => { toast.info("Otrzymano nowe zamówienie!", { position: "bottom-right", }); diff --git a/web_app/src/Librarian/LibrarianOrders.tsx b/web_app/src/Librarian/LibrarianOrders.tsx index c5afcee7..8f3505f4 100644 --- a/web_app/src/Librarian/LibrarianOrders.tsx +++ b/web_app/src/Librarian/LibrarianOrders.tsx @@ -1,6 +1,6 @@ import React, {useState, useEffect } from 'react'; import {Link, useNavigate} from 'react-router-dom'; -import {useWebSocketNewOrderNotification} from './useWebSocketNewOrderNotification.tsx'; +import {useWebSocketNotification} from '../Utils/useWebSocketNotification.tsx'; import {toast} from 'react-toastify'; import 'react-toastify/dist/ReactToastify.css'; @@ -69,7 +69,7 @@ const LibrarianOrders: React.FC = () => { const navigate = useNavigate(); - useWebSocketNewOrderNotification('librarian/orders/pending', () => { + useWebSocketNotification('librarian/orders/pending', () => { toast.info("Otrzymano nowe zamówienie!", { position: "bottom-right", }); diff --git a/web_app/src/Librarian/LibrarianReaders.tsx b/web_app/src/Librarian/LibrarianReaders.tsx index 806db30c..0b538789 100644 --- a/web_app/src/Librarian/LibrarianReaders.tsx +++ b/web_app/src/Librarian/LibrarianReaders.tsx @@ -1,6 +1,6 @@ import React, {useState} from 'react'; import {Link, useNavigate} from 'react-router-dom'; -import {useWebSocketNewOrderNotification} from './useWebSocketNewOrderNotification.tsx'; +import {useWebSocketNotification} from '../Utils/useWebSocketNotification.tsx'; import {toast} from 'react-toastify'; import 'react-toastify/dist/ReactToastify.css'; @@ -28,7 +28,7 @@ const LibrarianReaders: React.FC = () => { const navigate = useNavigate(); - useWebSocketNewOrderNotification('librarian/orders/pending', () => { + useWebSocketNotification('librarian/orders/pending', () => { toast.info("Otrzymano nowe zamówienie!", { position: "bottom-right", }); diff --git a/web_app/src/Librarian/LibrarianReturns.tsx b/web_app/src/Librarian/LibrarianReturns.tsx index 53e0ed9d..a885d8b7 100644 --- a/web_app/src/Librarian/LibrarianReturns.tsx +++ b/web_app/src/Librarian/LibrarianReturns.tsx @@ -1,6 +1,6 @@ import React, {useEffect, useState} from 'react'; import {Link, useNavigate} from 'react-router-dom'; -import {useWebSocketNewOrderNotification} from './useWebSocketNewOrderNotification.tsx'; +import {useWebSocketNotification} from '../Utils/useWebSocketNotification.tsx'; import {toast} from 'react-toastify'; import 'react-toastify/dist/ReactToastify.css'; @@ -10,7 +10,7 @@ const LibrarianReturns: React.FC = () => { const [message, setMessage] = useState(null); const [messageType, setMessageType] = useState<'success' | 'error' | null>(null); - useWebSocketNewOrderNotification('librarian/orders/pending', () => { + useWebSocketNotification('librarian/orders/pending', () => { toast.info("Otrzymano nowe zamówienie!", { position: "bottom-right", }); diff --git a/web_app/src/Librarian/LibrarianSettings.tsx b/web_app/src/Librarian/LibrarianSettings.tsx index 11b5d8e7..db863552 100644 --- a/web_app/src/Librarian/LibrarianSettings.tsx +++ b/web_app/src/Librarian/LibrarianSettings.tsx @@ -1,6 +1,6 @@ import React, { useState } from 'react'; import {Link, useNavigate} from 'react-router-dom'; -import {useWebSocketNewOrderNotification} from './useWebSocketNewOrderNotification.tsx'; +import {useWebSocketNotification} from '../Utils/useWebSocketNotification.tsx'; import {toast} from 'react-toastify'; import 'react-toastify/dist/ReactToastify.css'; @@ -13,7 +13,7 @@ const LibrarianSettings: React.FC = () => { const [message, setMessage] = useState(''); const [error, setError] = useState(''); - useWebSocketNewOrderNotification('librarian/orders/pending', () => { + useWebSocketNotification('librarian/orders/pending', () => { toast.info("Otrzymano nowe zamówienie!", { position: "bottom-right", }); diff --git a/web_app/src/LibraryAdmin/LibraryAdminSettings.tsx b/web_app/src/LibraryAdmin/LibraryAdminSettings.tsx index b0c0dd91..c0ff1afe 100644 --- a/web_app/src/LibraryAdmin/LibraryAdminSettings.tsx +++ b/web_app/src/LibraryAdmin/LibraryAdminSettings.tsx @@ -1,6 +1,6 @@ import React, {useState} from 'react'; import {Link, useNavigate} from 'react-router-dom'; -import {useWebSocketNewOrderNotification} from "../Librarian/useWebSocketNewOrderNotification.tsx"; +import {useWebSocketNotification} from "../Utils/useWebSocketNotification.tsx"; import {toast} from "react-toastify"; import 'react-toastify/dist/ReactToastify.css'; @@ -13,7 +13,7 @@ const LibraryAdminSettings: React.FC = () => { const [message, setMessage] = useState(''); const [error, setError] = useState(''); - useWebSocketNewOrderNotification('librarian/orders/pending', () => { + useWebSocketNotification('librarian/orders/pending', () => { toast.info("Otrzymano nowe zamówienie!", { position: "bottom-right", }); diff --git a/web_app/src/SystemAdmin/SystemAdminHomePage.tsx b/web_app/src/SystemAdmin/SystemAdminHomePage.tsx index e3ed3474..97b83a15 100644 --- a/web_app/src/SystemAdmin/SystemAdminHomePage.tsx +++ b/web_app/src/SystemAdmin/SystemAdminHomePage.tsx @@ -1,5 +1,8 @@ import React, {useEffect, useRef, useState} from 'react'; import {useNavigate} from 'react-router-dom'; +import {useWebSocketNotification} from '../Utils/useWebSocketNotification.tsx'; +import {toast} from 'react-toastify'; +import 'react-toastify/dist/ReactToastify.css'; const API_BASE_URL = import.meta.env.VITE_API_BASE_URL; @@ -41,6 +44,20 @@ const SystemAdminDashboard: React.FC = () => { const firstLoad = useRef(true); + useWebSocketNotification('system-administrator/library-requests/pending', () => { + toast.info("Otrzymano nowe zgłoszenie biblioteki!", { + position: "bottom-right", + }); + console.log("New library request received!"); + }); + + useWebSocketNotification('system-administrator/driver-requests/pending', () => { + toast.info("Otrzymano nowe zgłoszenie kierowcy!", { + position: "bottom-right", + }); + console.log("New driver request received!"); + }); + const handleChangePassword = ()=> { navigate('/system-admin-settings'); } diff --git a/web_app/src/SystemAdmin/useWebSocketNewLibraryNotification.tsx b/web_app/src/SystemAdmin/useWebSocketNewLibraryNotification.tsx deleted file mode 100644 index b0ccb047..00000000 --- a/web_app/src/SystemAdmin/useWebSocketNewLibraryNotification.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import { useEffect, useRef } from "react"; - -export function useWebSocketNewLibraryNotification( - channel: string, - onMessage: (msg: string) => void -) { - const wsRef = useRef(null); - const keepAliveRef = useRef(null); - - useEffect(() => { - const token = localStorage.getItem("access_token"); - if (!token) return; - - const apiBaseUrl = import.meta.env.VITE_API_BASE_URL?.replace(/^https?:\/\//, "") || "localhost"; - const wsUrl = `wss://${apiBaseUrl}/ws?token=${encodeURIComponent(token)}&channel=${encodeURIComponent(channel)}`; - - console.log("Connecting to:", wsUrl); - - const ws = new WebSocket(wsUrl); - wsRef.current = ws; - - ws.onopen = () => { - console.log("WebSocket opened:", wsUrl); - - // Keep-alive ping for the established WS connection - keepAliveRef.current = setInterval(() => { - if (ws.readyState === WebSocket.OPEN) { - ws.send("ping"); - } - }, 30000); - }; - - ws.onmessage = (event) => { - onMessage(event.data); - }; - - ws.onerror = (err) => { - console.error("WebSocket error:", err); - }; - - ws.onclose = (event) => { - console.warn("WebSocket closed", event); - if (keepAliveRef.current) { - clearInterval(keepAliveRef.current); - } - }; - - return () => { - console.log("Cleaning up WebSocket connection"); - if (keepAliveRef.current) { - clearInterval(keepAliveRef.current); - } - ws.close(); - wsRef.current = null; - }; - }, [channel]); -} diff --git a/web_app/src/Librarian/useWebSocketNewOrderNotification.tsx b/web_app/src/Utils/useWebSocketNotification.tsx similarity index 97% rename from web_app/src/Librarian/useWebSocketNewOrderNotification.tsx rename to web_app/src/Utils/useWebSocketNotification.tsx index f44ed0e3..649e3e85 100644 --- a/web_app/src/Librarian/useWebSocketNewOrderNotification.tsx +++ b/web_app/src/Utils/useWebSocketNotification.tsx @@ -1,6 +1,6 @@ import { useEffect, useRef } from "react"; -export function useWebSocketNewOrderNotification( +export function useWebSocketNotification( channel: string, onMessage: (msg: string) => void ) {