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:
75
react-vite-client/src/pages/WeatherPage/WeatherPage.tsx
Normal file
75
react-vite-client/src/pages/WeatherPage/WeatherPage.tsx
Normal 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;
|
||||
Reference in New Issue
Block a user