Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions .github/workflows/docker-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@ jobs:
username: ${{ secrets.pcicdevops_at_dockerhub_username }}
password: ${{ secrets.pcicdevops_at_dockerhub_password }}

- name: Build and Publish Demo App
- name: Build and Publish App
uses: docker/build-push-action@v4
with:
context: .
file: docker/app/Dockerfile
push: true
tags: |
pcic/bbox-demo-app:${{ github.ref_name }}
${{ github.ref_name == 'main' && 'pcic/bbox-demo-app:latest' || '' }}
pcic/vhmo-app:${{ github.ref_name }}
${{ github.ref_name == 'main' && 'pcic/vhmo-app:latest' || '' }}

- name: Build and Publish Data Import
uses: docker/build-push-action@v4
Expand All @@ -35,8 +35,8 @@ jobs:
file: docker/data-import/Dockerfile
push: true
tags: |
pcic/bbox-demo-data-import:${{ github.ref_name }}
${{ github.ref_name == 'main' && 'pcic/bbox-demo-data-import:latest' || '' }}
pcic/vhmo-data-import:${{ github.ref_name }}
${{ github.ref_name == 'main' && 'pcic/vhmo-data-import:latest' || '' }}

- name: Build and Publish PostGIS
uses: docker/build-push-action@v4
Expand All @@ -45,8 +45,8 @@ jobs:
file: docker/PostGIS/Dockerfile
push: true
tags: |
pcic/bbox-demo-postgis:${{ github.ref_name }}
${{ github.ref_name == 'main' && 'pcic/bbox-demo-postgis:latest' || '' }}
pcic/vhmo-postgis:${{ github.ref_name }}
${{ github.ref_name == 'main' && 'pcic/vhmo-postgis:latest' || '' }}

- name: Build and Publish BBOX server
uses: docker/build-push-action@v4
Expand All @@ -55,5 +55,5 @@ jobs:
file: docker/BBOX/Dockerfile
push: true
tags: |
pcic/bbox-demo-server:${{ github.ref_name }}
${{ github.ref_name == 'main' && 'pcic/bbox-demo-server:latest' || '' }}
pcic/vhmo-server:${{ github.ref_name }}
${{ github.ref_name == 'main' && 'pcic/vhmo-server:latest' || '' }}
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Demo App for BCSRIF-P2
# Vector Hydrologic Model Output Portal (VHMO) — BCSRIF-P2

This project provides an interactive web portal for visualizing and downloading hydrological model data, including water temperature, streamflow, and oxygen concentration timeseries.

Expand Down Expand Up @@ -101,10 +101,10 @@ BC Geographic Warehouse (BCGW) [Public Map Server](https://delivery.maps.gov.bc.

The following images are published to Docker Hub:

- `pcic/bbox-demo-app`: Frontend application
- `pcic/bbox-demo-data-import`: Data import utilities
- `pcic/bbox-demo-postgis`: PostGIS database
- `pcic/bbox-demo-server`: BBOX tile server
- `pcic/vhmo-app`: Frontend application
- `pcic/vhmo-data-import`: Data import utilities
- `pcic/vhmo-postgis`: PostGIS database
- `pcic/vhmo-server`: BBOX tile server

## Requirements

Expand Down
6 changes: 4 additions & 2 deletions app/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@

<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="https://services.pacificclimate.org/plan2adapt/app/favicon.ico" />
<link rel="preconnect" href="https://swarm.pacificclimate.org" crossorigin />
<link rel="dns-prefetch" href="//delivery.maps.gov.bc.ca" />
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
<link rel="preload" as="image" href="https://swarm.pacificclimate.org/tiles/bc-albers-lite/6/33/29.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vector Hydrologic Model Output</title>
Expand All @@ -14,4 +16,4 @@
<script type="module" src="/src/main.jsx"></script>
</body>

</html>
</html>
Binary file added app/public/favicon.ico
Binary file not shown.
9 changes: 1 addition & 8 deletions app/src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,5 @@
import React from 'react';
import MapComponent from "./components/map/MapComponent.jsx";

console.log("Rendering App...");
const App = () => {
console.log("Rendering MapComponent from App...");
return <MapComponent />;
};

console.log("Rendering MapComponent...");
const App = () => <MapComponent />;

export default App;
Binary file removed app/src/assets/logo.png
Binary file not shown.
Binary file added app/src/assets/logo.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
70 changes: 55 additions & 15 deletions app/src/components/data/DataSelectionTable.jsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,20 @@
import { useState, useEffect, useCallback } from "react";
import PropTypes from "prop-types";
import { getAvailableOptions, downloadTimeseries } from "../../services/timeseriesApi.js";
import {
getAvailableOptions,
downloadTimeseries,
} from "../../services/timeseriesApi.js";
import "./DataSelection.css";

const HISTORICAL_SCENARIO = "historical";
const HISTORICAL_MODEL = "PNWNAmet";

const getModelLabel = (model) =>
model === HISTORICAL_MODEL ? `${model} (historical)` : model;

const getScenarioLabel = (scenario) =>
scenario === HISTORICAL_SCENARIO ? `${scenario} (PNWNAmet only)` : scenario;

const DataSelectionTable = ({ featureId, onClose }) => {
const outletId = `${featureId}`;
const [options, setOptions] = useState({
Expand All @@ -15,35 +27,46 @@ const DataSelectionTable = ({ featureId, onClose }) => {
scenario: "",
variable: "",
});
const [isFetching, setIsFetching] = useState(true);
const [isLoading, setIsLoading] = useState(false);
const [showValidation, setShowValidation] = useState(false);
const [shake, setShake] = useState(false);

useEffect(() => {
const fetchOptions = async () => {
try {
try {
const availableOptions = await getAvailableOptions(outletId);
setOptions(availableOptions);
} catch (error) {
console.error("Failed to fetch options:", error);
alert("Failed to load available options");
} finally {
setIsFetching(false);
}
};

fetchOptions();
}, [outletId]);

const handleChange = (field, value) => {
setSelections((prev) => ({
...prev,
[field]: value,
}));
};
setSelections((prev) => {
const isNowInvalid =
field === "model" &&
prev.scenario !== "" &&
(value === HISTORICAL_MODEL) !==
(prev.scenario === HISTORICAL_SCENARIO);

const getValidationClass = (field) => {
return showValidation && !selections[field] ? "invalid" : "";
return {
...prev,
[field]: value,
...(isNowInvalid ? { scenario: "" } : {}),
};
});
};

const getValidationClass = (field) =>
showValidation && !selections[field] ? "invalid" : "";

const handleDownload = useCallback(async () => {
if (Object.values(selections).some((v) => !v)) {
setShowValidation(true);
Expand Down Expand Up @@ -77,11 +100,12 @@ const DataSelectionTable = ({ featureId, onClose }) => {
value={selections.model}
onChange={(e) => handleChange("model", e.target.value)}
className={getValidationClass("model")}
disabled={isFetching}
>
<option value="">Select Model</option>
<option value="">{isFetching ? "Loading..." : "Select Model"}</option>
{options.models.map((model) => (
<option key={model} value={model}>
{model}
{getModelLabel(model)}
</option>
))}
</select>
Expand All @@ -90,11 +114,24 @@ const DataSelectionTable = ({ featureId, onClose }) => {
value={selections.scenario}
onChange={(e) => handleChange("scenario", e.target.value)}
className={getValidationClass("scenario")}
disabled={isFetching}
>
<option value="">Select Scenario</option>
<option value="">
{isFetching ? "Loading..." : "Select Scenario"}
</option>
{options.scenarios.map((scenario) => (
<option key={scenario} value={scenario}>
{scenario}
<option
key={scenario}
value={scenario}
disabled={
(scenario === HISTORICAL_SCENARIO &&
selections.model !== "" &&
selections.model !== HISTORICAL_MODEL) ||
(scenario !== HISTORICAL_SCENARIO &&
selections.model === HISTORICAL_MODEL)
}
>
{getScenarioLabel(scenario)}
</option>
))}
</select>
Expand All @@ -103,8 +140,11 @@ const DataSelectionTable = ({ featureId, onClose }) => {
value={selections.variable}
onChange={(e) => handleChange("variable", e.target.value)}
className={getValidationClass("variable")}
disabled={isFetching}
>
<option value="">Select Variable</option>
<option value="">
{isFetching ? "Loading..." : "Select Variable"}
</option>
{options.variables.map((variable) => (
<option key={variable} value={variable}>
{variable}
Expand Down
Loading
Loading