diff --git a/client/src/components/Home/Home.module.css b/client/src/components/Home/Home.module.css
index 30ac2bb..09da7c5 100644
--- a/client/src/components/Home/Home.module.css
+++ b/client/src/components/Home/Home.module.css
@@ -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;
diff --git a/client/src/components/Home/Home.tsx b/client/src/components/Home/Home.tsx
index 6a928b7..5021a38 100644
--- a/client/src/components/Home/Home.tsx
+++ b/client/src/components/Home/Home.tsx
@@ -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 => {
{dateAndTime()}
{greeter()}
-
-
-
-
-
- 30°C
- 15°C
-
-
+
-
-
+
+
{props.loading
?
: app.isPinned)} />
diff --git a/client/src/components/UI/Icons/WeatherIcon/WeatherMapping.json b/client/src/components/UI/Icons/WeatherIcon/WeatherMapping.json
new file mode 100644
index 0000000..6a7bdaa
--- /dev/null
+++ b/client/src/components/UI/Icons/WeatherIcon/WeatherMapping.json
@@ -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"
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/client/src/components/Widgets/WeatherWidget/WeatherWidget.module.css b/client/src/components/Widgets/WeatherWidget/WeatherWidget.module.css
new file mode 100644
index 0000000..e13f5ab
--- /dev/null
+++ b/client/src/components/Widgets/WeatherWidget/WeatherWidget.module.css
@@ -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;
+}
\ No newline at end of file
diff --git a/client/src/components/Widgets/WeatherWidget/WeatherWidget.tsx b/client/src/components/Widgets/WeatherWidget/WeatherWidget.tsx
new file mode 100644
index 0000000..c3cf36b
--- /dev/null
+++ b/client/src/components/Widgets/WeatherWidget/WeatherWidget.tsx
@@ -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({
+ 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>('/api/weather')
+ .then(data => {
+ setWeather(data.data.data[0]);
+ setIsLoading(false);
+ })
+ .catch(err => console.log(err));
+ }, []);
+
+ return (
+
+ {isLoading
+ ? 'loading'
+ : (
+
+
+
+
+
+ {weather.tempC}°C
+ {weather.conditionCode}
+
+
+ )
+ }
+
+ )
+}
+
+export default WeatherWidget;
\ No newline at end of file
diff --git a/client/src/interfaces/Api.ts b/client/src/interfaces/Api.ts
new file mode 100644
index 0000000..2174598
--- /dev/null
+++ b/client/src/interfaces/Api.ts
@@ -0,0 +1,10 @@
+export interface Model {
+ id: number;
+ createdAt: Date;
+ updatedAt: Date;
+}
+
+export interface ApiResponse {
+ success: boolean;
+ data: T;
+}
\ No newline at end of file
diff --git a/client/src/interfaces/App.ts b/client/src/interfaces/App.ts
index ffdbf82..1904ea0 100644
--- a/client/src/interfaces/App.ts
+++ b/client/src/interfaces/App.ts
@@ -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 {
- success: boolean;
- data: T;
}
export interface NewApp {
diff --git a/client/src/interfaces/Weather.ts b/client/src/interfaces/Weather.ts
new file mode 100644
index 0000000..ea8e0d1
--- /dev/null
+++ b/client/src/interfaces/Weather.ts
@@ -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;
+}
\ No newline at end of file
diff --git a/client/src/interfaces/index.ts b/client/src/interfaces/index.ts
index 516fda3..5b42fe8 100644
--- a/client/src/interfaces/index.ts
+++ b/client/src/interfaces/index.ts
@@ -1,3 +1,5 @@
export * from './App';
export * from './Theme';
-export * from './GlobalState';
\ No newline at end of file
+export * from './GlobalState';
+export * from './Api';
+export * from './Weather';
\ No newline at end of file
diff --git a/client/src/store/actions/app.ts b/client/src/store/actions/app.ts
index 74fb079..9732a39 100644
--- a/client/src/store/actions/app.ts
+++ b/client/src/store/actions/app.ts
@@ -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 {
type: ActionTypes.getApps | ActionTypes.getAppsSuccess | ActionTypes.getAppsError;
@@ -15,7 +15,7 @@ export const getApps = () => async (dispatch: Dispatch) => {
});
try {
- const res = await axios.get>('/api/apps');
+ const res = await axios.get>('/api/apps');
dispatch>({
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>(`/api/apps/${id}`, { isPinned: !isPinned });
+ const res = await axios.put>(`/api/apps/${id}`, { isPinned: !isPinned });
dispatch({
type: ActionTypes.pinApp,
@@ -54,7 +54,7 @@ export interface AddAppAction {
export const addApp = (formData: NewApp) => async (dispatch: Dispatch) => {
try {
- const res = await axios.post>('/api/apps', formData);
+ const res = await axios.post>('/api/apps', formData);
dispatch({
type: ActionTypes.addAppSuccess,
@@ -72,7 +72,7 @@ export interface DeleteAppAction {
export const deleteApp = (id: number) => async (dispatch: Dispatch) => {
try {
- const res = await axios.delete>(`/api/apps/${id}`);
+ const res = await axios.delete>(`/api/apps/${id}`);
dispatch({
type: ActionTypes.deleteApp,
diff --git a/utils/getExternalWeather.js b/utils/getExternalWeather.js
index f265e20..b36d825 100644
--- a/utils/getExternalWeather.js
+++ b/utils/getExternalWeather.js
@@ -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);