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 = () => { } /> } /> } /> + } /> { 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 ab5131c1..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", }); @@ -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/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}

} +
+
); diff --git a/web_app/src/SystemAdmin/SystemAdminHomePage.tsx b/web_app/src/SystemAdmin/SystemAdminHomePage.tsx index 513b52ef..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; @@ -30,7 +33,6 @@ const formatDate = (isoString: string) => { }; const SystemAdminDashboard: React.FC = () => { - const [email, setEmail] = useState(null); const [activeSection, setActiveSection] = useState('librarySubmissions'); const [driverApplications, setDriverApplications] = useState([]); const [libraryRequests, setLibraryRequests] = useState([]); @@ -42,8 +44,22 @@ const SystemAdminDashboard: React.FC = () => { const firstLoad = useRef(true); - const getEmail = () => { - return localStorage.getItem('email'); + 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'); } const handleLogout = () => { @@ -60,11 +76,6 @@ const SystemAdminDashboard: React.FC = () => { return; } - const userEmail = getEmail(); - if (userEmail) { - setEmail(userEmail); - } - if (activeSection === 'driverSubmissions') { setDriverApplications([]); setDriverPage(0); @@ -178,9 +189,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; 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 ) {