{"id":26218238,"url":"https://github.com/sudhansuuranjan/planner-app","last_synced_at":"2026-05-03T18:35:51.557Z","repository":{"id":179242520,"uuid":"663183053","full_name":"SudhansuuRanjan/planner-app","owner":"SudhansuuRanjan","description":"A planner app using reactjs.","archived":false,"fork":false,"pushed_at":"2023-07-06T18:41:44.000Z","size":960,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-12-12T21:34:52.700Z","etag":null,"topics":["bounty","reactjs","stackup","upupstackup"],"latest_commit_sha":null,"homepage":"https://spectre-7-planner-app.vercel.app","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/SudhansuuRanjan.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2023-07-06T18:32:21.000Z","updated_at":"2023-07-06T18:46:18.000Z","dependencies_parsed_at":null,"dependency_job_id":"6f659cff-01b0-47ed-ac73-9fcb4f109827","html_url":"https://github.com/SudhansuuRanjan/planner-app","commit_stats":null,"previous_names":["sudhansuuranjan/planner-app"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/SudhansuuRanjan/planner-app","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SudhansuuRanjan%2Fplanner-app","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SudhansuuRanjan%2Fplanner-app/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SudhansuuRanjan%2Fplanner-app/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SudhansuuRanjan%2Fplanner-app/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/SudhansuuRanjan","download_url":"https://codeload.github.com/SudhansuuRanjan/planner-app/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SudhansuuRanjan%2Fplanner-app/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32580154,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-03T06:36:36.687Z","status":"ssl_error","status_checked_at":"2026-05-03T06:36:09.306Z","response_time":103,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bounty","reactjs","stackup","upupstackup"],"created_at":"2025-03-12T13:15:55.644Z","updated_at":"2026-05-03T18:35:51.549Z","avatar_url":"https://github.com/SudhansuuRanjan.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Planner App\n\n[Live Site URL](https://spectre-7-planner-app.vercel.app/)\n\n## Description\n\nThis is a simple planner app that allows you to add custom widgets to your dashboard.\n\n## Built with\n\n- ReactJs\n\n## Which widget I am trying to add and why?\n\n### Weather widget : \nI am trying to add a `weather widget` because it is a very useful widget to have on your dashboard. It allows you to see the weather forecast of your current location. It is very useful for planning your day.\n\n## My Work and How It works?\n\n- Added a `WeatherWidget` component in the `widgets` folder.\n- Added the `WeatherWidget` component in the `WidgetGalleryModal`.\n- Added a `.env` file to store the OpenWeatherMap API key.\n\n### WeatherWidget Component\n\n1. Used the `useState` hook to store the weather data, time, locationAllowed, error and loading state.\n\n```jsx\n    const [weather, setWeather] = useState(null);\n    const [time, setTime] = useState(new Date().toLocaleTimeString());\n    const [locationAllowed, setLocationAllowed] = useState(true);\n    const [error, setError] = useState(null);\n    const [loading, setLoading] = useState(false);\n```\n\n2. Used the `useEffect` hook to get the current time and update it every second.\n\n```jsx\n    useEffect(() =\u003e {\n        // Update the time every second\n        const interval = setInterval(() =\u003e {\n            setTime(new Date().toLocaleTimeString())\n        }, 1000);\n        // Clean up the interval on component unmount\n        return () =\u003e clearInterval(interval);\n    }, [time]);\n```\n3.  Used the `useEffect` hook to get the current location If the location is availabe then with the help of `getWeatherData` function we are fetching the weather data from the OpenWeatherMap API, else we are setting the `locationAllowed` state to false.\n\n    In the `getWeatherData` function we are fetching the weather data from the OpenWeatherMap API and setting the `weather` state to the fetched data. If there is any error then we are setting the `error` state to the error message.\n\n```jsx\n    async function getWeatherData(position) {\n        // get the latitude and longitude from the geolocation API\n        const latitude = position.coords.latitude;\n        const longitude = position.coords.longitude;\n\n        // fetch the weather data from the OpenWeatherMap API\n        try {\n            const response = await fetch(`https://api.openweathermap.org/data/2.5/weather?lat=${latitude}\u0026lon=${longitude}\u0026appid=${process.env.REACT_APP_WEATHER_API_KEY}`);\n            const data = await response.json();\n            setWeather(data);\n            setLoading(false);\n        } catch (error) {\n            console.error(error.message);\n            setError(error.message);\n            setLoading(false);\n        }\n    }\n\n    useEffect(() =\u003e {\n        if (navigator.geolocation) {\n            setLocationAllowed(true);\n            // get the current position\n            navigator.geolocation.getCurrentPosition(getWeatherData, (err) =\u003e {\n                setLocationAllowed(false);\n                setLoading(false);\n            });\n        } else {\n            setLocationAllowed(false);\n            console.error(\"Geolocation is not supported by this browser.\");\n        }\n    }, []);\n```\n\n4. Handling `Location Not Availabe` , `Loading` and `Error` states.\n\n```jsx\n    // if location is not allowed, show a message\n    if (!locationAllowed) {\n        return (\n            \u003cdiv\u003e\n                \u003cdiv style={styles.weathercontainer}\u003e\n                    \u003ch1 style={{ fontSize: \"1.3rem\", padding: \"1rem 0.5rem\", fontWeight: \"600\", textAlign: \"center\" }}\u003eLocation is not allowed!\u003c/h1\u003e\n                    \u003cp style={{\n                        fontSize: \"1rem\",\n                        padding: \"0 0.5rem 1rem\",\n                        fontWeight: \"500\",\n                        textAlign: \"center\",\n                    }}\u003eAllow access to Location to access weather info.\u003c/p\u003e\n                \u003c/div\u003e\n            \u003c/div\u003e\n        )\n    }\n\n    // if the weather data is not loaded, show a loading message\n    if (loading) {\n        return (\n            \u003cdiv\u003e\n                \u003cdiv style={styles.weathercontainer}\u003e\n                    \u003ch1 style={{ fontSize: \"1.3rem\", padding: \"1rem 0.5rem\", fontWeight: \"600\", textAlign: \"center\" }}\u003eLoading...\u003c/h1\u003e\n                \u003c/div\u003e\n            \u003c/div\u003e\n        )\n    }\n\n    // if there is an error, show an error message\n    if (error) {\n        return (\n            \u003cdiv\u003e\n                \u003cdiv style={styles.weathercontainer}\u003e\n                    \u003ch1 style={{ fontSize: \"1.3rem\", padding: \"1rem 0.5rem\", fontWeight: \"600\", textAlign: \"center\" }}\u003eError!\u003c/h1\u003e\n                    \u003cp style={{\n                        fontSize: \"1rem\",\n                        padding: \"0 0.5rem 1rem\",\n                        fontWeight: \"500\",\n                        textAlign: \"center\",\n                    }}\u003e{error}\u003c/p\u003e\n                \u003c/div\u003e\n            \u003c/div\u003e\n        )\n    }\n```\n\n5. Rendering the Componet UI if everything goes well.\n\n```jsx\n    return (\n        \u003cdiv\u003e\n            {weather \u0026\u0026\n                \u003cdiv style={styles.weathercontainer}\u003e\n                    \u003cdiv style={styles.container}\u003e\n                        \u003cdiv\u003e\n                            \u003cdiv style={styles.leftTop}\u003e\n                                \u003cimg alt=\"icon\" style={styles.icon} src={`https://openweathermap.org/img/wn/${weather.weather[0].icon}@2x.png`} /\u003e\n                                \u003cdiv\u003e{weather.weather[0].main}\u003c/div\u003e\n                            \u003c/div\u003e\n                            \u003cdiv style={styles.currentTemp}\u003e{(weather.main.temp - 273.13).toFixed(0)}° C\u003c/div\u003e\n                            \u003cdiv\u003e\n                                Real Feel : {(weather.main.feels_like - 273.13).toFixed(0)}° C\n                            \u003c/div\u003e\n                            \u003cdiv\u003e\n                                Humidity : {weather.main.humidity}%\n                            \u003c/div\u003e\n                            \u003cdiv\u003e\n                                Wind Speed : {weather.wind.speed} km/h\n                            \u003c/div\u003e\n                            \u003cdiv\u003e\n                                Visibility : {weather.visibility / 1000} km\n                            \u003c/div\u003e\n                            \u003cdiv\u003e\n                                Pressure : {weather.main.pressure} hPa\n                            \u003c/div\u003e\n                        \u003c/div\u003e\n                        \u003cdiv style={styles.right}\u003e\n                            \u003cdiv style={styles.dateTime}\u003e\n                                \u003cdiv\u003e{time}\u003c/div\u003e\n                                \u003cdiv\u003e{tidyDate(new Date())}\u003c/div\u003e\n                                \u003cdiv style={styles.day}\u003e{getDayAbbreviation()}\u003c/div\u003e\n                            \u003c/div\u003e\n\n                            \u003cdiv style={styles.dateTime}\u003e\n                                \u003cdiv style={styles.city}\u003e{weather.name}\u003c/div\u003e\n                                \u003cdiv\u003e({formatTimeZone(weather.timezone)}) GMT\u003c/div\u003e\n                            \u003c/div\u003e\n                        \u003c/div\u003e\n                    \u003c/div\u003e\n                    \u003cdiv style={styles.footer}\u003e\n                        \u003cdiv\u003e\n                            Sunrise : {new Date(weather.sys.sunrise * 1000).toLocaleTimeString()}\n                        \u003c/div\u003e\n                        \u003cdiv\u003e\n                            Sunset : {new Date(weather.sys.sunset * 1000).toLocaleTimeString()}\n                        \u003c/div\u003e\n                    \u003c/div\u003e\n                \u003c/div\u003e\n            }\n        \u003c/div\u003e\n    )\n```\n6. For Styling, I have used Inline CSS.\n\n```jsx\nconst styles = {\n    icon: {\n        height: \"3rem\",\n        width: \"3rem\",\n    },\n    weathercontainer: {\n        display: \"flex\",\n        flexDirection: \"column\",\n        width: \"22rem\"\n    },\n    container: {\n        padding: \"0.5rem\",\n        display: \"flex\",\n        justifyContent: \"space-between\",\n    },\n    leftTop: {\n        display: \"flex\",\n        alignItems: \"center\",\n        margin: \"-0.6rem 0\"\n    },\n    currentTemp: {\n        fontSize: \"3rem\",\n        fontWeight: \"700\",\n    },\n    right: {\n        display: \"flex\",\n        flexDirection: \"column\",\n        justifyContent: \"space-between\",\n        alignItems: \"flex-end\"\n    },\n    dateTime: {\n        display: \"flex\",\n        flexDirection: \"column\",\n        alignItems: \"flex-end\"\n    },\n    day: {\n        fontSize: \"1.5rem\",\n    },\n    city: {\n        fontSize: \"1.5rem\",\n        fontWeight: \"600\",\n    },\n    footer: {\n        display: \"flex\",\n        alignItems: \"center\",\n        justifyContent: \"space-between\",\n        padding: \"0 0.5rem\",\n        marginTop: \"-0.5rem\",\n        paddingBottom: \"0.5rem\",\n    }\n}\n```\n\n7. Helper Functions\n\n```js\n/**\n * Formats the timezone offset in seconds to a formatted string representation.\n * @param {number} offsetInSeconds - The timezone offset in seconds.\n * @returns {string} - The formatted timezone offset string, e.g., '+05:30'.\n */\nfunction formatTimeZone(offsetInSeconds) {\n    const offsetHours = Math.floor(Math.abs(offsetInSeconds) / 3600);\n    const offsetMinutes = Math.floor((Math.abs(offsetInSeconds) % 3600) / 60);\n    const sign = offsetInSeconds \u003e= 0 ? '+' : '-';\n    const formattedOffset = `${sign}${String(offsetHours).padStart(2, '0')}:${String(offsetMinutes).padStart(2, '0')}`;\n    return formattedOffset;\n}\n\n/**\n * Converts the given date to a formatted date string.\n * @param {Date} date - The input date.\n * @returns {string} - The formatted date string, e.g., '1, January 2021'.\n */\nconst tidyDate = (date) =\u003e {\n    const dateNumber = date.getDate();\n    const month = date.getMonth() + 1;\n    const year = date.getFullYear();\n    const months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];\n    return `${dateNumber}, ${months[month - 1]} ${year}`;\n}\n\n/**\n * Returns the abbreviation of the current day of the week.\n * @returns {string} - The day abbreviation, e.g., 'SUN'.\n */\nfunction getDayAbbreviation() {\n    const date = new Date();\n    const daysOfWeek = ['SUN', 'MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT'];\n    const dayIndex = date.getDay();\n    return daysOfWeek[dayIndex];\n}\n```\n\n8. Finally, I have exported the component.\n\n```jsx\nexport default WeatherWidget;\n```\n\n### Complete Code for the Weather Widget Component\n\n```jsx\nimport { useState, useEffect } from 'react';\n\n/**\n * Formats the timezone offset in seconds to a formatted string representation.\n * @param {number} offsetInSeconds - The timezone offset in seconds.\n * @returns {string} - The formatted timezone offset string, e.g., '+05:30'.\n */\nfunction formatTimeZone(offsetInSeconds) {\n    const offsetHours = Math.floor(Math.abs(offsetInSeconds) / 3600);\n    const offsetMinutes = Math.floor((Math.abs(offsetInSeconds) % 3600) / 60);\n    const sign = offsetInSeconds \u003e= 0 ? '+' : '-';\n    const formattedOffset = `${sign}${String(offsetHours).padStart(2, '0')}:${String(offsetMinutes).padStart(2, '0')}`;\n    return formattedOffset;\n}\n\n/**\n * Converts the given date to a formatted date string.\n * @param {Date} date - The input date.\n * @returns {string} - The formatted date string, e.g., '1, January 2021'.\n */\nconst tidyDate = (date) =\u003e {\n    const dateNumber = date.getDate();\n    const month = date.getMonth() + 1;\n    const year = date.getFullYear();\n    const months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];\n    return `${dateNumber}, ${months[month - 1]} ${year}`;\n}\n\n/**\n * Returns the abbreviation of the current day of the week.\n * @returns {string} - The day abbreviation, e.g., 'SUN'.\n */\nfunction getDayAbbreviation() {\n    const date = new Date();\n    const daysOfWeek = ['SUN', 'MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT'];\n    const dayIndex = date.getDay();\n    return daysOfWeek[dayIndex];\n}\n\nconst WeatherWidget = () =\u003e {\n    const [weather, setWeather] = useState(null);\n    const [time, setTime] = useState(new Date().toLocaleTimeString());\n    const [locationAllowed, setLocationAllowed] = useState(true);\n    const [error, setError] = useState(null);\n    const [loading, setLoading] = useState(false);\n\n    useEffect(() =\u003e {\n        // Update the time every second\n        const interval = setInterval(() =\u003e {\n            setTime(new Date().toLocaleTimeString())\n        }, 1000);\n        // Clean up the interval on component unmount\n        return () =\u003e clearInterval(interval);\n    }, [time]);\n\n    async function getWeatherData(position) {\n        // get the latitude and longitude from the geolocation API\n        const latitude = position.coords.latitude;\n        const longitude = position.coords.longitude;\n\n        // fetch the weather data from the OpenWeatherMap API\n        try {\n            const response = await fetch(`https://api.openweathermap.org/data/2.5/weather?lat=${latitude}\u0026lon=${longitude}\u0026appid=${process.env.REACT_APP_WEATHER_API_KEY}`);\n            const data = await response.json();\n            setWeather(data);\n            setLoading(false);\n        } catch (error) {\n            console.error(error.message);\n            setError(error.message);\n            setLoading(false);\n        }\n    }\n\n    useEffect(() =\u003e {\n        if (navigator.geolocation) {\n            setLocationAllowed(true);\n            // get the current position\n            navigator.geolocation.getCurrentPosition(getWeatherData, (err) =\u003e {\n                setLocationAllowed(false);\n                setLoading(false);\n            });\n        } else {\n            setLocationAllowed(false);\n            console.error(\"Geolocation is not supported by this browser.\");\n        }\n    }, []);\n\n    // if location is not allowed, show a message\n    if (!locationAllowed) {\n        return (\n            \u003cdiv\u003e\n                \u003cdiv style={styles.weathercontainer}\u003e\n                    \u003ch1 style={{ fontSize: \"1.3rem\", padding: \"1rem 0.5rem\", fontWeight: \"600\", textAlign: \"center\" }}\u003eLocation is not allowed!\u003c/h1\u003e\n                    \u003cp style={{\n                        fontSize: \"1rem\",\n                        padding: \"0 0.5rem 1rem\",\n                        fontWeight: \"500\",\n                        textAlign: \"center\",\n                    }}\u003eAllow access to Location to access weather info.\u003c/p\u003e\n                \u003c/div\u003e\n            \u003c/div\u003e\n        )\n    }\n\n    // if the weather data is not loaded, show a loading message\n    if (loading) {\n        return (\n            \u003cdiv\u003e\n                \u003cdiv style={styles.weathercontainer}\u003e\n                    \u003ch1 style={{ fontSize: \"1.3rem\", padding: \"1rem 0.5rem\", fontWeight: \"600\", textAlign: \"center\" }}\u003eLoading...\u003c/h1\u003e\n                \u003c/div\u003e\n            \u003c/div\u003e\n        )\n    }\n\n    // if there is an error, show an error message\n    if (error) {\n        return (\n            \u003cdiv\u003e\n                \u003cdiv style={styles.weathercontainer}\u003e\n                    \u003ch1 style={{ fontSize: \"1.3rem\", padding: \"1rem 0.5rem\", fontWeight: \"600\", textAlign: \"center\" }}\u003eError!\u003c/h1\u003e\n                    \u003cp style={{\n                        fontSize: \"1rem\",\n                        padding: \"0 0.5rem 1rem\",\n                        fontWeight: \"500\",\n                        textAlign: \"center\",\n                    }}\u003e{error}\u003c/p\u003e\n                \u003c/div\u003e\n            \u003c/div\u003e\n        )\n    }\n\n    return (\n        \u003cdiv\u003e\n            {weather \u0026\u0026\n                \u003cdiv style={styles.weathercontainer}\u003e\n                    \u003cdiv style={styles.container}\u003e\n                        \u003cdiv\u003e\n                            \u003cdiv style={styles.leftTop}\u003e\n                                \u003cimg alt=\"icon\" style={styles.icon} src={`https://openweathermap.org/img/wn/${weather.weather[0].icon}@2x.png`} /\u003e\n                                \u003cdiv\u003e{weather.weather[0].main}\u003c/div\u003e\n                            \u003c/div\u003e\n                            \u003cdiv style={styles.currentTemp}\u003e{(weather.main.temp - 273.13).toFixed(0)}° C\u003c/div\u003e\n                            \u003cdiv\u003e\n                                Real Feel : {(weather.main.feels_like - 273.13).toFixed(0)}° C\n                            \u003c/div\u003e\n                            \u003cdiv\u003e\n                                Humidity : {weather.main.humidity}%\n                            \u003c/div\u003e\n                            \u003cdiv\u003e\n                                Wind Speed : {weather.wind.speed} km/h\n                            \u003c/div\u003e\n                            \u003cdiv\u003e\n                                Visibility : {weather.visibility / 1000} km\n                            \u003c/div\u003e\n                            \u003cdiv\u003e\n                                Pressure : {weather.main.pressure} hPa\n                            \u003c/div\u003e\n                        \u003c/div\u003e\n                        \u003cdiv style={styles.right}\u003e\n                            \u003cdiv style={styles.dateTime}\u003e\n                                \u003cdiv\u003e{time}\u003c/div\u003e\n                                \u003cdiv\u003e{tidyDate(new Date())}\u003c/div\u003e\n                                \u003cdiv style={styles.day}\u003e{getDayAbbreviation()}\u003c/div\u003e\n                            \u003c/div\u003e\n\n                            \u003cdiv style={styles.dateTime}\u003e\n                                \u003cdiv style={styles.city}\u003e{weather.name}\u003c/div\u003e\n                                \u003cdiv\u003e({formatTimeZone(weather.timezone)}) GMT\u003c/div\u003e\n                            \u003c/div\u003e\n                        \u003c/div\u003e\n                    \u003c/div\u003e\n                    \u003cdiv style={styles.footer}\u003e\n                        \u003cdiv\u003e\n                            Sunrise : {new Date(weather.sys.sunrise * 1000).toLocaleTimeString()}\n                        \u003c/div\u003e\n                        \u003cdiv\u003e\n                            Sunset : {new Date(weather.sys.sunset * 1000).toLocaleTimeString()}\n                        \u003c/div\u003e\n                    \u003c/div\u003e\n                \u003c/div\u003e\n            }\n        \u003c/div\u003e\n    )\n}\n\nexport default WeatherWidget;\n\nconst styles = {\n    icon: {\n        height: \"3rem\",\n        width: \"3rem\",\n    },\n    weathercontainer: {\n        display: \"flex\",\n        flexDirection: \"column\",\n        width: \"22rem\"\n    },\n    container: {\n        padding: \"0.5rem\",\n        display: \"flex\",\n        justifyContent: \"space-between\",\n    },\n    leftTop: {\n        display: \"flex\",\n        alignItems: \"center\",\n        margin: \"-0.6rem 0\"\n    },\n    currentTemp: {\n        fontSize: \"3rem\",\n        fontWeight: \"700\",\n    },\n    right: {\n        display: \"flex\",\n        flexDirection: \"column\",\n        justifyContent: \"space-between\",\n        alignItems: \"flex-end\"\n    },\n    dateTime: {\n        display: \"flex\",\n        flexDirection: \"column\",\n        alignItems: \"flex-end\"\n    },\n    day: {\n        fontSize: \"1.5rem\",\n    },\n    city: {\n        fontSize: \"1.5rem\",\n        fontWeight: \"600\",\n    },\n    footer: {\n        display: \"flex\",\n        alignItems: \"center\",\n        justifyContent: \"space-between\",\n        padding: \"0 0.5rem\",\n        marginTop: \"-0.5rem\",\n        paddingBottom: \"0.5rem\",\n    }\n}\n```\n\n### Working of the Widget\n\nOpen the widget gallery modal and click the `WeatherWidget` button. The widget will be added to the dashboard. The widget will show the weather data of the current location. If the location is not allowed, the widget will show a message. If the weather data is not loaded, the widget will show a loading message. If there is an error, the widget will show an error message.\n\n![Weather Widget](https://i.ibb.co/TMrPNhx/Screenshot-1429-min.png)\n\nThat's it.We have successfully created a weather widget.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsudhansuuranjan%2Fplanner-app","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsudhansuuranjan%2Fplanner-app","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsudhansuuranjan%2Fplanner-app/lists"}