New Weather Modal

This commit is contained in:
Sean Edmunds
2025-02-10 17:06:00 -06:00
parent 1d1b707d0c
commit a4b1e55968
21 changed files with 972 additions and 67 deletions

324
package-lock.json generated
View File

@@ -30,7 +30,8 @@
"react-icons": "^5.4.0", "react-icons": "^5.4.0",
"react-query": "^3.39.3", "react-query": "^3.39.3",
"react-router-dom": "^7.1.2", "react-router-dom": "^7.1.2",
"react-toastify": "^11.0.3" "react-toastify": "^11.0.3",
"recharts": "^2.15.1"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^22.10.7", "@types/node": "^22.10.7",
@@ -2141,6 +2142,60 @@
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz",
"integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==" "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA=="
}, },
"node_modules/@types/d3-array": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz",
"integrity": "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg=="
},
"node_modules/@types/d3-color": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz",
"integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A=="
},
"node_modules/@types/d3-ease": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz",
"integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA=="
},
"node_modules/@types/d3-interpolate": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz",
"integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==",
"dependencies": {
"@types/d3-color": "*"
}
},
"node_modules/@types/d3-path": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz",
"integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg=="
},
"node_modules/@types/d3-scale": {
"version": "4.0.9",
"resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz",
"integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==",
"dependencies": {
"@types/d3-time": "*"
}
},
"node_modules/@types/d3-shape": {
"version": "3.1.7",
"resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.7.tgz",
"integrity": "sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==",
"dependencies": {
"@types/d3-path": "*"
}
},
"node_modules/@types/d3-time": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz",
"integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g=="
},
"node_modules/@types/d3-timer": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz",
"integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw=="
},
"node_modules/@types/eslint": { "node_modules/@types/eslint": {
"version": "8.56.12", "version": "8.56.12",
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.12.tgz", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.12.tgz",
@@ -3022,6 +3077,116 @@
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
}, },
"node_modules/d3-array": {
"version": "3.2.4",
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz",
"integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==",
"dependencies": {
"internmap": "1 - 2"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-color": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz",
"integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==",
"engines": {
"node": ">=12"
}
},
"node_modules/d3-ease": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz",
"integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==",
"engines": {
"node": ">=12"
}
},
"node_modules/d3-format": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz",
"integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==",
"engines": {
"node": ">=12"
}
},
"node_modules/d3-interpolate": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
"integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
"dependencies": {
"d3-color": "1 - 3"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-path": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz",
"integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==",
"engines": {
"node": ">=12"
}
},
"node_modules/d3-scale": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz",
"integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==",
"dependencies": {
"d3-array": "2.10.0 - 3",
"d3-format": "1 - 3",
"d3-interpolate": "1.2.0 - 3",
"d3-time": "2.1.1 - 3",
"d3-time-format": "2 - 4"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-shape": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz",
"integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==",
"dependencies": {
"d3-path": "^3.1.0"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-time": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz",
"integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==",
"dependencies": {
"d3-array": "2 - 3"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-time-format": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz",
"integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==",
"dependencies": {
"d3-time": "1 - 3"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-timer": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz",
"integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==",
"engines": {
"node": ">=12"
}
},
"node_modules/dayjs": { "node_modules/dayjs": {
"version": "1.11.13", "version": "1.11.13",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz",
@@ -3049,6 +3214,11 @@
} }
} }
}, },
"node_modules/decimal.js-light": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz",
"integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg=="
},
"node_modules/deep-eql": { "node_modules/deep-eql": {
"version": "5.0.2", "version": "5.0.2",
"resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz",
@@ -3074,6 +3244,15 @@
"resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz",
"integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==" "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="
}, },
"node_modules/dom-helpers": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
"integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
"dependencies": {
"@babel/runtime": "^7.8.7",
"csstype": "^3.0.2"
}
},
"node_modules/dotenv": { "node_modules/dotenv": {
"version": "16.4.7", "version": "16.4.7",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz",
@@ -3469,6 +3648,11 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/eventemitter3": {
"version": "4.0.7",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
"integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="
},
"node_modules/expect-type": { "node_modules/expect-type": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.1.0.tgz", "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.1.0.tgz",
@@ -3483,6 +3667,14 @@
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
}, },
"node_modules/fast-equals": {
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.2.2.tgz",
"integrity": "sha512-V7/RktU11J3I36Nwq2JnZEM7tNm17eBJz+u25qdxBZeCKiX6BkVSZQjwWIr+IobgnZy+ag73tTZgZi7tr0LrBw==",
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/fast-glob": { "node_modules/fast-glob": {
"version": "3.3.3", "version": "3.3.3",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
@@ -3887,6 +4079,14 @@
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
}, },
"node_modules/internmap": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz",
"integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==",
"engines": {
"node": ">=12"
}
},
"node_modules/is-arrayish": { "node_modules/is-arrayish": {
"version": "0.2.1", "version": "0.2.1",
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
@@ -4206,8 +4406,7 @@
"node_modules/lodash": { "node_modules/lodash": {
"version": "4.17.21", "version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
"dev": true
}, },
"node_modules/lodash.merge": { "node_modules/lodash.merge": {
"version": "4.6.2", "version": "4.6.2",
@@ -4215,6 +4414,17 @@
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
"dev": true "dev": true
}, },
"node_modules/loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
"dependencies": {
"js-tokens": "^3.0.0 || ^4.0.0"
},
"bin": {
"loose-envify": "cli.js"
}
},
"node_modules/loupe": { "node_modules/loupe": {
"version": "3.1.2", "version": "3.1.2",
"resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.2.tgz", "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.2.tgz",
@@ -4422,6 +4632,14 @@
"integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==",
"dev": true "dev": true
}, },
"node_modules/object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/oblivious-set": { "node_modules/oblivious-set": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/oblivious-set/-/oblivious-set-1.0.0.tgz", "resolved": "https://registry.npmjs.org/oblivious-set/-/oblivious-set-1.0.0.tgz",
@@ -4663,6 +4881,16 @@
"node": ">= 0.8.0" "node": ">= 0.8.0"
} }
}, },
"node_modules/prop-types": {
"version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
"dependencies": {
"loose-envify": "^1.4.0",
"object-assign": "^4.1.1",
"react-is": "^16.13.1"
}
},
"node_modules/punycode": { "node_modules/punycode": {
"version": "2.3.1", "version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
@@ -4850,6 +5078,20 @@
"react-dom": ">=18" "react-dom": ">=18"
} }
}, },
"node_modules/react-smooth": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-4.0.4.tgz",
"integrity": "sha512-gnGKTpYwqL0Iii09gHobNolvX4Kiq4PKx6eWBCYYix+8cdw+cGo3do906l1NBPKkSWx1DghC1dlWG9L2uGd61Q==",
"dependencies": {
"fast-equals": "^5.0.1",
"prop-types": "^15.8.1",
"react-transition-group": "^4.4.5"
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
"node_modules/react-style-singleton": { "node_modules/react-style-singleton": {
"version": "2.2.3", "version": "2.2.3",
"resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz", "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz",
@@ -4899,6 +5141,56 @@
"react-dom": "^18 || ^19" "react-dom": "^18 || ^19"
} }
}, },
"node_modules/react-transition-group": {
"version": "4.4.5",
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
"integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==",
"dependencies": {
"@babel/runtime": "^7.5.5",
"dom-helpers": "^5.0.1",
"loose-envify": "^1.4.0",
"prop-types": "^15.6.2"
},
"peerDependencies": {
"react": ">=16.6.0",
"react-dom": ">=16.6.0"
}
},
"node_modules/recharts": {
"version": "2.15.1",
"resolved": "https://registry.npmjs.org/recharts/-/recharts-2.15.1.tgz",
"integrity": "sha512-v8PUTUlyiDe56qUj82w/EDVuzEFXwEHp9/xOowGAZwfLjB9uAy3GllQVIYMWF6nU+qibx85WF75zD7AjqoT54Q==",
"dependencies": {
"clsx": "^2.0.0",
"eventemitter3": "^4.0.1",
"lodash": "^4.17.21",
"react-is": "^18.3.1",
"react-smooth": "^4.0.4",
"recharts-scale": "^0.4.4",
"tiny-invariant": "^1.3.1",
"victory-vendor": "^36.6.8"
},
"engines": {
"node": ">=14"
},
"peerDependencies": {
"react": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
"react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
"node_modules/recharts-scale": {
"version": "0.4.5",
"resolved": "https://registry.npmjs.org/recharts-scale/-/recharts-scale-0.4.5.tgz",
"integrity": "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==",
"dependencies": {
"decimal.js-light": "^2.4.1"
}
},
"node_modules/recharts/node_modules/react-is": {
"version": "18.3.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="
},
"node_modules/regenerator-runtime": { "node_modules/regenerator-runtime": {
"version": "0.14.1", "version": "0.14.1",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
@@ -5321,6 +5613,11 @@
"url": "https://github.com/sponsors/isaacs" "url": "https://github.com/sponsors/isaacs"
} }
}, },
"node_modules/tiny-invariant": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
"integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg=="
},
"node_modules/tinybench": { "node_modules/tinybench": {
"version": "2.9.0", "version": "2.9.0",
"resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz",
@@ -5636,6 +5933,27 @@
} }
} }
}, },
"node_modules/victory-vendor": {
"version": "36.9.2",
"resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.2.tgz",
"integrity": "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==",
"dependencies": {
"@types/d3-array": "^3.0.3",
"@types/d3-ease": "^3.0.0",
"@types/d3-interpolate": "^3.0.1",
"@types/d3-scale": "^4.0.2",
"@types/d3-shape": "^3.1.0",
"@types/d3-time": "^3.0.0",
"@types/d3-timer": "^3.0.0",
"d3-array": "^3.1.6",
"d3-ease": "^3.0.1",
"d3-interpolate": "^3.0.1",
"d3-scale": "^4.0.2",
"d3-shape": "^3.1.0",
"d3-time": "^3.0.0",
"d3-timer": "^3.0.1"
}
},
"node_modules/vite": { "node_modules/vite": {
"version": "6.0.7", "version": "6.0.7",
"resolved": "https://registry.npmjs.org/vite/-/vite-6.0.7.tgz", "resolved": "https://registry.npmjs.org/vite/-/vite-6.0.7.tgz",

View File

@@ -32,7 +32,8 @@
"react-icons": "^5.4.0", "react-icons": "^5.4.0",
"react-query": "^3.39.3", "react-query": "^3.39.3",
"react-router-dom": "^7.1.2", "react-router-dom": "^7.1.2",
"react-toastify": "^11.0.3" "react-toastify": "^11.0.3",
"recharts": "^2.15.1"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^22.10.7", "@types/node": "^22.10.7",
@@ -41,6 +42,8 @@
"@typescript-eslint/eslint-plugin": "^8.20.0", "@typescript-eslint/eslint-plugin": "^8.20.0",
"@typescript-eslint/parser": "^8.20.0", "@typescript-eslint/parser": "^8.20.0",
"@vitejs/plugin-react": "^4.3.4", "@vitejs/plugin-react": "^4.3.4",
"@vitest/coverage-v8": "^3.0.2",
"@vitest/ui": "^3.0.2",
"eslint": "^9.18.0", "eslint": "^9.18.0",
"eslint-plugin-react-hooks": "^5.1.0", "eslint-plugin-react-hooks": "^5.1.0",
"eslint-plugin-react-refresh": "^0.4.18", "eslint-plugin-react-refresh": "^0.4.18",
@@ -49,8 +52,6 @@
"vite-plugin-dts": "~4.5.0", "vite-plugin-dts": "~4.5.0",
"vite-plugin-eslint": "^1.8.1", "vite-plugin-eslint": "^1.8.1",
"vite-tsconfig-paths": "^5.1.4", "vite-tsconfig-paths": "^5.1.4",
"vitest": "^3.0.2", "vitest": "^3.0.2"
"@vitest/coverage-v8": "^3.0.2",
"@vitest/ui": "^3.0.2"
} }
} }

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48"><path fill="#BBDEFB" d="M29.5 12A8.5 8.5 0 1 0 29.5 29A8.5 8.5 0 1 0 29.5 12Z"/><path fill="#BBDEFB" d="M37 21.893A7 7 0 1 0 37 35.893 7 7 0 1 0 37 21.893zM11 22A7 7 0 1 0 11 36 7 7 0 1 0 11 22z"/><path fill="#BBDEFB" d="M17.5 15A6.5 6.5 0 1 0 17.5 28A6.5 6.5 0 1 0 17.5 15Z"/><path fill="#BBDEFB" d="M25 19.893A7 7 0 1 0 25 33.893A7 7 0 1 0 25 19.893Z"/><path fill="#BBDEFB" d="M7,32c0,2.209,1.791,4,4,4h25c2.209,0,4-1.791,4-4v-1c0-2.209-1.791-4-4-4H11c-2.209,0-4,1.791-4,4V32z"/></svg>

After

Width:  |  Height:  |  Size: 570 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48"><path fill="#FF9800" d="M24,6L6,24l1,1h1c1.104,0,2,0.896,2,2c0,0.293-0.066,0.569-0.18,0.82L23,41h2l17-17L24,6z"/><path fill="#FF9800" d="M11 11H37V37H11z"/><path fill="#FFEB3B" d="M13,24c0,6.077,4.923,11,11,11c6.076,0,11-4.923,11-11s-4.924-11-11-11C17.923,13,13,17.923,13,24"/><path fill="#64B5F6" d="M40,37h-9c-1.104,0-2-0.896-2-2s0.896-2,2-2h3c1.104,0,2-0.896,2-2s-0.896-2-2-2h-6h-3h-6c-1.104,0-2-0.896-2-2c0-1.102,0.89-1.994,1.99-1.999H21c1.104,0,2-0.896,2-2V23l0,0c0-1.104-0.896-2-2-2H7v0.001c-1.104,0-2,0.896-2,2s0.896,2,2,2h1C9.1,25.006,9.99,25.898,9.99,27c0,1.104-0.896,2-2,2H8c-1.104,0-2,0.896-2,2s0.896,2,2,2h1c1.104,0,2,0.896,2,2s-0.896,2-2,2s-2,0.896-2,2s0.896,2,2,2h31c0.8,0,1.485-0.473,1.805-1.152h0.001l0.001-0.002c0.057-0.123,0.104-0.25,0.135-0.385c0.002-0.008,0.006-0.016,0.008-0.023C41.98,39.296,42,39.15,42,39C42,37.896,41.104,37,40,37z"/><path fill="#BBDEFB" d="M24,35c4.273,0,7.969-2.44,9.79-6H28h-3h-6c-1.104,0-2-0.896-2-2c0-1.102,0.89-1.994,1.99-1.999H21c1.104,0,2-0.896,2-2V23c0-1.104-0.896-2-2-2h-7.576C13.154,21.955,13,22.959,13,24C13,30.077,17.923,35,24,35z"/></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48"><path fill="#BBDEFB" d="M29.5 5A8.5 8.5 0 1 0 29.5 22A8.5 8.5 0 1 0 29.5 5Z"/><path fill="#BBDEFB" d="M37 14.893A7 7 0 1 0 37 28.893 7 7 0 1 0 37 14.893zM11 15A7 7 0 1 0 11 29 7 7 0 1 0 11 15z"/><path fill="#BBDEFB" d="M17.5 8A6.5 6.5 0 1 0 17.5 21A6.5 6.5 0 1 0 17.5 8Z"/><path fill="#BBDEFB" d="M25 12.893A7 7 0 1 0 25 26.893A7 7 0 1 0 25 12.893Z"/><path fill="#BBDEFB" d="M7,25c0,2.209,1.791,4,4,4h25c2.209,0,4-1.791,4-4v-1c0-2.209-1.791-4-4-4H11c-2.209,0-4,1.791-4,4V25z"/><g><path fill="#2196F3" d="M20 29A1 1 0 1 0 20 31 1 1 0 1 0 20 29zM34 29A1 1 0 1 0 34 31 1 1 0 1 0 34 29zM19 32A1 1 0 1 0 19 34 1 1 0 1 0 19 32zM18 35A1 1 0 1 0 18 37 1 1 0 1 0 18 35zM17 38A1 1 0 1 0 17 40 1 1 0 1 0 17 38zM33 32A1 1 0 1 0 33 34 1 1 0 1 0 33 32zM32 35A1 1 0 1 0 32 37 1 1 0 1 0 32 35zM31 38A1 1 0 1 0 31 40 1 1 0 1 0 31 38zM16 41A1 1 0 1 0 16 43 1 1 0 1 0 16 41zM30 41A1 1 0 1 0 30 43 1 1 0 1 0 30 41zM12 32A1 1 0 1 0 12 34 1 1 0 1 0 12 32zM11 35A1 1 0 1 0 11 37 1 1 0 1 0 11 35zM26 32A1 1 0 1 0 26 34 1 1 0 1 0 26 32zM25 35A1 1 0 1 0 25 37 1 1 0 1 0 25 35zM10 38A1 1 0 1 0 10 40 1 1 0 1 0 10 38zM24 38A1 1 0 1 0 24 40 1 1 0 1 0 24 38zM27 29A1 1 0 1 0 27 31 1 1 0 1 0 27 29zM13 29A1 1 0 1 0 13 31 1 1 0 1 0 13 29z"/></g></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48"><path fill="#FF9800" d="M8 10.001H25V27.000999999999998H8z"/><path fill="#FF9800" d="M7.661 9.661H25.339V27.339H7.661z" transform="rotate(-45.001 16.5 18.5)"/><path fill="#FFEB3B" d="M10,18.5c0,3.591,2.909,6.5,6.5,6.5s6.5-2.909,6.5-6.5S20.091,12,16.5,12S10,14.909,10,18.5"/><g><path fill="#BBDEFB" d="M29.5 17A8.5 8.5 0 1 0 29.5 34A8.5 8.5 0 1 0 29.5 17Z"/><path fill="#BBDEFB" d="M37 26.893A7 7 0 1 0 37 40.893 7 7 0 1 0 37 26.893zM11 27A7 7 0 1 0 11 41 7 7 0 1 0 11 27z"/><path fill="#BBDEFB" d="M17.5 20A6.5 6.5 0 1 0 17.5 33A6.5 6.5 0 1 0 17.5 20Z"/><path fill="#BBDEFB" d="M25 24.893A7 7 0 1 0 25 38.893A7 7 0 1 0 25 24.893Z"/><path fill="#BBDEFB" d="M7,37c0,2.209,1.791,4,4,4h25c2.209,0,4-1.791,4-4v-1c0-2.209-1.791-4-4-4H11c-2.209,0-4,1.791-4,4V37z"/></g></svg>

After

Width:  |  Height:  |  Size: 851 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48"><path fill="#BBDEFB" d="M29.5 5A8.5 8.5 0 1 0 29.5 22A8.5 8.5 0 1 0 29.5 5Z"/><path fill="#BBDEFB" d="M37 14.893A7 7 0 1 0 37 28.893 7 7 0 1 0 37 14.893zM11 15A7 7 0 1 0 11 29 7 7 0 1 0 11 15z"/><path fill="#BBDEFB" d="M17.5 8A6.5 6.5 0 1 0 17.5 21A6.5 6.5 0 1 0 17.5 8Z"/><path fill="#BBDEFB" d="M25 12.893A7 7 0 1 0 25 26.893A7 7 0 1 0 25 12.893Z"/><path fill="#BBDEFB" d="M7,25c0,2.209,1.791,4,4,4h25c2.209,0,4-1.791,4-4v-1c0-2.209-1.791-4-4-4H11c-2.209,0-4,1.791-4,4V25z"/><g><path fill="#2196F3" d="M34.95 37.15c-1.132 1.133-2.968 1.133-4.101 0-1.134-1.131-1.133-2.969 0-4.1C31.982 31.917 37 31 37 31S36.082 36.02 34.95 37.15zM23.95 41.15c-1.132 1.132-2.968 1.132-4.101 0-1.133-1.133-1.132-2.969 0-4.101 1.133-1.133 6.152-2.05 6.152-2.05S25.082 40.02 23.95 41.15zM13.95 37.15c-1.132 1.133-2.968 1.133-4.101 0-1.133-1.131-1.132-2.969 0-4.1 1.132-1.133 6.151-2.05 6.151-2.05S15.082 36.02 13.95 37.15z"/></g></svg>

After

Width:  |  Height:  |  Size: 999 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48"><path fill="#BBDEFB" d="M29.5 5A8.5 8.5 0 1 0 29.5 22A8.5 8.5 0 1 0 29.5 5Z"/><path fill="#BBDEFB" d="M37 14.893A7 7 0 1 0 37 28.893 7 7 0 1 0 37 14.893zM11 15A7 7 0 1 0 11 29 7 7 0 1 0 11 15z"/><path fill="#BBDEFB" d="M17.5 8A6.5 6.5 0 1 0 17.5 21A6.5 6.5 0 1 0 17.5 8Z"/><path fill="#BBDEFB" d="M25 12.893A7 7 0 1 0 25 26.893A7 7 0 1 0 25 12.893Z"/><path fill="#BBDEFB" d="M7,25c0,2.209,1.791,4,4,4h25c2.209,0,4-1.791,4-4v-1c0-2.209-1.791-4-4-4H11c-2.209,0-4,1.791-4,4V25z"/><g><path fill="#2196F3" d="M35.95 37.15c-1.132 1.133-2.968 1.133-4.101 0-1.134-1.131-1.133-2.969 0-4.1C32.982 31.917 38 31 38 31S37.082 36.02 35.95 37.15zM14.95 37.15c-1.132 1.133-2.968 1.133-4.101 0-1.133-1.131-1.132-2.969 0-4.1 1.132-1.133 6.151-2.05 6.151-2.05S16.082 36.02 14.95 37.15zM23 34H25V43H23z"/><path fill="#2196F3" d="M23 33.999H25V42.999H23z" transform="rotate(120.471 24 38.499)"/><path fill="#2196F3" d="M23.001 34H24.999000000000002V43H23.001z" transform="rotate(60.679 24 38.5)"/></g></svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48"><path fill="#BBDEFB" d="M29.5 5A8.5 8.5 0 1 0 29.5 22A8.5 8.5 0 1 0 29.5 5Z"/><path fill="#BBDEFB" d="M37 14.893A7 7 0 1 0 37 28.893 7 7 0 1 0 37 14.893zM11 15A7 7 0 1 0 11 29 7 7 0 1 0 11 15z"/><path fill="#BBDEFB" d="M17.5 8A6.5 6.5 0 1 0 17.5 21A6.5 6.5 0 1 0 17.5 8Z"/><path fill="#BBDEFB" d="M25 12.893A7 7 0 1 0 25 26.893A7 7 0 1 0 25 12.893Z"/><path fill="#BBDEFB" d="M7,25c0,2.209,1.791,4,4,4h25c2.209,0,4-1.791,4-4v-1c0-2.209-1.791-4-4-4H11c-2.209,0-4,1.791-4,4V25z"/><g><path fill="#2196F3" d="M23 34H25V43H23z"/><path fill="#2196F3" d="M23 33.999H25V42.999H23z" transform="rotate(120.471 24 38.499)"/><path fill="#2196F3" d="M23.001 34H24.999000000000002V43H23.001z" transform="rotate(60.679 24 38.5)"/><path fill="#2196F3" d="M10 31H12V40H10z"/><path fill="#2196F3" d="M10 30.999H12V39.998999999999995H10z" transform="rotate(120.444 11 35.5)"/><path fill="#2196F3" d="M10.001 31H11.998999999999999V40H10.001z" transform="rotate(60.679 11 35.5)"/><path fill="#2196F3" d="M36 31H38V40H36z"/><path fill="#2196F3" d="M35.999 30.998H37.999V39.998000000000005H35.999z" transform="rotate(120.458 36.999 35.5)"/><path fill="#2196F3" d="M36 31H37.998V40.001H36z" transform="rotate(60.679 36.999 35.5)"/></g></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48"><path fill="#FF9800" d="M38,38c0,0-9-3-14-3s-14,3-14,3s3-10,3-14s-3-14-3-14s9,3,14,3s14-3,14-3s-3,10-3,14S38,38,38,38z"/><path fill="#FF9800" d="M43,24c0,0-8.222,4.778-11.222,7.778S24,43,24,43s-4.278-7.722-7.778-11.222S5,24,5,24s8.221-4.777,11.222-7.777S24,5,24,5s4.779,8.223,7.778,11.223S43,24,43,24z"/><path fill="#FFEB3B" d="M14,24c0,5.524,4.477,10,10,10s10-4.476,10-10s-4.477-10-10-10S14,18.476,14,24"/></svg>

After

Width:  |  Height:  |  Size: 496 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48"><path fill="#BBDEFB" d="M18.5 4A8.5 8.5 0 1 0 18.5 21A8.5 8.5 0 1 0 18.5 4Z"/><path fill="#BBDEFB" d="M19 20A7 7 0 1 0 19 34A7 7 0 1 0 19 20Z"/><path fill="#BBDEFB" d="M11 14A7 7 0 1 0 11 28 7 7 0 1 0 11 14zM38 14A6 6 0 1 0 38 26 6 6 0 1 0 38 14z"/><path fill="#BBDEFB" d="M30 19A6 6 0 1 0 30 31A6 6 0 1 0 30 19Z"/><path fill="#BBDEFB" d="M30.5 7A6.5 6.5 0 1 0 30.5 20A6.5 6.5 0 1 0 30.5 7Z"/><path fill="#BBDEFB" d="M23 12A7 7 0 1 0 23 26A7 7 0 1 0 23 12Z"/><g><path fill="#D500F9" d="M22 44l2-7h-3l4-8h4l-3.594 6H29L22 44zM32.594 39l2-7h-3l4-8h4L36 30h3.594L32.594 39z"/></g></svg>

After

Width:  |  Height:  |  Size: 666 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48"><path fill="#bbdefb" d="M38,15.5c0,4.7-3.8,8.5-8.5,8.5S21,20.2,21,15.5S24.8,7,29.5,7S38,10.8,38,15.5z"/><path fill="#bbdefb" d="M44 23.9c0 3.9-3.1 7-7 7s-7-3.1-7-7c0-3.9 3.1-7 7-7S44 20 44 23.9zM18 24c0 3.9-3.1 7-7 7s-7-3.1-7-7 3.1-7 7-7S18 20.1 18 24z"/><path fill="#bbdefb" d="M24,16.5c0,3.6-2.9,6.5-6.5,6.5S11,20.1,11,16.5s2.9-6.5,6.5-6.5S24,12.9,24,16.5z"/><path fill="#bbdefb" d="M32,21.9c0,3.9-3.1,7-7,7s-7-3.1-7-7c0-3.9,3.1-7,7-7S32,18,32,21.9z"/><path fill="#bbdefb" d="M7,27c0,2.2,1.8,4,4,4h25c2.2,0,4-1.8,4-4v-1c0-2.2-1.8-4-4-4H11c-2.2,0-4,1.8-4,4V27z"/><path fill="#2196f3" d="M31.9 41c-2.5 0-4.6-1.5-5.5-3.9-.2-.5.1-1.1.6-1.3.5-.2 1.1.1 1.3.6.6 1.7 2 2.7 3.7 2.6 2.3-.1 4-1.8 4-4 0-2.2-1.8-4-4-4H12c-.6 0-1-.4-1-1s.4-1 1-1h20c3.3 0 6 2.7 6 6 0 3.2-2.6 5.9-6 6C32 41 31.9 41 31.9 41zM25 27H14c-.6 0-1-.4-1-1s.4-1 1-1h11c1.6 0 3-1.4 3-3s-1.4-3-3-3c-1.2 0-2.3.8-2.8 1.9-.2.5-.8.8-1.3.6-.5-.2-.8-.8-.6-1.3.8-1.9 2.6-3.2 4.7-3.2 2.8 0 5 2.2 5 5S27.8 27 25 27z"/><path fill="#2196f3" d="M18,41c-1.5,0-2.9-0.8-3.5-2.2c-0.2-0.5,0-1.1,0.4-1.3c0.5-0.2,1.1,0,1.3,0.4c0.3,0.6,1,1,1.7,1 c1.1,0,2-0.9,2-2s-0.9-2-2-2h-8c-0.6,0-1-0.4-1-1s0.4-1,1-1h8c2.2,0,4,1.8,4,4S20.2,41,18,41z"/></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1,145 @@
import { Grid, Group, Stack, Text, ThemeIcon } from '@mantine/core';
import { FaLongArrowAltDown, FaLongArrowAltUp, FaWind } from 'react-icons/fa';
import { WiBarometer, WiHumidity } from 'react-icons/wi';
import WeatherIcon from './WeatherIcon';
export default function CurrentWeather({ weather }: any) {
return (
<Stack
gap={0}
w={'100%'}
justify='space-between'
style={{
backgroundColor: 'var(--color-search-background)',
padding: '1rem',
borderRadius: '8px',
paddingBottom: '.5rem',
}}
>
<Group gap={0} px={16} w={'100%'}>
<Text
c={'var(--color-text-pri)'}
style={{
fontSize: '1.375rem',
textAlign: 'left',
}}
>
Current Weather
</Text>
</Group>
<Group gap={0} w={'100%'}>
<Stack gap={0} w={'50%'}>
<Group gap={0}>
<WeatherIcon textDescription={`${weather.textDescription}`} />
<Text fz={'7rem'} fw={200}>
{weather.temperature}°<sup style={{ fontSize: '3rem' }}>F</sup>
</Text>
</Group>
<Text
c={'var(--color-text-pri)'}
style={{
fontSize: '1.375rem',
textAlign: 'left',
marginTop: -32,
marginLeft: 16,
}}
>
{weather.textDescription}
</Text>
</Stack>
<Stack gap={4} w={'50%'}>
<Text>
Feels like {weather.heatIndex ?? weather.windChill ?? weather.temperature}°
<sup>F</sup>
</Text>
<Grid align='center' w={'100%'}>
<Grid.Col span={2}>
<ThemeIcon
h={'auto'}
w={'auto'}
variant='subtle'
c={'var(--color-text-pri)'}
>
<FaLongArrowAltUp size={32} />
</ThemeIcon>
</Grid.Col>
<Grid.Col span={1}>
<Text>
{weather.highTemp}°<sup>F</sup>
</Text>
</Grid.Col>
<Grid.Col span={2}>
<ThemeIcon
h={'auto'}
w={'auto'}
variant='subtle'
c={'var(--color-text-pri)'}
>
<FaLongArrowAltDown size={32} />
</ThemeIcon>
</Grid.Col>
<Grid.Col span={1}>
<Text>
{weather.lowTemp}°<sup>F</sup>
</Text>
</Grid.Col>
</Grid>
<Grid align='center'>
<Grid.Col span={2}>
<ThemeIcon
h={'auto'}
w={'auto'}
variant='subtle'
c={'var(--color-text-pri)'}
>
<WiHumidity size={32} />
</ThemeIcon>
</Grid.Col>
<Grid.Col span={2}>
<Text>Humidity</Text>
</Grid.Col>
<Grid.Col span={2}>
<Text>{weather.humidity}%</Text>
</Grid.Col>
</Grid>
<Grid align='center'>
<Grid.Col span={2}>
<ThemeIcon
h={'auto'}
w={'auto'}
variant='subtle'
c={'var(--color-text-pri)'}
>
<FaWind size={32} />
</ThemeIcon>
</Grid.Col>
<Grid.Col span={2}>
<Text>Wind</Text>
</Grid.Col>
<Grid.Col span={2}>
<Text>{weather.windSpeed}mph</Text>
</Grid.Col>
</Grid>
<Grid align='center'>
<Grid.Col span={2}>
<ThemeIcon
h={'auto'}
w={'auto'}
variant='subtle'
c={'var(--color-text-pri)'}
>
<WiBarometer size={32} />
</ThemeIcon>
</Grid.Col>
<Grid.Col span={2}>
<Text>Pressure</Text>
</Grid.Col>
<Grid.Col span={2}>
<Text>{weather.pressure} in</Text>
</Grid.Col>
</Grid>
</Stack>
</Group>
</Stack>
);
}

View File

@@ -0,0 +1,52 @@
import { Group, Stack, Text } from '@mantine/core';
import WeatherIcon from './WeatherIcon';
export default function ExtendedForecast({ forecast }: any) {
const day = forecast.slice(2, 14).filter((item: any) => item.isDaytime);
const night = forecast.slice(2, 14).filter((item: any) => !item.isDaytime);
return (
<Stack
gap={0}
w={'100%'}
justify='space-between'
style={{
backgroundColor: 'var(--color-search-background)',
padding: '1rem',
borderRadius: '8px',
paddingBottom: '.5rem',
}}
>
<Group gap={0} px={16} w={'100%'}>
<Text
c={'var(--color-text-pri)'}
style={{
fontSize: '1.375rem',
textAlign: 'left',
}}
>
Extended Forecast
</Text>
</Group>
<Group w={'100%'} align='center' justify='space-between'>
{day.map((item: any, index: number) => {
const short = item.shortForecast.includes('And')
? item.shortForecast.substring(0, item.shortForecast.indexOf('And'))
: item.shortForecast.includes('then')
? item.shortForecast.substring(0, item.shortForecast.indexOf('then'))
: item.shortForecast;
return (
<Stack key={item.name} gap={0} align='center'>
<Text>{item.name}</Text>
<WeatherIcon textDescription={short} />
<Text>{short}</Text>
<Text>
{item.temperature}°{item.temperatureUnit}/{night[index].temperature}°
{item.temperatureUnit}
</Text>
</Stack>
);
})}
</Group>
</Stack>
);
}

View File

@@ -94,7 +94,7 @@ export default function HourlyCard({ weather }: any) {
color: 'white', color: 'white',
}} }}
> >
{weather.temperature}°{weather.temperatureUnit}/{weather.relativeHumidity.value}% {weather.temperature}°{weather.temperatureUnit}/{weather.relativeHumidity?.value}%
</Text> </Text>
<Box <Box
mt={4} mt={4}
@@ -114,7 +114,7 @@ export default function HourlyCard({ weather }: any) {
transform: `rotate(${WindDirection[weather.windDirection]}deg)`, transform: `rotate(${WindDirection[weather.windDirection]}deg)`,
}} }}
/> />
{`${weather.probabilityOfPrecipitation.value}`}% {`${weather.probabilityOfPrecipitation?.value}`}%
</Group> </Group>
</Box> </Box>
</Stack> </Stack>

View File

@@ -0,0 +1,251 @@
import { Image } from '@mantine/core';
import CloudyIcon from '../../assets/weather/cloudy.svg';
import HazeIcon from '../../assets/weather/haze.svg';
import HeavyRainIcon from '../../assets/weather/heavy-rain.svg';
import PartlyCloudyIcon from '../../assets/weather/partly-cloudy.svg';
import RainIcon from '../../assets/weather/rain.svg';
import SleetIcon from '../../assets/weather/sleet.svg';
import SnowIcon from '../../assets/weather/snow.svg';
import SunnyIcon from '../../assets/weather/sunny.svg';
import ThunderstormIcon from '../../assets/weather/thunderstorm.svg';
export default function WeatherIcon({ textDescription }: { textDescription: string }) {
const Icon = () => {
switch (textDescription.trim()) {
case 'Clear':
return (
<Image
style={{ width: '100px', height: '100px' }}
src={SunnyIcon}
alt='SunnyIcon'
/>
);
case 'Cloudy':
return (
<Image
style={{ width: '100px', height: '100px' }}
src={CloudyIcon}
alt='CloudyIcon'
/>
);
case 'Mostly Cloudy':
return (
<Image
style={{ width: '100px', height: '100px' }}
src={CloudyIcon}
alt='CloudyIcon'
/>
);
case 'Rain':
return (
<Image
style={{ width: '100px', height: '100px' }}
src={RainIcon}
alt='RainIcon'
/>
);
case 'Showers':
return (
<Image
style={{ width: '100px', height: '100px' }}
src={RainIcon}
alt='RainIcon'
/>
);
case 'Drizzle':
return (
<Image
style={{ width: '100px', height: '100px' }}
src={RainIcon}
alt='RainIcon'
/>
);
case 'Snow':
return (
<Image
style={{ width: '100px', height: '100px' }}
src={SnowIcon}
alt='SnowIcon'
/>
);
case 'Thunderstorm':
return (
<Image
style={{ width: '100px', height: '100px' }}
src={ThunderstormIcon}
alt='ThunderstormIcon'
/>
);
case 'Mist':
return (
<Image
style={{ width: '100px', height: '100px' }}
src={HazeIcon}
alt='HazeIcon'
/>
);
case 'Haze':
return (
<Image
style={{ width: '100px', height: '100px' }}
src={HazeIcon}
alt='HazeIcon'
/>
);
case 'Dust':
return (
<Image
style={{ width: '100px', height: '100px' }}
src={HazeIcon}
alt='HazeIcon'
/>
);
case 'Fog':
return (
<Image
style={{ width: '100px', height: '100px' }}
src={HazeIcon}
alt='HazeIcon'
/>
);
case 'Smoke':
return (
<Image
style={{ width: '100px', height: '100px' }}
src={HazeIcon}
alt='HazeIcon'
/>
);
case 'Sand':
return (
<Image
style={{ width: '100px', height: '100px' }}
src={HazeIcon}
alt='HazeIcon'
/>
);
case 'Ash':
return (
<Image
style={{ width: '100px', height: '100px' }}
src={HazeIcon}
alt='HazeIcon'
/>
);
case 'Squall':
return (
<Image
style={{ width: '100px', height: '100px' }}
src={HazeIcon}
alt='HazeIcon'
/>
);
case 'Tornado':
return (
<Image
style={{ width: '100px', height: '100px' }}
src={HazeIcon}
alt='HazeIcon'
/>
);
case 'Freezing Rain':
return (
<Image
style={{ width: '100px', height: '100px' }}
src={HazeIcon}
alt='HazeIcon'
/>
);
case 'Shower Rain':
return (
<Image
style={{ width: '100px', height: '100px' }}
src={HazeIcon}
alt='HazeIcon'
/>
);
case 'Heavy Rain':
return (
<Image
style={{ width: '100px', height: '100px' }}
src={HeavyRainIcon}
alt='HeavyRainIcon'
/>
);
case 'Scattered Clouds':
return (
<Image
style={{ width: '100px', height: '100px' }}
src={PartlyCloudyIcon}
alt='PartlyCloudyIcon'
/>
);
case 'Broken Clouds':
return (
<Image
style={{ width: '100px', height: '100px' }}
src={PartlyCloudyIcon}
alt='PartlyCloudyIcon'
/>
);
case 'Overcast Clouds':
return (
<Image
style={{ width: '100px', height: '100px' }}
src={PartlyCloudyIcon}
alt='PartlyCloudyIcon'
/>
);
case 'Moderate Rain':
return (
<Image
style={{ width: '100px', height: '100px' }}
src={PartlyCloudyIcon}
alt='PartlyCloudyIcon'
/>
);
case 'Light Rain':
return (
<Image
style={{ width: '100px', height: '100px' }}
src={PartlyCloudyIcon}
alt='PartlyCloudyIcon'
/>
);
case 'Heavy Snow':
return (
<Image
style={{ width: '100px', height: '100px' }}
src={SnowIcon}
alt='SnowIcon'
/>
);
case 'Sleet':
return (
<Image
style={{ width: '100px', height: '100px' }}
src={SleetIcon}
alt='SleetIcon'
/>
);
case 'Partly Sunny':
return (
<Image
style={{ width: '100px', height: '100px' }}
src={PartlyCloudyIcon}
alt='PartlyCloudyIcon'
/>
);
default:
return (
<Image
style={{ width: '100px', height: '100px' }}
src={SunnyIcon}
alt='SunnyIcon'
/>
);
}
};
return <Icon />;
}

View File

@@ -1,15 +1,26 @@
import { Box, Center, Divider, Group, Modal, Stack, Text } from '@mantine/core'; import { Center, Group, Modal, Stack, Text } from '@mantine/core';
import { modalClasses } from 'styles'; import { modalClasses } from 'styles';
import SmallOutlookCard from './SmallOutlookCard';
import SmallWeatherCard from './SmallWeatherCard';
import { useCommonHooks, useWeatherAPI } from 'hooks'; import { useCommonHooks, useWeatherAPI } from 'hooks';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import HourlyCard from './HourlyCard'; import {
Area,
CartesianGrid,
ComposedChart,
Legend,
Line,
ResponsiveContainer,
Tooltip,
XAxis,
YAxis,
} from 'recharts';
import CurrentWeather from './CurrentWeather';
import ExtendedForecast from './ExtendedForecast';
export function WeatherModal({ export function WeatherModal({
current, current,
outlook, outlook,
extendedForecast,
closeWeatherModal, closeWeatherModal,
weatherModalOpened, weatherModalOpened,
}: any) { }: any) {
@@ -31,9 +42,32 @@ export function WeatherModal({
const [hourlyForecast, setHourlyForecast] = useState<any>([]); const [hourlyForecast, setHourlyForecast] = useState<any>([]);
const [sunset, setSunset] = useState(100); const [sunset, setSunset] = useState(100);
function CustomTooltip({ payload, active, unit }: any) {
if (active) {
return (
<Stack
style={{
backgroundColor: 'var(--color-background)',
padding: '1rem',
borderRadius: '8px',
}}
>
{payload.map((item: any) => (
<Text
key={item.name}
size='sm'
>{`${item.name} - ${item.value}${item.unit}`}</Text>
))}
</Stack>
);
}
return null;
}
useEffect(() => { useEffect(() => {
if (hourly) { if (hourly) {
setHourlyForecast(hourly.properties.periods.slice(1, 13)); setHourlyForecast(hourly.properties.periods.slice(1, 25));
} }
}, [hourly]); }, [hourly]);
@@ -49,7 +83,6 @@ export function WeatherModal({
); );
} }
}, [hourlyForecast]); }, [hourlyForecast]);
return ( return (
<Modal.Root <Modal.Root
fullScreen={isMobile} fullScreen={isMobile}
@@ -89,58 +122,99 @@ export function WeatherModal({
</Modal.Title> </Modal.Title>
<Modal.CloseButton /> <Modal.CloseButton />
</Modal.Header> </Modal.Header>
<Modal.Body pb={'2rem'}> <Modal.Body m={0}>
<Center w={'100%'}> <Center w={'100%'}>
<Stack w={'100%'}> <Stack w={'100%'}>
<Group gap={0} w={'100%'} align='center' justify='center'> <CurrentWeather weather={current} />
<Stack gap={0} align='center'> <Stack
<h3 style={{ padding: 0, margin: 0, color: 'white' }}>Currently</h3>
<Stack>
<SmallWeatherCard weather={current} />
</Stack>
</Stack>
<Divider
orientation={isMobile ? 'horizontal' : 'vertical'}
mx={16}
color={theme.accentColor}
/>
<Group gap={8}>
{outlook.map((o: any) => (
<Stack key={o.name} gap={0} align='center'>
<h3 style={{ padding: 0, margin: 0, color: 'white' }}>{o.name}</h3>
<Stack>
<SmallOutlookCard weather={o} />
</Stack>
</Stack>
))}
</Group>
</Group>
<Divider orientation='horizontal' mx={8} color={theme.accentColor} />
<Group
style={{ style={{
background: background: 'var(--color-search-background)',
hourlyForecast.length > 0 && hourlyForecast[0].isDaytime
? `linear-gradient(90deg, rgba(245, 225, 171, 0.85) ${sunset - 5}%, rgba(35, 28, 42, 0.85) ${sunset + 5}%)`
: `linear-gradient(90deg, rgba(35, 28, 42, 0.85)${sunset - 5}%, rgba(245, 225, 171, 0.85)${sunset + 5}%)`,
padding: '.5rem', padding: '.5rem',
borderRadius: '.5rem', borderRadius: '.5rem',
}} }}
> >
<Box <ResponsiveContainer width={'100%'} height={150}>
style={{ <ComposedChart data={hourlyForecast}>
display: 'flex', <XAxis
overflow: 'auto', type='category'
whiteSpace: 'nowrap', stroke={theme.primaryColor}
justifyContent: 'space-between', dataKey={(data: any) => {
width: '100%', return new Date(data.startTime).toLocaleTimeString('en-US', {
}} hour: 'numeric',
> minute: 'numeric',
{hourlyForecast.length > 0 && });
hourlyForecast.map((h: any) => { }}
return <HourlyCard key={h.number} weather={h} />; />
})} <YAxis
</Box> stroke={theme.primaryColor}
</Group> unit={'°F'}
dataKey={'temperature'}
/>
<CartesianGrid stroke='#f5f5f522' />
<Tooltip content={<CustomTooltip />} />
<Line
unit={'°F'}
type='monotone'
name='Temperature'
dataKey='temperature'
stroke={theme.primaryColor}
dot={{ stroke: theme.primaryColor, strokeWidth: 1 }}
/>
<Line
unit={'°F'}
type='monotone'
name='Dew Point'
dataKey={(data: any) => {
return (data.dewpoint.value * 9) / 5 + 32;
}}
stroke={theme.accentColor}
dot={{ stroke: theme.primaryColor, strokeWidth: 1 }}
/>
<Legend style={{ background: 'var(--color-search-background)' }} />
</ComposedChart>
</ResponsiveContainer>
<ResponsiveContainer width={'100%'} height={150}>
<ComposedChart data={hourlyForecast}>
<XAxis
type='category'
stroke={theme.primaryColor}
dataKey={(data: any) => {
return new Date(data.startTime).toLocaleTimeString('en-US', {
hour: 'numeric',
minute: 'numeric',
});
}}
/>
<YAxis stroke={theme.primaryColor} unit={'%'} domain={[0, 100]} />
<CartesianGrid stroke='#f5f5f522' />
<Tooltip content={<CustomTooltip />} />
<Area
unit={'%'}
type='monotone'
name='Precipitation'
dataKey={(data: any) => {
return data.probabilityOfPrecipitation.value;
}}
stroke={theme.primaryColor}
fill={theme.accentColor}
dot={{ stroke: theme.primaryColor, strokeWidth: 1 }}
/>
<Area
unit={'%'}
type='monotone'
name='Humidity'
dataKey={(data: any) => {
return data.relativeHumidity.value;
}}
stroke={theme.accentColor}
fill={theme.accentColor}
dot={{ stroke: theme.primaryColor, strokeWidth: 1 }}
/>
<Legend style={{ background: 'var(--color-search-background)' }} />
</ComposedChart>
</ResponsiveContainer>
</Stack>
<ExtendedForecast forecast={extendedForecast} />
</Stack> </Stack>
</Center> </Center>
</Modal.Body> </Modal.Body>

View File

@@ -4,6 +4,7 @@ import { useCommonHooks, useOSMAPI, useQueryAPI, useWeatherAPI } from 'hooks';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { stackClasses } from 'styles'; import { stackClasses } from 'styles';
import type { CurrentWeather, OutlookWeather } from 'types'; import type { CurrentWeather, OutlookWeather } from 'types';
import type { ExtendedForecast } from 'types/weather';
import Applications from './components/Applications/Applications'; import Applications from './components/Applications/Applications';
import Bookmarks from './components/Bookmarks/Bookmarks'; import Bookmarks from './components/Bookmarks/Bookmarks';
import Header from './components/Header/Header'; import Header from './components/Header/Header';
@@ -106,14 +107,17 @@ export function Homepage() {
}); });
const [current, setCurrent] = useState<CurrentWeather>({ const [current, setCurrent] = useState<CurrentWeather>({
icon: '', icon: '',
textDescription: '',
temperature: 0, temperature: 0,
highTemp: 0, highTemp: 0,
lowTemp: 0, lowTemp: 0,
precipitation: 'N/A', precipitation: 'N/A',
humidity: 0, humidity: 0,
heatIndex: 0, heatIndex: null,
windChill: null,
windSpeed: 0, windSpeed: 0,
windDirection: 0, windDirection: 0,
pressure: 0,
}); });
const [outlook, setOutlook] = useState<OutlookWeather[]>([ const [outlook, setOutlook] = useState<OutlookWeather[]>([
{ {
@@ -126,11 +130,22 @@ export function Homepage() {
windDirection: 'N', windDirection: 'N',
}, },
]); ]);
const [extendedForecast, setExtendedForecast] = useState<ExtendedForecast[]>([
{
detailedForecast: '',
isDaytime: true,
name: '',
shortForecast: '',
temperature: 0,
temperatureUnit: 'F',
},
]);
useEffect(() => { useEffect(() => {
if (weather !== undefined && basicWeather !== undefined) { if (weather !== undefined && basicWeather !== undefined) {
setCurrent({ setCurrent({
icon: weather.features[0]?.properties.icon?.replace('medium', 'large') || '', icon: weather.features[0]?.properties.icon?.replace('medium', 'large') || '',
textDescription: weather.features[0]?.properties.textDescription,
temperature: Math.round( temperature: Math.round(
(weather.features[0]?.properties.temperature.value * 9) / 5 + 32, (weather.features[0]?.properties.temperature.value * 9) / 5 + 32,
), ),
@@ -143,13 +158,23 @@ export function Homepage() {
precipitation: precipitation:
weather.features[0]?.properties.precipitationLastHour.value || 'N/A', weather.features[0]?.properties.precipitationLastHour.value || 'N/A',
humidity: Math.round(weather.features[0]?.properties.relativeHumidity.value), humidity: Math.round(weather.features[0]?.properties.relativeHumidity.value),
heatIndex: Math.round( heatIndex:
weather.features[0]?.properties.heatIndex.value !== null basicWeather.properties.heatIndex?.values[0].value === null
? (weather.features[0]?.properties.heatIndex.value * 9) / 5 + 32 ? null
: (weather.features[0]?.properties.temperature.value * 9) / 5 + 32, : Math.round(
), (basicWeather.properties.heatIndex.values[0].value * 9) / 5 + 32,
),
windChill:
basicWeather.properties.windChill?.values[0].value === null
? null
: Math.round(
(basicWeather.properties.windChill.values[0].value * 9) / 5 + 32,
),
windSpeed: Math.round(weather.features[0]?.properties.windSpeed.value / 1.60934), windSpeed: Math.round(weather.features[0]?.properties.windSpeed.value / 1.60934),
windDirection: weather.features[0]?.properties.windDirection.value, windDirection: weather.features[0]?.properties.windDirection.value,
pressure: Math.round(
weather.features[0]?.properties.barometricPressure.value / 3386.38,
),
}); });
} }
}, [weather, basicWeather]); }, [weather, basicWeather]);
@@ -179,6 +204,16 @@ export function Homepage() {
windDirection: forecast.properties.periods[1].windDirection, windDirection: forecast.properties.periods[1].windDirection,
}, },
]); ]);
setExtendedForecast(
forecast.properties.periods.map((item: any) => ({
detailedForecast: item.detailedForecast,
isDaytime: item.isDaytime,
name: item.name,
shortForecast: item.shortForecast,
temperature: item.temperature,
temperatureUnit: item.temperatureUnit,
})),
);
setCurrentLocation({ setCurrentLocation({
office: points?.properties.gridId, office: points?.properties.gridId,
station: stations?.features[0].properties.stationIdentifier, station: stations?.features[0].properties.stationIdentifier,
@@ -189,11 +224,13 @@ export function Homepage() {
}); });
} }
}, [forecast]); }, [forecast]);
return ( return (
<Stack gap={0} className={stackClasses.containerStack} justify='center'> <Stack gap={0} className={stackClasses.containerStack} justify='center'>
<Search <Search
current={current} current={current}
outlook={outlook} outlook={outlook}
extendedForecast={extendedForecast}
weather={weather} weather={weather}
basicWeather={basicWeather} basicWeather={basicWeather}
weatherModalOpened={weatherModalOpened} weatherModalOpened={weatherModalOpened}
@@ -206,6 +243,7 @@ export function Homepage() {
current={current} current={current}
setCurrent={setCurrent} setCurrent={setCurrent}
outlook={outlook} outlook={outlook}
extendedForecast={extendedForecast}
weather={weather} weather={weather}
basicWeather={basicWeather} basicWeather={basicWeather}
weatherModalOpened={weatherModalOpened} weatherModalOpened={weatherModalOpened}

View File

@@ -10,6 +10,7 @@ function Header({
current, current,
setCurrent, setCurrent,
outlook, outlook,
extendedForecast,
weather, weather,
basicWeather, basicWeather,
weatherModalOpened, weatherModalOpened,
@@ -154,6 +155,7 @@ function Header({
<WeatherModal <WeatherModal
current={current} current={current}
outlook={outlook} outlook={outlook}
extendedForecast={extendedForecast}
closeWeatherModal={closeWeatherModal} closeWeatherModal={closeWeatherModal}
weatherModalOpened={weatherModalOpened} weatherModalOpened={weatherModalOpened}
/> />

View File

@@ -8,6 +8,7 @@ import { textInputClasses } from 'styles';
export default function Search({ export default function Search({
current, current,
outlook, outlook,
extendedForecast,
weatherModalOpened, weatherModalOpened,
closeWeatherModal, closeWeatherModal,
openWeatherModal, openWeatherModal,
@@ -161,6 +162,7 @@ export default function Search({
<WeatherModal <WeatherModal
current={current} current={current}
outlook={outlook} outlook={outlook}
extendedForecast={extendedForecast}
closeWeatherModal={closeWeatherModal} closeWeatherModal={closeWeatherModal}
weatherModalOpened={weatherModalOpened} weatherModalOpened={weatherModalOpened}
/> />

View File

@@ -1,13 +1,16 @@
export interface CurrentWeather { export interface CurrentWeather {
icon: string; icon: string;
textDescription: string;
temperature: number; temperature: number;
highTemp: number; highTemp: number;
lowTemp: number; lowTemp: number;
precipitation: string; precipitation: string;
humidity: number; humidity: number;
heatIndex: number; heatIndex: number | null;
windChill: number | null;
windSpeed: number; windSpeed: number;
windDirection: number; windDirection: number;
pressure: number;
} }
export interface OutlookWeather { export interface OutlookWeather {
@@ -19,3 +22,12 @@ export interface OutlookWeather {
windSpeed: string; windSpeed: string;
windDirection: string; windDirection: string;
} }
export interface ExtendedForecast {
detailedForecast: string;
isDaytime: boolean;
name: string;
shortForecast: string;
temperature: number;
temperatureUnit: string;
}