feat: enhance weather fetching experience with loading state and notifications
This commit is contained in:
@@ -13,8 +13,14 @@ const Layout: React.FC<LayoutProps> = ({ children }) => {
|
||||
<ToastContainer
|
||||
position="top-right"
|
||||
autoClose={3000}
|
||||
hideProgressBar
|
||||
theme="colored"
|
||||
hideProgressBar={false}
|
||||
newestOnTop
|
||||
closeOnClick
|
||||
rtl={false}
|
||||
pauseOnFocusLoss={false}
|
||||
draggable
|
||||
pauseOnHover
|
||||
theme="dark"
|
||||
/>
|
||||
<main className="flex-1 container mx-auto px-4 py-8">{children}</main>
|
||||
</div>
|
||||
|
@@ -1,11 +1,12 @@
|
||||
import { useState } from "react";
|
||||
import React from "react";
|
||||
import { changeAPIcookie } from "../utils/changeAPIcookie";
|
||||
|
||||
interface Props {
|
||||
currentAPIKey: string;
|
||||
}
|
||||
|
||||
function ChangeAPI({ currentAPIKey }: Props) {
|
||||
const ChangeAPI: React.FC<Props> = ({ currentAPIKey }) => {
|
||||
const [apiKey, setApiKey] = useState(currentAPIKey);
|
||||
|
||||
return (
|
||||
@@ -14,6 +15,13 @@ function ChangeAPI({ currentAPIKey }: Props) {
|
||||
<p className="mb-6 text-gray-600">
|
||||
Update your API key to fetch weather data.
|
||||
</p>
|
||||
<p className="mb-6 text-gray-600">
|
||||
We are using{" "}
|
||||
<a href="https://openweathermap.org/api">
|
||||
<strong>OpenWeatherMap</strong>
|
||||
</a>{" "}
|
||||
API for fetching weather data.
|
||||
</p>
|
||||
<form className="flex flex-col gap-4">
|
||||
<label htmlFor="apiKey" className="font-medium text-gray-700">
|
||||
API Key:
|
||||
@@ -38,6 +46,6 @@ function ChangeAPI({ currentAPIKey }: Props) {
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default ChangeAPI;
|
||||
|
@@ -21,7 +21,7 @@ const Header: React.FC = () => {
|
||||
</button>
|
||||
</header>
|
||||
{apiCard && (
|
||||
<div className="fixed inset-0 bg-black bg-opacity-40 flex items-center justify-center z-50">
|
||||
<div className="fixed inset-0 bg-gray-800 bg-opacity-40 flex items-center justify-center z-50">
|
||||
<div className="bg-white rounded-xl shadow-lg p-8 w-full max-w-md relative">
|
||||
<button
|
||||
className="absolute top-4 right-4 text-gray-400 hover:text-blue-600 text-xl"
|
||||
|
15
frontend/src/components/IsLoading.tsx
Normal file
15
frontend/src/components/IsLoading.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import React from "react";
|
||||
|
||||
interface Props {
|
||||
message: string;
|
||||
}
|
||||
|
||||
const IsLoading: React.FC<Props> = ({ message }) => {
|
||||
return (
|
||||
<div>
|
||||
<p>{message}</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default IsLoading;
|
@@ -2,18 +2,27 @@ import React from "react";
|
||||
import { useState } from "react";
|
||||
import { fetchWeather } from "../utils/apiFunc";
|
||||
import Cookies from "js-cookie";
|
||||
import IsLoading from "./IsLoading";
|
||||
import { toast } from "react-toastify";
|
||||
|
||||
const WeatherCard: React.FC = () => {
|
||||
const [city, setCity] = useState("");
|
||||
const [loading, setLoading] = useState(false);
|
||||
const getAPIKey = () => Cookies.get("apiKey") || "";
|
||||
|
||||
const handleCityChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setCity(event.target.value);
|
||||
};
|
||||
|
||||
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
|
||||
const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
|
||||
event.preventDefault();
|
||||
fetchWeather(city, getAPIKey());
|
||||
setLoading(true);
|
||||
toast.promise(fetchWeather(city, getAPIKey()), {
|
||||
pending: "Fetching weather data...",
|
||||
success: "Weather data loaded successfully!",
|
||||
error: "Error loading weather data!",
|
||||
});
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -33,6 +42,7 @@ const WeatherCard: React.FC = () => {
|
||||
/>
|
||||
<button type="submit">Get Weather</button>
|
||||
</form>
|
||||
{loading && <IsLoading message="Loading weather data..." />}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@@ -1,12 +1,9 @@
|
||||
import { myToast } from "./toastify";
|
||||
|
||||
export const fetchWeather = async (city: string, apiKey: string) => {
|
||||
// Get location data
|
||||
const location = await fetch(
|
||||
`http://api.openweathermap.org/geo/1.0/direct?q=${city}&appid=${apiKey}`
|
||||
).then((response) => {
|
||||
if (response.status === 401) {
|
||||
myToast("Request Failed! Check API key and city name.", "error");
|
||||
throw new Error("Network response was not ok");
|
||||
} else if (response.ok) {
|
||||
return response.json();
|
||||
@@ -20,5 +17,4 @@ export const fetchWeather = async (city: string, apiKey: string) => {
|
||||
`https://api.openweathermap.org/data/2.5/weather?lat=${lat}&lon=${lon}&appid=${apiKey}`
|
||||
).then((response) => response.json());
|
||||
console.log(weather);
|
||||
myToast("Successfully fetched weather data", "success");
|
||||
};
|
||||
|
@@ -5,7 +5,7 @@ export type ToastType = "success" | "error" | "info" | "warning";
|
||||
export const myToast = (message: string, msgType: ToastType) => {
|
||||
let config: ToastOptions = {
|
||||
position: "top-right",
|
||||
autoClose: 5000,
|
||||
autoClose: 3000,
|
||||
hideProgressBar: false,
|
||||
closeOnClick: true,
|
||||
pauseOnHover: true,
|
||||
|
Reference in New Issue
Block a user