Implement SignalR integration and refactor WeatherPage

Added SignalR for real-time progress updates during weather data fetch. Refactored WeatherPage to use a new reusable WeatherGrid component and SignalRHelper. Improved loading UI with a radial progress indicator.
This commit is contained in:
2025-02-09 02:00:06 +01:00
parent 72b7902b55
commit c4071786ea
7 changed files with 184 additions and 65 deletions

View File

@@ -0,0 +1,75 @@
import with_main_layout from "../../layouts/with_main_layout.ts.tsx";
import useLocalStorageState from 'use-local-storage-state'; // install via npm
import {Client, WeatherForecast} from "../../ApiCient.ts";
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import {faDownload, faTrash} from "@fortawesome/free-solid-svg-icons";
import {useState} from "react";
import WeatherGrid from "./WeatherGrid.tsx";
import {useSignalR} from "../../signalr/SignalRHelper.ts";
function WeatherPage() {
// Use the hook to persist your weather data. It will default to null.
const [weatherData, setWeatherData] = useLocalStorageState<WeatherForecast[] | null>('WeatherPage-Forecast', {defaultValue: [],});
const [loading, setLoading] = useState(false);
const [currentState, setCurrentState] = useState<string | null>(null);
const {subscribe} = useSignalR('http://localhost:5175', 'WeatherUpdateHub');
const fetchWeatherData = async () => {
const apiClient = new Client();
try {
const progressCleanup = subscribe<string>('ProgressUpdate', (_, message) => {
setCurrentState(message);
});
setLoading(true);
const data = await apiClient.getWeatherForecast();
setWeatherData(data);
setLoading(false);
progressCleanup();
setCurrentState(null);
} catch (error) {
console.error("Error fetching weather data:", error);
}
};
return (
<div className="card shadow-md card-sm w-full bg-auto-50">
<div className="card-header text-2xl ml-5 mt-1">Weather Data</div>
<div className="card-body">
<div className="row-auto flex gap-x-2 ">
<button className="btn w-48" onClick={fetchWeatherData}>
<FontAwesomeIcon className="mr-1" icon={faDownload}/>
Fetch Weather Data
</button>
<button className="btn w-48" onClick={() => setWeatherData(null)}>
<FontAwesomeIcon className="mr-1" icon={faTrash}/>
Clear Weather
</button>
</div>
<div className="mt-5">
{loading ?
<div>
{/*<p className="m-5">Progress: {currentState ? JSON.parse(currentState).percentage : 0}% - {currentState ? JSON.parse(currentState).message : ''}</p>*/}
{(() => {
const percentage = currentState ? JSON.parse(currentState).percentage : 0;
return (
<div className="radial-progress" style={{['--value' as unknown as string]: percentage}} aria-valuenow={percentage} role="progressbar">
{percentage}%
</div>
);
})()}
</div>
:
!weatherData || weatherData.length === 0 ? (
<p>No weather data, please request it!</p>
) : (
<WeatherGrid data={weatherData}/>
)}
</div>
</div>
</div>
);
}
export const PageWithLayout = with_main_layout(WeatherPage);
export default PageWithLayout;