Setting up harmonie page in frontend
This commit is contained in:
@@ -55,7 +55,8 @@ class FetchService(log: Logger[IO]) {
|
|||||||
for {
|
for {
|
||||||
base <- baseUrl
|
base <- baseUrl
|
||||||
queryParams = Query.fromPairs(
|
queryParams = Query.fromPairs(
|
||||||
"parameter-name" -> "temperature-2m",
|
// https://opendatadocs.dmi.govcloud.dk/Data/Forecast_Data_Weather_Model_HARMONIE_DINI_EDR
|
||||||
|
"parameter-name" -> "temperature-2m,total-precipitation,precipitation-type,wind-speed,gust-wind-speed-10m,wind-10m-u,wind-10m-v",
|
||||||
"datetime" -> time.toString,
|
"datetime" -> time.toString,
|
||||||
"api-key" -> harmonieServerConfig.api_key,
|
"api-key" -> harmonieServerConfig.api_key,
|
||||||
)
|
)
|
||||||
|
|||||||
Vendored
+5
-1
@@ -1,3 +1,7 @@
|
|||||||
{
|
{
|
||||||
"typescript.tsdk": "node_modules/typescript/lib"
|
"typescript.tsdk": "node_modules/typescript/lib",
|
||||||
|
"javascript.preferences.quoteStyle": "single",
|
||||||
|
"typescript.preferences.quoteStyle": "single",
|
||||||
|
"javascript.format.semicolons": "remove",
|
||||||
|
"typescript.format.semicolons": "remove"
|
||||||
}
|
}
|
||||||
Generated
+16
@@ -8,6 +8,7 @@
|
|||||||
"name": "weather-tool-web",
|
"name": "weather-tool-web",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@solidjs/router": "^0.15.2",
|
||||||
"chart.js": "^4.4.1",
|
"chart.js": "^4.4.1",
|
||||||
"moment": "^2.30.1",
|
"moment": "^2.30.1",
|
||||||
"solid-js": "^1.9.4"
|
"solid-js": "^1.9.4"
|
||||||
@@ -1096,6 +1097,15 @@
|
|||||||
"win32"
|
"win32"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"node_modules/@solidjs/router": {
|
||||||
|
"version": "0.15.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@solidjs/router/-/router-0.15.3.tgz",
|
||||||
|
"integrity": "sha512-iEbW8UKok2Oio7o6Y4VTzLj+KFCmQPGEpm1fS3xixwFBdclFVBvaQVeibl1jys4cujfAK5Kn6+uG2uBm3lxOMw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"solid-js": "^1.8.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/babel__core": {
|
"node_modules/@types/babel__core": {
|
||||||
"version": "7.20.5",
|
"version": "7.20.5",
|
||||||
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
|
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
|
||||||
@@ -3271,6 +3281,12 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
|
"@solidjs/router": {
|
||||||
|
"version": "0.15.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@solidjs/router/-/router-0.15.3.tgz",
|
||||||
|
"integrity": "sha512-iEbW8UKok2Oio7o6Y4VTzLj+KFCmQPGEpm1fS3xixwFBdclFVBvaQVeibl1jys4cujfAK5Kn6+uG2uBm3lxOMw==",
|
||||||
|
"requires": {}
|
||||||
|
},
|
||||||
"@types/babel__core": {
|
"@types/babel__core": {
|
||||||
"version": "7.20.5",
|
"version": "7.20.5",
|
||||||
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
|
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
|
||||||
|
|||||||
+2
-1
@@ -19,6 +19,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chart.js": "^4.4.1",
|
"chart.js": "^4.4.1",
|
||||||
"moment": "^2.30.1",
|
"moment": "^2.30.1",
|
||||||
"solid-js": "^1.9.4"
|
"solid-js": "^1.9.4",
|
||||||
|
"@solidjs/router": "^0.15.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-25
@@ -1,4 +1,5 @@
|
|||||||
import { Component, JSXElement, createSignal } from 'solid-js';
|
import { Component, JSXElement, createSignal } from 'solid-js'
|
||||||
|
import { Route, Router } from '@solidjs/router'
|
||||||
|
|
||||||
import styles from './css/menu.module.css';
|
import styles from './css/menu.module.css';
|
||||||
import { Country } from './country/Country';
|
import { Country } from './country/Country';
|
||||||
@@ -7,46 +8,35 @@ import { Database } from './database/Database';
|
|||||||
import { Station } from './station/Station';
|
import { Station } from './station/Station';
|
||||||
import { apiHost } from './consts';
|
import { apiHost } from './consts';
|
||||||
import { WeatherIconStub } from './components/weatherIcons/WeatherIconStub';
|
import { WeatherIconStub } from './components/weatherIcons/WeatherIconStub';
|
||||||
|
import { Harmonie } from './harmonie/Harmonie'
|
||||||
|
|
||||||
console.log("env:", import.meta.env.MODE);
|
console.log("env:", import.meta.env.MODE);
|
||||||
console.log("api host:", apiHost);
|
console.log("api host:", apiHost);
|
||||||
|
|
||||||
const App: Component = () => {
|
const App: Component = () => {
|
||||||
const pages: [string, () => JSXElement][] = [
|
const pages = ['station', 'cities', 'latvia', 'database', 'harmonie']
|
||||||
["station", () => <Station />],
|
|
||||||
["cities", () => <Cities />],
|
|
||||||
["latvia", () => <Country />],
|
|
||||||
["database", () => <Database />],
|
|
||||||
];
|
|
||||||
const [getPageTitle, setPageTitle] = createSignal("station");
|
|
||||||
|
|
||||||
const getPageContent = () => {
|
|
||||||
const page = pages.find(p => p[0] === getPageTitle());
|
|
||||||
return page ? page[1]() : <div>Page not found!</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
const getActiveCSS = (pageTitle: string) => {
|
|
||||||
return getPageTitle() === pageTitle ? styles.active : "";
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div class="grid-1-1">
|
<div class="grid-1-1">
|
||||||
<div><WeatherIconStub /></div>
|
<div><WeatherIconStub /></div>
|
||||||
<div>
|
<div>
|
||||||
<ul class={styles.menu}>
|
<ul class={styles.menu}>
|
||||||
{ pages.map(([pageTitle]) =>
|
{ pages.map(page =>
|
||||||
<li
|
<li>
|
||||||
class={getActiveCSS(pageTitle)}
|
<a href={page}>{page}</a>
|
||||||
onClick={() => setPageTitle(pageTitle)}
|
|
||||||
>
|
|
||||||
{pageTitle}
|
|
||||||
</li>
|
</li>
|
||||||
)}
|
)}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{ getPageContent() }
|
<Router>
|
||||||
|
<Route path="/" component={Harmonie} />
|
||||||
|
<Route path="/harmonie" component={Harmonie} />
|
||||||
|
<Route path="/station" component={Station} />
|
||||||
|
<Route path="/cities" component={Cities} />
|
||||||
|
<Route path="/latvia" component={Country} />
|
||||||
|
<Route path="/database" component={Database} />
|
||||||
|
</Router>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import { Accessor, Component, createSignal } from 'solid-js'
|
||||||
|
|
||||||
|
import styles from './harmonie.module.css'
|
||||||
|
import { apiHost } from '../consts'
|
||||||
|
|
||||||
|
export const GribFile: Component<{
|
||||||
|
name: string,
|
||||||
|
isActive: Accessor<boolean>,
|
||||||
|
onClick: (name: string) => void,
|
||||||
|
}> = ({ name, isActive, onClick }) => {
|
||||||
|
const [getStructure, setStructure] = createSignal<any[]>([])
|
||||||
|
|
||||||
|
function onFileClick() {
|
||||||
|
onClick(name)
|
||||||
|
|
||||||
|
if (getStructure().length === 0) {
|
||||||
|
fetch(`${apiHost}/api/show/grib/${name}`)
|
||||||
|
.then(re => re.json())
|
||||||
|
.then(setStructure)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return <li
|
||||||
|
class={isActive() ? styles.active : ''}
|
||||||
|
onClick={onFileClick}
|
||||||
|
>
|
||||||
|
<div class={styles.name}>{ name }</div>
|
||||||
|
<ul class={styles.meteoParams}>
|
||||||
|
{ getStructure().map(grib =>
|
||||||
|
<li>{ grib.title }</li>
|
||||||
|
)}
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import { Component, createSignal } from 'solid-js'
|
||||||
|
import { apiHost } from '../consts'
|
||||||
|
|
||||||
|
import styles from './harmonie.module.css'
|
||||||
|
import { GribFile } from './GribFile'
|
||||||
|
|
||||||
|
export const Harmonie: Component<{}> = () => {
|
||||||
|
const [getFileList, setFileList] = createSignal<string[]>([])
|
||||||
|
const [getActiveGrib, setActiveGrib] = createSignal('')
|
||||||
|
|
||||||
|
fetch(`${apiHost}/api/show/grib-list`)
|
||||||
|
.then(re => re.json())
|
||||||
|
.then(fileList => {
|
||||||
|
fileList.sort((a: string, b: string) => a < b ? 1 : -1)
|
||||||
|
setFileList(fileList)
|
||||||
|
})
|
||||||
|
|
||||||
|
return <div>
|
||||||
|
Harmonie
|
||||||
|
<ul class={styles.fileList}>
|
||||||
|
{getFileList().map(fileName =>
|
||||||
|
<GribFile
|
||||||
|
name={fileName}
|
||||||
|
isActive={() => getActiveGrib() === fileName}
|
||||||
|
onClick={() => setActiveGrib(fileName)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
.fileList li {
|
||||||
|
padding: 5px 0;
|
||||||
|
}
|
||||||
|
.fileList > li:nth-child(odd) {
|
||||||
|
background-color: #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fileList .active .name {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fileList .meteoParams {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fileList li.active .meteoParams {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user