Created WeatherWidget

This commit is contained in:
unknown 2021-05-19 18:26:57 +02:00
parent 38f5d3b66a
commit e170f56a03
11 changed files with 462 additions and 57 deletions

View file

@ -19,29 +19,6 @@
margin-bottom: 2.5rem;
}
.WeatherWidget {
display: flex;
}
.WeatherDetails {
display: flex;
flex-direction: column;
justify-content: center;
font-size: 16px;
color: var(--color-primary);
margin-left: 10px;
font-weight: 500;
}
.WeatherDetails span:first-child {
border-bottom: 1px solid var(--color-primary);
padding-bottom: 5px;
}
.WeatherDetails span:last-child {
padding-top: 5px;
}
.SettingsButton {
width: 35px;
height: 35px;

View file

@ -5,7 +5,6 @@ import { GlobalState } from '../../interfaces/GlobalState';
import { getApps } from '../../store/actions';
import Icon from '../UI/Icons/Icon/Icon';
import WeatherIcon from '../UI/Icons/WeatherIcon/WeatherIcon';
import classes from './Home.module.css';
import { Container } from '../UI/Layout/Layout';
@ -13,6 +12,7 @@ import SectionHeadline from '../UI/Headlines/SectionHeadline/SectionHeadline';
import AppGrid from '../Apps/AppGrid/AppGrid';
import { App } from '../../interfaces';
import Spinner from '../UI/Spinner/Spinner';
import WeatherWidget from '../Widgets/WeatherWidget/WeatherWidget';
interface ComponentProps {
getApps: Function;
@ -63,19 +63,11 @@ const Home = (props: ComponentProps): JSX.Element => {
<p>{dateAndTime()}</p>
<span className={classes.HeaderMain}>
<h1>{greeter()}</h1>
<div className={classes.WeatherWidget}>
<div className={classes.WeatherIcon}>
<WeatherIcon icon='clear-day' />
</div>
<div className={classes.WeatherDetails}>
<span>30°C</span>
<span>15°C</span>
</div>
</div>
<WeatherWidget />
</span>
</header>
<SectionHeadline title='Apps' link='/apps' />
<SectionHeadline title='Applications' link='/applications' />
{props.loading
? <Spinner />
: <AppGrid apps={props.apps.filter((app: App) => app.isPinned)} />

View file

@ -0,0 +1,340 @@
{
"mapping": [
{
"code": 1000,
"icon": {
"day": "clear-day",
"night": "clear-night"
}
},
{
"code": 1003,
"icon": {
"day": "partly-cloudy-day",
"night": "partly-cloudy-night"
}
},
{
"code": 1006,
"icon": {
"day": "cloudy",
"night": "cloudy"
}
},
{
"code": 1009,
"icon": {
"day": "cloudy",
"night": "cloudy"
}
},
{
"code": 1030,
"icon": {
"day": "fog",
"night": "fog"
}
},
{
"code": 1063,
"icon": {
"day": "rain-day",
"night": "rain-night"
}
},
{
"code": 1066,
"icon": {
"day": "snow-day",
"night": "snow-night"
}
},
{
"code": 1069,
"icon": {
"day": "rain-snow-day",
"night": "rain-snow-night"
}
},
{
"code": 1072,
"icon": {
"day": "sleet",
"night": "sleet"
}
},
{
"code": 1087,
"icon": {
"day": "thunder-day",
"night": "thunder-night"
}
},
{
"code": 1114,
"icon": {
"day": "snow",
"night": "snow"
}
},
{
"code": 1117,
"icon": {
"day": "snow",
"night": "snow"
}
},
{
"code": 1135,
"icon": {
"day": "fog",
"night": "fog"
}
},
{
"code": 1147,
"icon": {
"day": "fog",
"night": "fog"
}
},
{
"code": 1150,
"icon": {
"day": "rain",
"night": "rain"
}
},
{
"code": 1153,
"icon": {
"day": "rain",
"night": "rain"
}
},
{
"code": 1168,
"icon": {
"day": "sleet",
"night": "sleet"
}
},
{
"code": 1171,
"icon": {
"day": "sleet",
"night": "sleet"
}
},
{
"code": 1180,
"icon": {
"day": "rain-day",
"night": "rain-night"
}
},
{
"code": 1183,
"icon": {
"day": "rain",
"night": "rain"
}
},
{
"code": 1186,
"icon": {
"day": "rain-day",
"night": "rain-night"
}
},
{
"code": 1189,
"icon": {
"day": "rain",
"night": "rain"
}
},
{
"code": 1192,
"icon": {
"day": "rain-day",
"night": "rain-night"
}
},
{
"code": 1195,
"icon": {
"day": "rain",
"night": "rain"
}
},
{
"code": 1198,
"icon": {
"day": "sleet",
"night": "sleet"
}
},
{
"code": 1201,
"icon": {
"day": "sleet",
"night": "sleet"
}
},
{
"code": 1204,
"icon": {
"day": "rain-snow",
"night": "rain-snow"
}
},
{
"code": 1207,
"icon": {
"day": "rain-snow",
"night": "rain-snow"
}
},
{
"code": 1210,
"icon": {
"day": "snow-day",
"night": "snow-night"
}
},
{
"code": 1213,
"icon": {
"day": "snow",
"night": "snow"
}
},
{
"code": 1216,
"icon": {
"day": "snow-day",
"night": "snow-night"
}
},
{
"code": 1219,
"icon": {
"day": "snow",
"night": "snow"
}
},
{
"code": 1222,
"icon": {
"day": "snow-day",
"night": "snow-night"
}
},
{
"code": 1225,
"icon": {
"day": "snow",
"night": "snow"
}
},
{
"code": 1237,
"icon": {
"day": "hail",
"night": "hail"
}
},
{
"code": 1240,
"icon": {
"day": "rain-day",
"night": "rain-night"
}
},
{
"code": 1243,
"icon": {
"day": "rain-day",
"night": "rain-night"
}
},
{
"code": 1246,
"icon": {
"day": "rain-day",
"night": "rain-night"
}
},
{
"code": 1249,
"icon": {
"day": "rain-snow-day",
"night": "rain-snow-night"
}
},
{
"code": 1252,
"icon": {
"day": "rain-snow-day",
"night": "rain-snow-night"
}
},
{
"code": 1255,
"icon": {
"day": "snow-day",
"night": "snow-night"
}
},
{
"code": 1258,
"icon": {
"day": "snow-day",
"night": "snow-night"
}
},
{
"code": 1261,
"icon": {
"day": "hail",
"night": "hail"
}
},
{
"code": 1264,
"icon": {
"day": "hail",
"night": "hail"
}
},
{
"code": 1273,
"icon": {
"day": "thunder-rain-day",
"night": "thunder-rain-night"
}
},
{
"code": 1276,
"icon": {
"day": "thunder-rain",
"night": "thunder-rain"
}
},
{
"code": 1279,
"icon": {
"day": "thunder-day",
"night": "thunder-night"
}
},
{
"code": 1282,
"icon": {
"day": "thunder",
"night": "thunder"
}
}
]
}

View file

@ -0,0 +1,22 @@
.WeatherWidget {
display: flex;
}
.WeatherDetails {
display: flex;
flex-direction: column;
justify-content: center;
font-size: 16px;
color: var(--color-primary);
margin-left: 10px;
font-weight: 500;
}
.WeatherDetails span:first-child {
border-bottom: 1px solid var(--color-primary);
padding-bottom: 5px;
}
.WeatherDetails span:last-child {
padding-top: 5px;
}

View file

@ -0,0 +1,52 @@
import { useState, useEffect, Fragment } from 'react';
import { Weather, ApiResponse } from '../../../interfaces';
import axios from 'axios';
import WeatherIcon from '../../UI/Icons/WeatherIcon/WeatherIcon';
import classes from './WeatherWidget.module.css';
const WeatherWidget = (): JSX.Element => {
const [weather, setWeather] = useState<Weather>({
externalLastUpdate: '',
tempC: 0,
tempF: 0,
isDay: 1,
conditionText: '',
conditionCode: 1000,
id: 0,
createdAt: new Date(),
updatedAt: new Date()
});
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
axios.get<ApiResponse<Weather[]>>('/api/weather')
.then(data => {
setWeather(data.data.data[0]);
setIsLoading(false);
})
.catch(err => console.log(err));
}, []);
return (
<div className={classes.WeatherWidget}>
{isLoading
? 'loading'
: (
<Fragment>
<div className={classes.WeatherIcon}>
<WeatherIcon weatherStatusCode={weather.conditionCode} />
</div>
<div className={classes.WeatherDetails}>
<span>{weather.tempC}°C</span>
<span>{weather.conditionCode}</span>
</div>
</Fragment>
)
}
</div>
)
}
export default WeatherWidget;

View file

@ -0,0 +1,10 @@
export interface Model {
id: number;
createdAt: Date;
updatedAt: Date;
}
export interface ApiResponse<T> {
success: boolean;
data: T;
}

View file

@ -1,16 +1,10 @@
export interface App {
id: number;
import { Model } from './Api';
export interface App extends Model {
name: string;
url: string;
icon: string;
isPinned: boolean;
createdAt: Date;
updatedAt: Date;
}
export interface AppResponse<T> {
success: boolean;
data: T;
}
export interface NewApp {

View file

@ -0,0 +1,10 @@
import { Model } from './Api';
export interface Weather extends Model {
externalLastUpdate: string;
tempC: number;
tempF: number;
isDay: number;
conditionText: string;
conditionCode: number;
}

View file

@ -1,3 +1,5 @@
export * from './App';
export * from './Theme';
export * from './GlobalState';
export * from './GlobalState';
export * from './Api';
export * from './Weather';

View file

@ -1,7 +1,7 @@
import axios from 'axios';
import { Dispatch } from 'redux';
import { ActionTypes } from './actionTypes';
import { App, AppResponse, NewApp } from '../../interfaces/App';
import { App, ApiResponse, NewApp } from '../../interfaces';
export interface GetAppsAction<T> {
type: ActionTypes.getApps | ActionTypes.getAppsSuccess | ActionTypes.getAppsError;
@ -15,7 +15,7 @@ export const getApps = () => async (dispatch: Dispatch) => {
});
try {
const res = await axios.get<AppResponse<App[]>>('/api/apps');
const res = await axios.get<ApiResponse<App[]>>('/api/apps');
dispatch<GetAppsAction<App[]>>({
type: ActionTypes.getAppsSuccess,
@ -36,7 +36,7 @@ export interface PinAppAction {
export const pinApp = (id: number, isPinned: boolean) => async (dispatch: Dispatch) => {
try {
const res = await axios.put<AppResponse<App>>(`/api/apps/${id}`, { isPinned: !isPinned });
const res = await axios.put<ApiResponse<App>>(`/api/apps/${id}`, { isPinned: !isPinned });
dispatch<PinAppAction>({
type: ActionTypes.pinApp,
@ -54,7 +54,7 @@ export interface AddAppAction {
export const addApp = (formData: NewApp) => async (dispatch: Dispatch) => {
try {
const res = await axios.post<AppResponse<App>>('/api/apps', formData);
const res = await axios.post<ApiResponse<App>>('/api/apps', formData);
dispatch<AddAppAction>({
type: ActionTypes.addAppSuccess,
@ -72,7 +72,7 @@ export interface DeleteAppAction {
export const deleteApp = (id: number) => async (dispatch: Dispatch) => {
try {
const res = await axios.delete<AppResponse<{}>>(`/api/apps/${id}`);
const res = await axios.delete<ApiResponse<{}>>(`/api/apps/${id}`);
dispatch<DeleteAppAction>({
type: ActionTypes.deleteApp,

View file

@ -3,21 +3,27 @@ const Weather = require('../models/Weather');
const axios = require('axios');
const getExternalWeather = async () => {
// Get API key from database
let secret = await Config.findOne({
where: { key: 'WEATHER_API_KEY' }
});
// Get config from database
const config = await Config.findAll();
// Find and check values
const secret = config.find(pair => pair.key === 'WEATHER_API_KEY');
const lat = config.find(pair => pair.key === 'lat');
const long = config.find(pair => pair.key === 'long');
if (!secret) {
console.log('API key was not found');
console.log('API key was not found. Weather updated failed');
return;
}
secret = secret.value;
if (!lat || !long) {
console.log('Location was not found. Weather updated failed');
return;
}
// Fetch data from external API
try {
const res = await axios.get(`http://api.weatherapi.com/v1/current.json?key=${secret}&q=52.229676,21.012229`);
const res = await axios.get(`http://api.weatherapi.com/v1/current.json?key=${secret.value}&q=${lat.value},${long.value}`);
// For dev
// console.log(res.data);