6 Commits

7 changed files with 52 additions and 15 deletions

9
RELEASE_NOTES.md Normal file
View File

@@ -0,0 +1,9 @@
## Release Notes
### New Features
- Changed **web panel** backend, so you don't need to set the API key anymore. [Web Panel](https://weather.the1s.de)
- You can now see the **last updated** time in the web panel.
### Improvements
- Changed Website Icons to lucide react icons
- Changed overall handling of the panel

View File

@@ -53,7 +53,7 @@ const ChangeAPI: React.FC<Props> = ({ currentAPIKey, onClose }) => {
</div>
<button
type="submit"
className="bg-gradient-to-r from-blue-600 to-blue-400 text-white font-bold px-6 py-3 rounded-xl shadow-lg hover:from-blue-700 hover:to-blue-500 transition-all"
className="cursor-pointer bg-gradient-to-r from-blue-600 to-blue-400 text-white font-bold px-6 py-3 rounded-xl shadow-lg hover:from-blue-700 hover:to-blue-500 transition-all"
onClick={handleUpdate}
>
Update API Key

View File

@@ -47,7 +47,7 @@ const ChangePreferences: React.FC<Props> = ({ onClose }) => {
Temperature Unit
</label>
<select
className="w-full border-2 border-blue-200 rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-400 transition"
className="cursor-pointer w-full border-2 border-blue-200 rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-400 transition"
value={unit}
onChange={(e) => setUnit(e.target.value)}
>
@@ -63,7 +63,7 @@ const ChangePreferences: React.FC<Props> = ({ onClose }) => {
Theme
</label>
<select
className="w-full border-2 border-blue-200 rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-400 transition"
className="cursor-pointer w-full border-2 border-blue-200 rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-400 transition"
value={theme}
onChange={(e) => setTheme(e.target.value)}
>
@@ -77,7 +77,7 @@ const ChangePreferences: React.FC<Props> = ({ onClose }) => {
{/* Submit */}
<button
type="submit"
className="w-full bg-gradient-to-r from-blue-600 to-blue-400 text-white py-3 rounded-xl font-bold text-lg shadow-lg hover:from-blue-700 hover:to-blue-500 transition-all focus:outline-none focus:ring-2 focus:ring-blue-400"
className="cursor-pointer w-full bg-gradient-to-r from-blue-600 to-blue-400 text-white py-3 rounded-xl font-bold text-lg shadow-lg hover:from-blue-700 hover:to-blue-500 transition-all focus:outline-none focus:ring-2 focus:ring-blue-400"
>
Save Preferences
</button>

View File

@@ -21,14 +21,14 @@ const Header: React.FC = () => {
className="w-10 h-10 drop-shadow-lg"
/>
<h1 className="text-4xl font-extrabold tracking-wide drop-shadow-lg">
Weather App
Weather App - <strong>Local hosted</strong>
</h1>
</div>
<div className="flex items-center gap-4">
{" "}
{/* Added container for buttons */}
<button
className="bg-white text-blue-700 font-bold px-6 py-3 rounded-xl shadow-lg hover:bg-blue-100 transition-all border border-blue-200 flex items-center gap-2"
className="cursor-pointer bg-white text-blue-700 font-bold px-6 py-3 rounded-xl shadow-lg hover:bg-blue-100 transition-all border border-blue-200 flex items-center gap-2"
onClick={() => setApiCard(true)}
>
<span className="flex items-center gap-2">
@@ -37,7 +37,7 @@ const Header: React.FC = () => {
</span>
</button>
<button
className="bg-white text-blue-700 font-bold px-6 py-3 rounded-xl shadow-lg hover:bg-blue-100 transition-all border border-blue-200 flex items-center gap-2"
className="cursor-pointer bg-white text-blue-700 font-bold px-6 py-3 rounded-xl shadow-lg hover:bg-blue-100 transition-all border border-blue-200 flex items-center gap-2"
onClick={() => setPreferencesCard(true)}
>
<span className="flex items-center gap-2">
@@ -50,7 +50,7 @@ const Header: React.FC = () => {
target="_blank"
rel="noopener noreferrer"
>
<button className="bg-white text-blue-700 font-bold px-6 py-3 rounded-xl shadow-lg hover:bg-blue-100 transition-all border border-blue-200 flex items-center gap-2">
<button className="cursor-help bg-white text-blue-700 font-bold px-6 py-3 rounded-xl shadow-lg hover:bg-blue-100 transition-all border border-blue-200 flex items-center gap-2">
<span className="flex items-center gap-2">
<Github /> Docs
</span>

View File

@@ -1,5 +1,6 @@
import React from "react";
import { Sunrise, Sunset } from "lucide-react";
import { getDateTime } from "../utils/utils";
const WeatherData: React.FC = () => {
const weatherRaw = localStorage.getItem("weather");
@@ -75,6 +76,11 @@ const WeatherData: React.FC = () => {
: "--:--"}
</span>
</div>
<div className="flex items-center gap-3">
<span className="text-base text-gray-600 italic font-semibold">
Last updated: {getDateTime()}
</span>
</div>
</div>
</div>
</div>

View File

@@ -9,7 +9,6 @@ import { X } from "lucide-react";
const WeatherCard: React.FC = () => {
const [city, setCity] = useState("");
const [loading, setLoading] = useState(false);
const [weatherData, setWeatherData] = useState(false);
const getAPIKey = () => Cookies.get("apiKey") || "";
const getUnit = () => localStorage.getItem("unit") || "metric";
@@ -20,13 +19,10 @@ const WeatherCard: React.FC = () => {
const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
setLoading(true);
toast
.promise(fetchWeather(city, getAPIKey(), getUnit()), {
pending: "Fetching weather data...",
success: "Weather data loaded successfully!",
error:
"Failed to load weather data. Please check your entered city name. (Error: x4040)",
})
.then(() => {
if (localStorage.getItem("weather")) {
@@ -34,7 +30,6 @@ const WeatherCard: React.FC = () => {
} else {
setWeatherData(false);
}
setLoading(false);
});
};
@@ -55,6 +50,9 @@ const WeatherCard: React.FC = () => {
<p className="mb-2 text-gray-600">
Current weather will be displayed here.
</p>
<p className="mb-2 text-gray-600">
<strong>Make sure to set your API key in the header section.</strong>
</p>
<form onSubmit={handleSubmit} className="flex flex-col gap-4 mt-4">
<label htmlFor="city" className="font-medium text-gray-700">
Enter City:
@@ -72,7 +70,7 @@ const WeatherCard: React.FC = () => {
<div className="flex items-center gap-2">
<button
type="submit"
className="flex-1 bg-gradient-to-r from-blue-600 to-blue-400 text-white font-bold px-4 py-3 rounded-xl shadow-lg hover:from-blue-700 hover:to-blue-500 transition-all"
className="cursor-pointer flex-1 bg-gradient-to-r from-blue-600 to-blue-400 text-white font-bold px-4 py-3 rounded-xl shadow-lg hover:from-blue-700 hover:to-blue-500 transition-all"
>
Get Weather
</button>
@@ -83,7 +81,7 @@ const WeatherCard: React.FC = () => {
setWeatherData(false);
localStorage.removeItem("weather");
}}
className="flex-shrink-0 bg-red-500 hover:bg-red-600 text-white rounded-xl p-3 shadow-lg transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-red-400"
className="cursor-pointer flex-shrink-0 bg-red-500 hover:bg-red-600 text-white rounded-xl p-3 shadow-lg transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-red-400"
aria-label="Close weather data"
>
<X />

View File

@@ -0,0 +1,24 @@
export const getDateTime = () => {
const now = new Date();
const day = now.getDate();
const month = now.toLocaleString("en-GB", { month: "long" });
const hours = now.getHours().toString().padStart(2, "0");
const minutes = now.getMinutes().toString().padStart(2, "0");
// Ordinal suffix
const getOrdinal = (n: number) => {
if (n > 3 && n < 21) return "th";
switch (n % 10) {
case 1:
return "st";
case 2:
return "nd";
case 3:
return "rd";
default:
return "th";
}
};
return `${day}${getOrdinal(day)} ${month}, ${hours}:${minutes}`;
};