Add delay and spinner for station map, common const and component for delay

This commit is contained in:
Guntis Smaukstelis
2024-01-19 20:32:20 +02:00
parent ad03f43f66
commit 16ed47c661
10 changed files with 54 additions and 47 deletions
+4 -8
View File
@@ -2,9 +2,10 @@ import moment from "moment";
import { Accessor, Component, createResource, createSignal } from "solid-js"; import { Accessor, Component, createResource, createSignal } from "solid-js";
import "../../css/Result.css" import "../../css/Result.css"
import { apiHost } from "../../consts"; import { FETCH_DELAY_MS, apiHost } from "../../consts";
import { isQueryResult } from "../helpers"; import { isQueryResult } from "../helpers";
import { Result } from "./Result"; import { Result } from "./Result";
import { LoadingSpinner } from "../../components/LoadingSpinner";
export const QueryResult: Component<{ export const QueryResult: Component<{
getCities: Accessor<Set<string>>, getCities: Accessor<Set<string>>,
@@ -21,7 +22,7 @@ export const QueryResult: Component<{
const fetchQuery = async (timestamp: number) => { const fetchQuery = async (timestamp: number) => {
if (cities() === "") return new Error("ERROR: Select cities!"); if (cities() === "") return new Error("ERROR: Select cities!");
await new Promise(resolve => setTimeout(resolve, 500)) await new Promise(resolve => setTimeout(resolve, FETCH_DELAY_MS))
const response = await fetch(`${apiHost}/api/query/city/${cities()}/${queryStart()}-${queryEnd()}/${props.getGranularity()}/${props.getField()}/${props.getKey()}`); const response = await fetch(`${apiHost}/api/query/city/${cities()}/${queryStart()}-${queryEnd()}/${props.getGranularity()}/${props.getField()}/${props.getKey()}`);
const json = await response.json(); const json = await response.json();
return json; return json;
@@ -37,12 +38,7 @@ export const QueryResult: Component<{
value="Query Data" value="Query Data"
onClick={() => setTimestamp(Date.now())} onClick={() => setTimestamp(Date.now())}
/> />
{ queryResource.loading && ( { queryResource.loading && <LoadingSpinner text="Loading query" /> }
<div>
<span class="spinner"></span>
<span style={{ "padding-left": "16px" }}>Loading query</span>
</div>
)}
{ queryResource.error && ( { queryResource.error && (
<div>Error while querying: ${queryResource.error}</div> <div>Error while querying: ${queryResource.error}</div>
)} )}
+10
View File
@@ -0,0 +1,10 @@
import { Component } from "solid-js";
export const LoadingSpinner: Component<{ text: string; }> = ({ text }) => {
return (
<div>
<span class="spinner"></span>
<span style={{ "padding-left": "16px" }}>{ text }</span>
</div>
);
}
+2
View File
@@ -13,6 +13,8 @@ export const CityChart: Component<{
const [getCanvas, setCanvas] = createSignal<HTMLCanvasElement>(); const [getCanvas, setCanvas] = createSignal<HTMLCanvasElement>();
const [getIsLarge, setIsLarge] = createSignal(false); const [getIsLarge, setIsLarge] = createSignal(false);
console.log("props.data()", props.data());
let chart: Chart | undefined; let chart: Chart | undefined;
const data: [string, number | null][] = props.data().map(([dateStr, value]) => [ const data: [string, number | null][] = props.data().map(([dateStr, value]) => [
formatDateString(dateStr), formatDateString(dateStr),
+2
View File
@@ -6,6 +6,8 @@ export const apiHost = import.meta.env.MODE === "development"
? import.meta.env.VITE_API_HOST : `${protocol}//${host}:${port}`; ? import.meta.env.VITE_API_HOST : `${protocol}//${host}:${port}`;
// export const apiHost = import.meta.env.VITE_API_HOST; // export const apiHost = import.meta.env.VITE_API_HOST;
export const FETCH_DELAY_MS = 500; // delays by 500ms for server requests, so user sees that something is loading
export const cityList = ["Ainaži", "Alūksne", "Bauska", "Dagda", "Daugavgrīva", "Daugavpils", "Dobele", "Gulbene", "Jelgava", "Kalnciems", "Kolka", "Kuldīga", "Lielpēči", "Liepāja", "Madona", "Mērsrags", "Pāvilosta", "Piedruja", "Priekuļi", "Rēzekne", "Rīga", "Rucava", "Rūjiena", "Saldus", "Sigulda", "Sīļi", "Skrīveri", "Skulte", "Stende", "Ventspils", "Vičaki", "Zīlāni", "Zosēni"]; export const cityList = ["Ainaži", "Alūksne", "Bauska", "Dagda", "Daugavgrīva", "Daugavpils", "Dobele", "Gulbene", "Jelgava", "Kalnciems", "Kolka", "Kuldīga", "Lielpēči", "Liepāja", "Madona", "Mērsrags", "Pāvilosta", "Piedruja", "Priekuļi", "Rēzekne", "Rīga", "Rucava", "Rūjiena", "Saldus", "Sigulda", "Sīļi", "Skrīveri", "Skulte", "Stende", "Ventspils", "Vičaki", "Zīlāni", "Zosēni"];
export const weatherField = ["tempMax", "tempMin", "tempAvg", "precipitation", "windAvg", "windMax", "visibilityMin", "visibilityAvg", "snowAvg", "atmPressire", "dewPoint", "humidity", "sunDuration", "phenomena"]; export const weatherField = ["tempMax", "tempMin", "tempAvg", "precipitation", "windAvg", "windMax", "visibilityMin", "visibilityAvg", "snowAvg", "atmPressire", "dewPoint", "humidity", "sunDuration", "phenomena"];
+4 -8
View File
@@ -1,8 +1,9 @@
import moment from "moment"; import moment from "moment";
import { Accessor, Component, createResource, createSignal } from "solid-js"; import { Accessor, Component, createResource, createSignal } from "solid-js";
import { apiHost } from "../../consts"; import { FETCH_DELAY_MS, apiHost } from "../../consts";
import { Result } from "./Result"; import { Result } from "./Result";
import { LoadingSpinner } from "../../components/LoadingSpinner";
export const QueryResult: Component<{ export const QueryResult: Component<{
@@ -16,7 +17,7 @@ export const QueryResult: Component<{
const fetchQuery = async (timestamp: number) => { const fetchQuery = async (timestamp: number) => {
if (props.getFields().length === 0) return new Error("ERROR: Select weather parameters!"); if (props.getFields().length === 0) return new Error("ERROR: Select weather parameters!");
await new Promise(resolve => setTimeout(resolve, 500)) await new Promise(resolve => setTimeout(resolve, FETCH_DELAY_MS))
const response = await fetch(`${apiHost}/api/query/country/${queryStart()}-${queryEnd()}/${props.getFields().join(",")}`); const response = await fetch(`${apiHost}/api/query/country/${queryStart()}-${queryEnd()}/${props.getFields().join(",")}`);
const json = await response.json(); const json = await response.json();
return json; return json;
@@ -32,12 +33,7 @@ export const QueryResult: Component<{
value="Query Data" value="Query Data"
onClick={() => setTimestamp(Date.now())} onClick={() => setTimestamp(Date.now())}
/> />
{ queryResource.loading && ( { queryResource.loading && <LoadingSpinner text="Loading query" /> }
<div>
<span class="spinner"></span>
<span style={{ "padding-left": "16px" }}>Loading query</span>
</div>
)}
{ queryResource.error && ( { queryResource.error && (
<div>Error while querying: ${queryResource.error}</div> <div>Error while querying: ${queryResource.error}</div>
)} )}
+4 -8
View File
@@ -1,10 +1,11 @@
import moment from "moment"; import moment from "moment";
import { Accessor, Component, createResource, createSignal, Setter } from "solid-js"; import { Accessor, Component, createResource, createSignal, Setter } from "solid-js";
import { apiHost } from "../consts"; import { apiHost, FETCH_DELAY_MS } from "../consts";
import "../css/spinner.css"; import "../css/spinner.css";
import { Calendar } from "./Calendar"; import { Calendar } from "./Calendar";
import { LoadingSpinner } from "../components/LoadingSpinner";
export const DateList: Component<{getDate: Accessor<Date>, setDate: Setter<Date>;}> = (props) => { export const DateList: Component<{getDate: Accessor<Date>, setDate: Setter<Date>;}> = (props) => {
const [getCurrentMonth, setCurrentMonth] = createSignal(new Date()); const [getCurrentMonth, setCurrentMonth] = createSignal(new Date());
@@ -14,7 +15,7 @@ export const DateList: Component<{getDate: Accessor<Date>, setDate: Setter<Date>
moment(getCurrentMonth()).subtract(1, "months").format("yyyyMM"), moment(getCurrentMonth()).subtract(1, "months").format("yyyyMM"),
moment(getCurrentMonth()).add(1, "months").format("yyyyMM"), moment(getCurrentMonth()).add(1, "months").format("yyyyMM"),
]; ];
await new Promise(resolve => setTimeout(resolve, 500)) await new Promise(resolve => setTimeout(resolve, FETCH_DELAY_MS))
const response = await fetch(`${apiHost}/api/show/months/${months.join(",")}`); const response = await fetch(`${apiHost}/api/show/months/${months.join(",")}`);
const json = await response.json(); const json = await response.json();
return json; return json;
@@ -24,12 +25,7 @@ export const DateList: Component<{getDate: Accessor<Date>, setDate: Setter<Date>
return ( return (
<div> <div>
{ datesResource.loading && ( { datesResource.loading && <LoadingSpinner text="Loading calendar..." /> }
<div>
<span class="spinner"></span>
<span style={{ "padding-left": "16px" }}>Loading dates</span>
</div>
)}
{ datesResource.error && ( { datesResource.error && (
<div>Error while loading dates: ${datesResource.error}</div> <div>Error while loading dates: ${datesResource.error}</div>
)} )}
+4 -8
View File
@@ -1,11 +1,12 @@
import { Accessor, Component, createResource, JSXElement } from "solid-js"; import { Accessor, Component, createResource, JSXElement } from "solid-js";
import { apiHost } from "../consts"; import { apiHost, FETCH_DELAY_MS } from "../consts";
import { PrettifyCSV } from "./PrettifyCSV"; import { PrettifyCSV } from "./PrettifyCSV";
import { LoadingSpinner } from "../components/LoadingSpinner";
export const FileContent: Component<{getFileName: Accessor<string>}> = (props) => { export const FileContent: Component<{getFileName: Accessor<string>}> = (props) => {
const fetchFileContent = async (fileName: string) => { const fetchFileContent = async (fileName: string) => {
if (fileName === "") return; if (fileName === "") return;
await new Promise(resolve => setTimeout(resolve, 500)) await new Promise(resolve => setTimeout(resolve, FETCH_DELAY_MS))
const response = await fetch(`${apiHost}/api/show/datetime/${fileName}`); const response = await fetch(`${apiHost}/api/show/datetime/${fileName}`);
const text = await response.json(); const text = await response.json();
return text; return text;
@@ -16,12 +17,7 @@ export const FileContent: Component<{getFileName: Accessor<string>}> = (props) =
return ( return (
<div> <div>
{ contentResource.loading && ( { contentResource.loading && <LoadingSpinner text="Loading date-time weather" /> }
<div>
<span class="spinner"></span>
<span style={{ "padding-left": "16px" }}>load content</span>
</div>
)}
{ contentResource.error && ( { contentResource.error && (
<div>Error while loading file content: ${contentResource.error}</div> <div>Error while loading file content: ${contentResource.error}</div>
)} )}
+4 -8
View File
@@ -1,15 +1,16 @@
import moment from "moment"; import moment from "moment";
import { Accessor, Component, createResource, Setter } from "solid-js"; import { Accessor, Component, createResource, Setter } from "solid-js";
import { apiHost } from "../consts"; import { apiHost, FETCH_DELAY_MS } from "../consts";
import "../css/spinner.css"; import "../css/spinner.css";
import { LoadingSpinner } from "../components/LoadingSpinner";
export const FileNameList: Component<{ export const FileNameList: Component<{
getDate: Accessor<Date>, getDate: Accessor<Date>,
setFileName: Setter<string>, setFileName: Setter<string>,
}> = (props) => { }> = (props) => {
const fetchFileNames = async (fetchDate: Date) => { const fetchFileNames = async (fetchDate: Date) => {
await new Promise(resolve => setTimeout(resolve, 500)) await new Promise(resolve => setTimeout(resolve, FETCH_DELAY_MS))
const response = await fetch(`${apiHost}/api/show/date/${moment(fetchDate).format("YYYYMMDD")}`); const response = await fetch(`${apiHost}/api/show/date/${moment(fetchDate).format("YYYYMMDD")}`);
const json = await response.json(); const json = await response.json();
return json; return json;
@@ -23,12 +24,7 @@ export const FileNameList: Component<{
return ( return (
<div> <div>
{ fileNameResource.loading && ( { fileNameResource.loading && <LoadingSpinner text={"Loading date: "+moment(props.getDate()).format("YYYYMMDD")} /> }
<div>
<span class="spinner"></span>
<span style={{ "padding-left": "16px" }}>loading {moment(props.getDate()).format("YYYYMMDD")}</span>
</div>
)}
{ fileNameResource.error && ( { fileNameResource.error && (
<div>Error while loading file names: ${fileNameResource.error}</div> <div>Error while loading file names: ${fileNameResource.error}</div>
)} )}
+15 -2
View File
@@ -4,7 +4,8 @@ import { coordToCity } from "./coordToCity";
import { cityCoords } from "./cityCoords"; import { cityCoords } from "./cityCoords";
import mapUrl from "../assets/map_1000x570.webp"; import mapUrl from "../assets/map_1000x570.webp";
import moment from "moment"; import moment from "moment";
import { apiHost } from "../consts"; import { FETCH_DELAY_MS, apiHost } from "../consts";
import { LoadingSpinner } from "../components/LoadingSpinner";
export const MapResult: Component<{ export const MapResult: Component<{
setCity: Setter<string | undefined>, setCity: Setter<string | undefined>,
@@ -49,6 +50,8 @@ export const MapResult: Component<{
const [meteoValues] = createResource(citiesFieldDate, async ([cities, field, start, end]: [Set<string>, string, Date, Date]) => { const [meteoValues] = createResource(citiesFieldDate, async ([cities, field, start, end]: [Set<string>, string, Date, Date]) => {
if (cities.size === 0) return; if (cities.size === 0) return;
await new Promise(resolve => setTimeout(resolve, FETCH_DELAY_MS));
const queryStart = moment(start).format("YYYYMMDD_HHmm"); const queryStart = moment(start).format("YYYYMMDD_HHmm");
const queryEnd = moment(end).format("YYYYMMDD_HHmm"); const queryEnd = moment(end).format("YYYYMMDD_HHmm");
let aggregate: string = "avg"; let aggregate: string = "avg";
@@ -74,7 +77,17 @@ export const MapResult: Component<{
); );
}); });
return <canvas ref={setCanvas} width={1000} height={570} />; return (
<div>
{ meteoValues.loading && <LoadingSpinner text="Drawing map..." /> }
<canvas
ref={setCanvas}
style={{ display: meteoValues.loading ? "none" : "block" }}
width={1000}
height={570}
/>
</div>
);
} }
function drawOnMap( function drawOnMap(
+5 -5
View File
@@ -1,5 +1,5 @@
import { Accessor, Component, createResource } from "solid-js"; import { Accessor, Component, createResource } from "solid-js";
import { apiHost } from "../consts"; import { FETCH_DELAY_MS, apiHost } from "../consts";
import moment from "moment"; import moment from "moment";
import { CityChart } from "../components/chart/CityChart"; import { CityChart } from "../components/chart/CityChart";
@@ -14,7 +14,7 @@ export const Result: Component<{
const fetchList = async ([city, field, getStart, getEnd]: [string | undefined, string, Date, Date]) => { const fetchList = async ([city, field, getStart, getEnd]: [string | undefined, string, Date, Date]) => {
if (city === undefined) return undefined; if (city === undefined) return undefined;
await new Promise(resolve => setTimeout(resolve, 500)); await new Promise(resolve => setTimeout(resolve, FETCH_DELAY_MS));
const response = await fetch(`${apiHost}/api/query/city/${city}/${queryStart()}-${queryEnd()}/hour/${field}/list`); const response = await fetch(`${apiHost}/api/query/city/${city}/${queryStart()}-${queryEnd()}/hour/${field}/list`);
const json = await response.json(); const json = await response.json();
return json; return json;
@@ -22,7 +22,7 @@ export const Result: Component<{
const fetchMeteo = async ([city, getStart, getEnd]: [string | undefined, Date, Date]) => { const fetchMeteo = async ([city, getStart, getEnd]: [string | undefined, Date, Date]) => {
if (city === undefined) return undefined; if (city === undefined) return undefined;
await new Promise(resolve => setTimeout(resolve, 500)); await new Promise(resolve => setTimeout(resolve, FETCH_DELAY_MS));
const response = await fetch(`${apiHost}/api/query/city/${city}/${queryStart()}-${queryEnd()}/allFields`); const response = await fetch(`${apiHost}/api/query/city/${city}/${queryStart()}-${queryEnd()}/allFields`);
const json = await response.json(); const json = await response.json();
return json; return json;
@@ -63,10 +63,10 @@ export const Result: Component<{
} }
{ listResource() && props.getCity() && !listResource.loading && { listResource() && props.getCity() && !listResource.loading &&
<div> <div>
<h3>{ props.getCity() }</h3> <h3>{ props.getCity() }: { props.getField() }</h3>
<CityChart <CityChart
city={() => listResource().query.cities[0]} city={() => listResource().query.cities[0]}
data={() => listResource().result[listResource().query.cities[0]]} data={() => listResource().result[listResource().query.cities[0]] ?? []}
query={() => listResource().query} query={() => listResource().query}
/> />
</div> </div>