From 0a0a7ddceedd7990b76712b22d5510f8d6bd2d87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=98yvind=20Isaksen?= Date: Fri, 25 Mar 2022 17:33:27 +0100 Subject: [PATCH] feat: add climate page and reading page --- .env.development | 1 + .env.production | 1 + env.ts | 11 +++++++ package-lock.json | 13 ++++++++ package.json | 1 + pages/climate/index.tsx | 38 ++++++++++++++++++++++++ pages/climate/location/[id]/index.tsx | 35 ++++++++++++++++++++++ services/api/climate-reading/host.ts | 27 +++++++++++++++++ services/api/climate-reading/readings.ts | 7 +++++ types/common.d.ts | 3 ++ types/domain.d.ts | 12 ++++++++ types/enums.ts | 3 ++ types/env.d.ts | 4 +++ types/index.d.ts | 3 ++ utils/common/index.ts | 17 +++++++++++ 15 files changed, 176 insertions(+) create mode 100644 .env.development create mode 100644 .env.production create mode 100644 env.ts create mode 100644 pages/climate/index.tsx create mode 100644 pages/climate/location/[id]/index.tsx create mode 100644 services/api/climate-reading/host.ts create mode 100644 services/api/climate-reading/readings.ts create mode 100644 types/common.d.ts create mode 100644 types/domain.d.ts create mode 100644 types/enums.ts create mode 100644 types/env.d.ts create mode 100644 types/index.d.ts create mode 100755 utils/common/index.ts diff --git a/.env.development b/.env.development new file mode 100644 index 0000000..a37fabf --- /dev/null +++ b/.env.development @@ -0,0 +1 @@ +NEXT_PUBLIC_READING_BASE_URI=https://www.oyvindis.com \ No newline at end of file diff --git a/.env.production b/.env.production new file mode 100644 index 0000000..a37fabf --- /dev/null +++ b/.env.production @@ -0,0 +1 @@ +NEXT_PUBLIC_READING_BASE_URI=https://www.oyvindis.com \ No newline at end of file diff --git a/env.ts b/env.ts new file mode 100644 index 0000000..1f66c6c --- /dev/null +++ b/env.ts @@ -0,0 +1,11 @@ +import { validateEnv } from './utils/common'; + +const NAMESPACE = process.env.NAMESPACE! ?? 'dev'; +const READING_BASE_URI = process.env.NEXT_PUBLIC_READING_BASE_URI! ?? 'https://www.oyvindis.com' + +const env = { + NAMESPACE, + READING_BASE_URI +} + +export default validateEnv(env); diff --git a/package-lock.json b/package-lock.json index eb18d6c..9e731fd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -381,6 +381,14 @@ "integrity": "sha512-gd1kmb21kwNuWr6BQz8fv6GNECPBnUasepcoLbekws23NVBLODdsClRZ+bQ8+9Uomf3Sm3+Vwn0oYG9NvwnJCw==", "dev": true }, + "axios": { + "version": "0.26.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz", + "integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==", + "requires": { + "follow-redirects": "^1.14.8" + } + }, "axobject-query": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz", @@ -997,6 +1005,11 @@ "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", "dev": true }, + "follow-redirects": { + "version": "1.14.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz", + "integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==" + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", diff --git a/package.json b/package.json index 2a20b59..3b72e94 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "lint": "next lint" }, "dependencies": { + "axios": "^0.26.1", "next": "12.1.0", "react": "17.0.2", "react-dom": "17.0.2" diff --git a/pages/climate/index.tsx b/pages/climate/index.tsx new file mode 100644 index 0000000..7b6469e --- /dev/null +++ b/pages/climate/index.tsx @@ -0,0 +1,38 @@ +import type { NextPage } from 'next'; +import Link from 'next/link'; +import { useEffect, useState } from 'react'; + +import env from '../../env'; +import { Location } from '../../types'; +import {getLocations} from '../../services/api/climate-reading/readings'; + +interface Props {} + +const Climate: NextPage = () => { + const [locations, setLocations] = useState([]); + const { READING_BASE_URI } = env; + + useEffect(() => { + getLocations().then(locations => setLocations(locations)) + }, []) + + return ( +
+
Climate Page12
+
+ {READING_BASE_URI} +
+
    + {locations?.length > 0 && locations.map(({id, name}) => ( +
  • + + {name} + +
  • + ))} +
+
+ ) +} + +export default Climate; diff --git a/pages/climate/location/[id]/index.tsx b/pages/climate/location/[id]/index.tsx new file mode 100644 index 0000000..e5c92cf --- /dev/null +++ b/pages/climate/location/[id]/index.tsx @@ -0,0 +1,35 @@ +import type { NextPage } from 'next'; +import { useRouter } from 'next/router' +import { useEffect, useState } from 'react'; + +import { Reading } from '../../../../types'; +import { getReadings } from '../../../../services/api/climate-reading/readings'; + +interface Props {} + +const Location: NextPage = () => { + const [readings, setReadings] = useState([]); + + const router = useRouter() + const { id } = router.query; + + useEffect(() => { + getReadings(Array.isArray(id) ? id[0]: id).then(readings => setReadings(readings)) + }, []) + + return ( +
+
    + {readings?.length > 0 && readings.map(({id, temperatureReading, humidityReading}) => ( +
  • +
    + {temperatureReading} - {humidityReading} +
    +
  • + ))} +
+
+ ) +} + +export default Location; diff --git a/services/api/climate-reading/host.ts b/services/api/climate-reading/host.ts new file mode 100644 index 0000000..fedfe06 --- /dev/null +++ b/services/api/climate-reading/host.ts @@ -0,0 +1,27 @@ +import axios from 'axios'; +import env from '../../../env'; + +interface Props { + path: string; + method: any; + data?: any; + params?: URLSearchParams; +} + +const { READING_BASE_URI } = env; + +export const climateReading = ({ path, method, data, params }: Props) => + axios({ + url: `${READING_BASE_URI}${path}`, + method, + data, + params + }) + .then(response => response.data) + .catch(() => null); + +export const climateReadingPost = (path: string, body: any) => + climateReading({ path, method: 'POST', data: body }); + +export const climateReadingGet = (path: string, params?: URLSearchParams) => + climateReading({ path, method: 'GET', params }); diff --git a/services/api/climate-reading/readings.ts b/services/api/climate-reading/readings.ts new file mode 100644 index 0000000..ea16f9b --- /dev/null +++ b/services/api/climate-reading/readings.ts @@ -0,0 +1,7 @@ +import { climateReadingGet } from './host'; + +export const getReadings = (location: string) => + climateReadingGet(`/climate-api/reading/${location}`) + +export const getLocations = () => + climateReadingGet('/climate-api/location') diff --git a/types/common.d.ts b/types/common.d.ts new file mode 100644 index 0000000..93f7b9a --- /dev/null +++ b/types/common.d.ts @@ -0,0 +1,3 @@ +export type Actions any }> = { + [K in keyof T]: ReturnType; +}[keyof T]; diff --git a/types/domain.d.ts b/types/domain.d.ts new file mode 100644 index 0000000..e04b270 --- /dev/null +++ b/types/domain.d.ts @@ -0,0 +1,12 @@ +export interface Location { + id: string; + name: string; +} + +export interface Reading { + id: string; + createdDate: string; + temperatureReading: string; + humidityReading: string; + location: Location; +} diff --git a/types/enums.ts b/types/enums.ts new file mode 100644 index 0000000..0aa0e77 --- /dev/null +++ b/types/enums.ts @@ -0,0 +1,3 @@ +export enum Namespace { + DEVELOPMENT = 'dev' +} diff --git a/types/env.d.ts b/types/env.d.ts new file mode 100644 index 0000000..832809e --- /dev/null +++ b/types/env.d.ts @@ -0,0 +1,4 @@ +export interface EnvironmentVariables { + NAMESPACE: string; + READING_BASE_URI: string; +} diff --git a/types/index.d.ts b/types/index.d.ts new file mode 100644 index 0000000..182bc7c --- /dev/null +++ b/types/index.d.ts @@ -0,0 +1,3 @@ +export * from './env'; +export * from './common'; +export * from './domain'; diff --git a/utils/common/index.ts b/utils/common/index.ts new file mode 100755 index 0000000..452224c --- /dev/null +++ b/utils/common/index.ts @@ -0,0 +1,17 @@ +import type { EnvironmentVariables } from '../../types'; + +function assertIsDefined( + key: string, + value: T +): asserts value is NonNullable { + if (value === undefined || value === null) { + throw new Error(`Expected ${key} to be defined, but received ${value}`); + } +} + +export const validateEnv = ( + env: EnvironmentVariables +): EnvironmentVariables => { + Object.entries(env).forEach(([key, value]) => assertIsDefined(key, value)); + return env; +};