diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..4e73393
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,9 @@
+.git
+*/.git
+node_modules
+*/node_modules
+npm-debug.log
+dist
+.swc
+.env
+!.env/.env
\ No newline at end of file
diff --git a/.env/.env b/.env/.env
index 8c530a5..51ef250 100644
--- a/.env/.env
+++ b/.env/.env
@@ -1 +1,20 @@
-VITE_UNITY_UI_VERSION=${npm_package_version}
\ No newline at end of file
+# GENERAL
+VITE_UNITY_UI_VERSION=${npm_package_version}
+VITE_ADMIN_EMAIL=ENV_UNITY_UI_ADMIN_EMAIL
+
+# Auth
+VITE_AUTH_OAUTH_CLIENT_ID=ENV_UNITY_UI_AUTH_OAUTH_CLIENT_ID
+VITE_AUTH_OAUTH_REDIRECT_URI=ENV_UNITY_UI_AUTH_OAUTH_REDIRECT_URI
+VITE_AUTH_OAUTH_LOGOUT_ENDPOINT=ENV_UNITY_UI_AUTH_OAUTH_LOGOUT_ENDPOINT
+VITE_AUTH_OAUTH_PROVIDER_URL=ENV_UNITY_UI_AUTH_OAUTH_PROVIDER_URL
+VITE_AUTH_APP_ADMIN_GROUP_NAME=ENV_UNITY_UI_AUTH_APP_ADMIN_GROUP_NAME
+VITE_AUTH_APP_APP_VIEWER_GROUP_NAME=ENV_UNITY_UI_AUTH_APP_APP_VIEWER_GROUP_NAME
+
+# ADS
+VITE_ADS_URL=ENV_UNITY_UI_ADS_URL
+
+# DS
+VITE_STAC_BROWSER_URL=ENV_UNITY_UI_STAC_BROWSER_URL
+
+# SPS
+VITE_SPS_WPST_ENDPOINT=ENV_UNITY_UI_SPS_WPST_ENDPOINT
\ No newline at end of file
diff --git a/.env/.env.development b/.env/.env.development
deleted file mode 100644
index 8973bba..0000000
--- a/.env/.env.development
+++ /dev/null
@@ -1,18 +0,0 @@
-# GENERAL
-VITE_ADMIN_EMAIL=anil.natha@jpl.nasa.gov
-
-# Auth
-VITE_AUTH_OAUTH_CLIENT_ID=SET_IN_DEVELOPMENT_DOT_LOCAL_FILE
-VITE_AUTH_OAUTH_REDIRECT_URI=http://localhost:8080
-VITE_AUTH_OAUTH_LOGOUT_ENDPOINT=https://unitysds.auth.us-west-2.amazoncognito.com/logout
-VITE_AUTH_OAUTH_PROVIDER_URL=https://unitysds.auth.us-west-2.amazoncognito.com/oauth2
-VITE_AUTH_APP_ADMIN_GROUP_NAME=Unity_Admin
-VITE_AUTH_APP_APP_VIEWER_GROUP_NAME=Unity_Viewer
-
-# ADS
-VITE_ADS_URL=http://uads-test-dockstore-deploy-lb-1762603872.us-west-2.elb.amazonaws.com:9998
-
-# SPS
-#VITE_SPS_WPST_ENDPOINT=http://aaed06716800f4ead8e75815506a2999-2059110582.us-west-2.elb.amazonaws.com:5001/ # Direct
-#VITE_SPS_WPST_ENDPOINT=https://1gp9st60gd.execute-api.us-west-2.amazonaws.com/dev/ades-wpst/ # Via API GATEWAY
-VITE_SPS_WPST_ENDPOINT=https://d3vc8w9zcq658.cloudfront.net/ades-wpst/ # Via CloudFront
diff --git a/.env/.env.docker.example b/.env/.env.docker.example
new file mode 100644
index 0000000..202c636
--- /dev/null
+++ b/.env/.env.docker.example
@@ -0,0 +1,19 @@
+# GENERAL
+ENV_UNITY_UI_ADMIN_EMAIL=REPLACE_WITH_ADMIN_EMAIL
+
+# Auth
+ENV_UNITY_UI_AUTH_OAUTH_CLIENT_ID=REPLACE_WITH_COGNITO_USER_POOL_CLIENT_ID
+ENV_UNITY_UI_AUTH_OAUTH_REDIRECT_URI=REPLACE_WITH_URL_OF_APPLICATION_WITH_PORT_IF_NEEDED
+ENV_UNITY_UI_AUTH_OAUTH_LOGOUT_ENDPOINT=REPLACE_WITH_COGNITO_DOMAIN/logout
+ENV_UNITY_UI_AUTH_OAUTH_PROVIDER_URL=REPLACE_WITH_COGNITO_DOMAIN/oauth2
+ENV_UNITY_UI_AUTH_APP_ADMIN_GROUP_NAME=REPLACE_WITH_USER_POOL_ADMIN_GROUP_NAME
+ENV_UNITY_UI_AUTH_APP_APP_VIEWER_GROUP_NAME=REPLACE_WITH_USER_PROOL_VIEWER_GROUP_NAME
+
+# ADS
+ENV_UNITY_UI_ADS_URL=REPLACE_WITH_ADS_URL
+
+# DS
+VITE_STAC_BROWSER_URL=REPLACE_WITH_STAC_BROWSER_URL
+
+# SPS
+ENV_UNITY_UI_SPS_WPST_ENDPOINT=REPLACE_WITH_ADES_WPST_URL
diff --git a/.env/.env.integration b/.env/.env.integration
deleted file mode 100644
index 54c7659..0000000
--- a/.env/.env.integration
+++ /dev/null
@@ -1,18 +0,0 @@
-# GENERAL
-VITE_ADMIN_EMAIL=anil.natha@jpl.nasa.gov
-
-# Auth
-VITE_AUTH_OAUTH_CLIENT_ID=SET_IN_INTEGRATION_DOT_LOCAL_FILE
-VITE_AUTH_OAUTH_REDIRECT_URI=https://d3vc8w9zcq658.cloudfront.net/dashboard
-VITE_AUTH_OAUTH_LOGOUT_ENDPOINT=https://unitysds.auth.us-west-2.amazoncognito.com/logout
-VITE_AUTH_OAUTH_PROVIDER_URL=https://unitysds.auth.us-west-2.amazoncognito.com/oauth2
-VITE_AUTH_APP_ADMIN_GROUP_NAME=Unity_Admin
-VITE_AUTH_APP_APP_VIEWER_GROUP_NAME=Unity_Viewer
-
-# ADS
-VITE_ADS_URL=http://uads-test-dockstore-deploy-lb-1762603872.us-west-2.elb.amazonaws.com:9998
-
-# SPS
-#VITE_SPS_WPST_ENDPOINT=http://aaed06716800f4ead8e75815506a2999-2059110582.us-west-2.elb.amazonaws.com:5001/ # Direct
-#VITE_SPS_WPST_ENDPOINT=https://1gp9st60gd.execute-api.us-west-2.amazonaws.com/dev/ades-wpst/ # Via API GATEWAY
-VITE_SPS_WPST_ENDPOINT=https://d3vc8w9zcq658.cloudfront.net/ades-wpst/ # Via CloudFront
\ No newline at end of file
diff --git a/.env/.env.production b/.env/.env.production
deleted file mode 100644
index e69de29..0000000
diff --git a/.env/.env.staging b/.env/.env.staging
deleted file mode 100644
index e69de29..0000000
diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml
new file mode 100644
index 0000000..e8c9aa0
--- /dev/null
+++ b/.github/workflows/docker-build.yml
@@ -0,0 +1,30 @@
+name: Docker Build and Push
+
+on:
+ push:
+ branches:
+ - main # or any other branch you want to trigger the workflow
+ - "features/*"
+
+jobs:
+ build-and-push:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout Repository
+ uses: actions/checkout@v4
+
+ - name: Log in to Docker Registry
+ uses: docker/login-action@v3
+ with:
+ registry: ghcr.io # replace with your registry if different
+ username: ${{ github.actor }}
+ password: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Build and Push "Latest" Docker image
+ id: build-and-push-latest
+ uses: docker/build-push-action@v5
+ with:
+ context: .
+ file: ./Dockerfile
+ push: true
+ tags: ghcr.io/${{ github.repository }}/unity-ui-application:latest # replace with your image name and tag
diff --git a/.gitignore b/.gitignore
index 7a01d61..4a315ee 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,6 +10,9 @@ yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
+.env
+!.env/.env
+!.env/.env.docker.example
node_modules
dist
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 747976c..4cb2746 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,25 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [0.5.0] 2024-04-15
+- Updated Navbar CSS styling to match Figma designs [#5](https://github.com/unity-sds/unity-ui/issues/5)
+- Added CI/CD workflow to build application as a docker image. [#21](https://github.com/unity-sds/unity-ui/issues/21)
+- Updated application build configuration. Stateful information has been removed from project configuration. Instead we now allow environment variables to be supplied to the container at startup which in turn get injected into the Unity UI Codebase. This is in support of moving to dynamic configuration of the application via Unity Marketplace. Related to [#3](https://github.com/unity-sds/unity-sds-portal/issues/3)
+
+## [0.4.0] - 2023-12-13
+- Updated /job/new endpoint so it lists the applications for which jobs may be executed.
+- Moved chirp rebinning job form to /jobs/new/chirp
+- Added job submission forms for L1A and L1B PGEs
+- Added process utility to help facilitate changes listed above.
+
+## [0.3.1] - 2023-12-12
+- Fixed link associated with logo on mobile platforms
+
+## [0.3.0] - 2023-11-13
+
+- Updated environment configurations to account for SIPS-TEST deployments
+- Updated scrolling behavior for all UIs so they can be scrolled independently from the navbar. And in the case of the Job Monitoring UI, the detail view scrolls independently from the main view.
+
## [0.2.0] - 2023-10-02
- Updated Job Monitoring Dashboard, data is fetched from WPS-T Endpoint
@@ -29,4 +48,4 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Integration of React Stellar Navbar component [#73](https://github.com/unity-sds/unity-project-management/issues/73)
- Developed Jobs Monitoring Dashboard — currently loads static data - [#1](https://github.com/unity-sds/unity-jobs-ui/issues/1), [#4],(https://github.com/unity-sds/unity-jobs-ui/issues/4)[#12], (https://github.com/unity-sds/unity-jobs-ui/issues/12)
- Jobs Detail view [#72](https://github.com/unity-sds/unity-jobs-ui/issues/18)
-- Integration of generic web view for loading third-party user interfaces
\ No newline at end of file
+- Integration of generic web view for loading third-party user interfaces
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..c2c60f6
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,56 @@
+############################################################
+############################################################
+# Code Building Stage
+
+FROM node:lts-hydrogen as builder
+
+RUN apt-get update
+
+# Create app directory
+WORKDIR /usr/src/app
+
+# Copy source to workdir
+COPY . .
+
+# Install dependencies
+RUN npm clean-install
+
+# Build distribution
+RUN npm run build
+
+############################################################
+############################################################
+# Build Image Stage
+
+FROM ubuntu:18.04
+LABEL version="0.1.0"
+
+ENV UNITY_WWW_ROOT=/var/www/unity-ui
+ENV ENTRYPOINT_FOLDER=/entrypoint.d
+
+RUN apt-get update \
+ && apt-get install -y apache2 \
+ && rm -rf /var/lib/apt/lists/* \
+ && apt-get clean
+
+# Create app directory and copy distribution code from builder stage
+WORKDIR ${UNITY_WWW_ROOT}
+COPY --from=builder /usr/src/app/dist ./
+
+# Configure apache2
+COPY ./etc/apache2/sites-available/unity-ui.conf /etc/apache2/sites-available/
+RUN echo "ServerName localhost" >> /etc/apache2/apache2.conf
+RUN a2dissite 000-default.conf
+RUN a2ensite unity-ui.conf
+
+# Copy and set up files needed for container startup
+COPY ./entrypoint.d/* ${ENTRYPOINT_FOLDER}/
+RUN chmod 777 -R ${ENTRYPOINT_FOLDER}/* && chmod +x -R ${ENTRYPOINT_FOLDER}/*
+
+EXPOSE 80
+
+# Default process to run on container startup
+ENTRYPOINT ["/entrypoint.d/docker-entrypoint.sh"]
+
+# Default options to pass to apache when starting container in docker-entrypoint.sh
+CMD ["-k", "start"]
\ No newline at end of file
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..58742f5
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,44 @@
+export IMAGE_NAME = unity-ui
+export CONTAINER_NAME = unity-ui
+export RUN_OPTIONS =
+
+build-no-cache: RUN_OPTIONS = "--no-cache"
+build-no-cache: build
+
+build:
+ docker buildx build --progress=plain $(RUN_OPTIONS) -t ${IMAGE_NAME} -f Dockerfile .
+
+destroy-container:
+ docker container rm ${CONTAINER_NAME}
+
+destroy-image:
+ docker image rm ${IMAGE_NAME}
+
+kill:
+ docker kill ${CONTAINER_NAME}
+
+run:
+ docker run --env-file=./.env/.env.docker -t -i --rm -p 8080:80 --name ${CONTAINER_NAME} ${IMAGE_NAME}
+
+start:
+ docker start ${CONTAINER_NAME}
+
+stop:
+ docker stop ${CONTAINER_NAME}
+
+# ----------------------------------------------------------------------------
+# Self-Documented Makefile
+# ref: http://marmelab.com/blog/2016/02/29/auto-documented-makefile.html
+# ----------------------------------------------------------------------------
+help: ## (DEFAULT) This help information
+ @echo ====================================================================
+ @grep -E '^## .*$$' \
+ $(MAKEFILE_LIST) \
+ | awk 'BEGIN { FS="## " }; {printf "\033[33m%-24s\033[0m \n", $$2}'
+ @echo
+ @grep -E '^[0-9a-zA-Z_-]+:.*?## .*$$' \
+ $(MAKEFILE_LIST) \
+ | awk 'BEGIN { FS=":.*?## " }; {printf "\033[36m%-24s\033[0m %s\n", $$1, $$2}' \
+# | sort
+.PHONY: help
+.DEFAULT_GOAL := help
\ No newline at end of file
diff --git a/entrypoint.d/docker-entrypoint.sh b/entrypoint.d/docker-entrypoint.sh
new file mode 100644
index 0000000..5896ae2
--- /dev/null
+++ b/entrypoint.d/docker-entrypoint.sh
@@ -0,0 +1,11 @@
+#!/usr/bin/env bash
+#set -x; : "$0" "$@" # Use for debugging
+
+# Inject Environment Variables for React App
+source ${ENTRYPOINT_FOLDER}/env.sh
+
+rm -f /var/run/apache2/apache2.pid
+
+# Run the main container process
+# (Arguments for Apache supplied from the Dockerfile CMD)
+exec /usr/sbin/apachectl -D FOREGROUND "$@"
\ No newline at end of file
diff --git a/entrypoint.d/env.sh b/entrypoint.d/env.sh
new file mode 100644
index 0000000..56b7236
--- /dev/null
+++ b/entrypoint.d/env.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+for i in $(env | grep ENV_UNITY_UI_)
+do
+ key=$(echo $i | cut -d '=' -f 1)
+ value=$(echo $i | cut -d '=' -f 2-)
+ echo $key=$value
+ # sed All files
+ # find $UNITY_WWW_ROOT -type f -exec sed -i "s|${key}|${value}|g" '{}' +
+
+ # sed .js only
+ find $UNITY_WWW_ROOT -type f \( -name '*.js' \) -exec sed -i "s|${key}|${value}|g" '{}' +
+done
diff --git a/etc/apache2/sites-available/unity-ui.conf b/etc/apache2/sites-available/unity-ui.conf
new file mode 100644
index 0000000..3b8321a
--- /dev/null
+++ b/etc/apache2/sites-available/unity-ui.conf
@@ -0,0 +1,15 @@
+
+
+ ServerName localhost
+ DocumentRoot /var/www/unity-ui/
+ ErrorLog ${APACHE_LOG_DIR}/error.log
+ CustomLog ${APACHE_LOG_DIR}/access.log combined
+
+
+ Options -Indexes
+ AllowOverride All
+ Order allow,deny
+ allow from all
+
+
+
diff --git a/package.json b/package.json
index 56d5991..5c5d56c 100644
--- a/package.json
+++ b/package.json
@@ -1,13 +1,11 @@
{
"name": "unity-ui",
"private": true,
- "version": "0.2.0",
+ "version": "0.5.0",
"type": "module",
"scripts": {
"dev": "vite",
- "build-integration": "tsc && vite build --mode integration --base=/dashboard/",
- "build-staging": "tsc && vite build --mode staging --base=/dashboard/",
- "build-prod": "tsc && vite build --mode production",
+ "build": "tsc && vite build",
"lint": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview"
},
diff --git a/src/Config.tsx b/src/Config.tsx
index 1ef0e62..8121da6 100644
--- a/src/Config.tsx
+++ b/src/Config.tsx
@@ -24,4 +24,9 @@ const Config = {
}
+if( import.meta.env.DEV ) {
+ // Output Configuration on every call to help with debugging only in DEV mode
+ console.log(Config)
+}
+
export default Config;
\ No newline at end of file
diff --git a/src/Root.tsx b/src/Root.tsx
index 0ce8f18..b534c06 100644
--- a/src/Root.tsx
+++ b/src/Root.tsx
@@ -6,14 +6,21 @@ import {
import Home from "./routes/home"
import JobMonitoring from "./routes/jobs/monitoring";
import NewJob from "./routes/jobs/new";
+
import Navbar from "./components/Navbar"
import WebView from "./components/WebView";
import Config from "./Config";
+import { getProcesses, getProcessRoute } from "./utils/processes";
+import NotFound from "./routes/errors/not-found";
+
function Root() {
+
+ const processes = getProcesses();
+
return (
- <>
+
@@ -21,10 +28,23 @@ function Root() {
} />
} />
} />
- } />
+
+ {
+ /* Add routes for job execution forms */
+ processes.map( (item) => {
+ const path = "/jobs/new/" + item['id'];
+ const route:JSX.Element | null = getProcessRoute(item['id']);
+ return (
+ } key={"route_" + item['id']}/>
+ )
+ })
+ }
+
+ } />
+ } />
- >
+
)
}
diff --git a/src/components/BackLink/index.tsx b/src/components/BackLink/index.tsx
new file mode 100644
index 0000000..791b544
--- /dev/null
+++ b/src/components/BackLink/index.tsx
@@ -0,0 +1,18 @@
+import { Link } from "react-router-dom";
+import ChevronLeft from "@nasa-jpl/stellar/icons/chevron_left.svg";
+
+type BackLinkProps = {
+ label:string;
+ path:string;
+};
+
+export const BackLink = (props:BackLinkProps) => {
+
+ const {label, path} = props;
+
+ return(
+ <>
+
{label}
+ >
+ )
+}
\ No newline at end of file
diff --git a/src/components/DocumentMeta/DocumentMeta.tsx b/src/components/DocumentMeta/DocumentMeta.tsx
index 7e7d4bb..3be67a9 100644
--- a/src/components/DocumentMeta/DocumentMeta.tsx
+++ b/src/components/DocumentMeta/DocumentMeta.tsx
@@ -7,7 +7,7 @@ export type DocumentMetaProps = {
export const DocumentMeta = (props:DocumentMetaProps) => {
- let {title, description} = props;
+ const {title, description} = props;
return (
diff --git a/src/components/Navbar/index.tsx b/src/components/Navbar/index.tsx
index 4ce92fe..4625067 100644
--- a/src/components/Navbar/index.tsx
+++ b/src/components/Navbar/index.tsx
@@ -93,6 +93,7 @@ export default function Navbar() {
min={800}
>
}
title="Unity"
version={uiVersion}
diff --git a/src/components/WebView/index.css b/src/components/WebView/index.css
index 9a6f6eb..7444f24 100644
--- a/src/components/WebView/index.css
+++ b/src/components/WebView/index.css
@@ -1,4 +1,10 @@
.unity-webview {
width: 100%;
height: 100%;
+ overflow: auto;
+}
+
+.unity-webview iframe {
+ width: 100%;
+ height: 100%;
}
\ No newline at end of file
diff --git a/src/components/WebView/index.tsx b/src/components/WebView/index.tsx
index 10cd5b4..76cb863 100644
--- a/src/components/WebView/index.tsx
+++ b/src/components/WebView/index.tsx
@@ -9,8 +9,8 @@ export default function WebView(props:WebViewProps) {
const url = props.url;
return (
- <>
-
- >
+
+
+
)
}
\ No newline at end of file
diff --git a/src/css/app.css b/src/css/app.css
index f234d0c..a0cec27 100644
--- a/src/css/app.css
+++ b/src/css/app.css
@@ -1,9 +1,9 @@
/* @import './stellar.css'; */
@import "@nasa-jpl/react-stellar/dist/esm/stellar.css";
-@import "unity-stellar.css";
@import 'ag-grid-community/styles/ag-grid.min.css'; /* Core grid CSS, always needed */
@import 'ag-grid-community/styles/ag-theme-alpine.min.css'; /* Optional grid theme CSS */
@import 'ag-grid-stellar.css';
+@import "unity-stellar.css";
#root {
display: flex;
diff --git a/src/css/unity-stellar.css b/src/css/unity-stellar.css
index e42bd6e..9f02c9c 100644
--- a/src/css/unity-stellar.css
+++ b/src/css/unity-stellar.css
@@ -8,8 +8,52 @@
/* Menu */
-.st-react-menu-label {
- text-transform: uppercase;
+.st-react-menu-content {
+ background: var(--st-gray-80);
+ border: 1px solid var(--st-white);
+ border-radius: 0;
+ padding: 0;
+}
+
+.st-react-menu-content .st-react-menu-arrow {
+ fill: var(--st-white);
+ filter: none;
+ stroke: var(--st-white);
+}
+
+.st-react-menu-content .st-react-menu-label {
+ color: var(--st-gray-10);
+ font-size: 1em;
+ font-weight: 700;
+ letter-spacing: 0.48;
+ padding: 10px 16px;
+ text-transform: uppercase;
+ word-wrap: break-word;
+}
+
+.st-react-menu-content .st-react-menu-item {
+ background-color: var(--st-gray-70);
+ border-radius: 0;
+ color: var(--st-gray-10);
+ font-weight: 400;
+ letter-spacing: 0.60px;
+ line-height: 1em;
+ margin-bottom: 1px;
+ padding: 16px 16px;
+ word-wrap: break-word;
+}
+
+.st-react-menu-content .st-react-menu-item:hover, .st-react-menu-content .st-react-menu-item:hover a {
+ background: var(--st-primary-20);
+ font-weight: 700;
+ color: var(--st-black);
+ cursor: pointer;
+}
+
+.st-react-menu-content .st-react-menu-item > a {
+ color: var(--st-gray-10);
+ text-decoration: none;
+ word-wrap: break-word;
}
/* Navbar */
@@ -40,4 +84,38 @@
.st-react-navbar.st-react-navbar--mobile .st-button:hover.icon {
color: var(--st-button-tertiary-color);
+}
+
+.st-react-navbar-mobile-menu {
+ background: var(--st-gray-80);
+ border: 0;
+ padding: 0;
+}
+
+.st-react-navbar-mobile-menu .st-react-menu-label {
+ font-size: 1em;
+ font-weight: 700;
+ color: var(--st-gray-10);
+ text-transform: uppercase;
+ padding: 10px 16px;
+ word-wrap: break-word;
+}
+
+.st-react-navbar-mobile-menu .st-react-navbar-link {
+ background-color: var(--st-gray-70);
+ border-radius: 0;
+ color: var(--st-gray-10);
+ font-weight: 400;
+ letter-spacing: 0.48px;
+ line-height: 1em;
+ padding: 10px 16px;
+ text-decoration: none;
+ word-wrap: break-word;
+}
+
+.st-react-navbar-mobile-menu .st-react-navbar-link:hover {
+ background: var(--st-primary-20);
+ font-weight: 700;
+ color: var(--st-black);
+ cursor: pointer;
}
\ No newline at end of file
diff --git a/src/index.css b/src/index.css
index ff89127..222ad97 100644
--- a/src/index.css
+++ b/src/index.css
@@ -12,23 +12,30 @@
body {
margin: 0;
- display: flex;
min-width: 320px;
min-height: 100vh;
}
+.viewWrapper {
+ display: flex;
+ flex-direction: column;
+}
+
.view {
+ height: calc(100vh - 48px);
padding: 0px;
background-color: #F1F2F3;
- height: 100%;
+ flex-grow: 1;
}
-.mainView {
+.main-view {
+ height: 100%;
padding: 20px;
}
-.detailView {
- width: 200px;
+.detail-view {
+ height: 100%;
+ min-width: 250px;
background-color: var(--unity-white);
padding: 8px;
}
\ No newline at end of file
diff --git a/src/routes/errors/not-found/index.tsx b/src/routes/errors/not-found/index.tsx
new file mode 100644
index 0000000..3dc3830
--- /dev/null
+++ b/src/routes/errors/not-found/index.tsx
@@ -0,0 +1,19 @@
+import { DocumentMeta } from "../../../components/DocumentMeta/DocumentMeta"
+
+function NotFound() {
+
+ return (
+ <>
+
+
+
Not Found
+ The requested resource cannot be found
+
+ >
+ )
+}
+
+export default NotFound
\ No newline at end of file
diff --git a/src/routes/home/index.tsx b/src/routes/home/index.tsx
index c8ad148..94bf86d 100644
--- a/src/routes/home/index.tsx
+++ b/src/routes/home/index.tsx
@@ -8,7 +8,7 @@ function Home() {
title="Home"
description="Home"
/>
-
+
Home
>
diff --git a/src/routes/jobs/monitoring/index.css b/src/routes/jobs/monitoring/index.css
index c037947..88af009 100644
--- a/src/routes/jobs/monitoring/index.css
+++ b/src/routes/jobs/monitoring/index.css
@@ -8,4 +8,12 @@
.job-detail-item {
padding-bottom: 8px;
word-wrap: break-word;
+}
+
+.button-bar button {
+ margin-right: 10px;
+}
+
+.button-bar button:last-child {
+ margin-right: 0px;
}
\ No newline at end of file
diff --git a/src/routes/jobs/monitoring/index.tsx b/src/routes/jobs/monitoring/index.tsx
index 83cdb46..1d4c311 100644
--- a/src/routes/jobs/monitoring/index.tsx
+++ b/src/routes/jobs/monitoring/index.tsx
@@ -152,33 +152,31 @@ function JobMonitoring() {
title={ !jobid_param ? "Job Monitoring" : "Job Monitoring — " + jobid_param }
description={ !jobid_param ? "Job Monitoring" : "Job Monitoring — " + jobid_param }
/>
-
-
- <>
- Job Monitoring
-
-
- >
+
+
+ Job Monitoring
+
+
+
+
{
selectedJob && (
<>
-
+
Job Details
diff --git a/src/routes/jobs/new/chirp/index.tsx b/src/routes/jobs/new/chirp/index.tsx
new file mode 100644
index 0000000..976051c
--- /dev/null
+++ b/src/routes/jobs/new/chirp/index.tsx
@@ -0,0 +1,275 @@
+import { useState } from "react";
+import { Link } from "react-router-dom";
+import { Button, TextField } from "@nasa-jpl/react-stellar";
+import { DocumentMeta } from "../../../../components/DocumentMeta/DocumentMeta";
+import Config from "../../../../Config";
+import { getTokens } from "../../../../AuthenticationWrapper";
+import { BackLink } from "../../../../components/BackLink";
+
+/*const JOB_FORM_PAGE_LOAD_STATE = {
+ input_processing_labels: "label1, label2",
+ input_cmr_collection_name: "C2011289787-GES_DISC",
+ input_cmr_search_start_time: "2016-08-22T00:10:00Z",
+ input_cmr_search_stop_time: "2016-08-22T01:10:00Z",
+ input_cmr_edl_user: "cmr_user",
+ input_cmr_edl_pass: "cmr_pass",
+ output_collection_id: "urn:nasa:unity:uds_local_test:DEV1:CHRP_16_DAY_REBIN___1",
+ output_data_bucket: "uds-test-cumulus-sps",
+ input_daac_collection_shortname: "CHIRP_L1B",
+ input_daac_collection_sns: "arn:://SNS-arn"
+}*/
+
+const JOB_FORM_PAGE_LOAD_STATE = {
+ input_processing_labels: "",
+ input_cmr_collection_name: "",
+ input_cmr_search_start_time: "",
+ input_cmr_search_stop_time: "",
+ input_cmr_edl_user: "cmr_user",
+ input_cmr_edl_pass: "cmr_pass",
+ output_collection_id: "",
+ output_data_bucket: "",
+ input_daac_collection_shortname: "CHIRP_L1B",
+ input_daac_collection_sns: "arn:://SNS-arn"
+}
+
+const JOB_FORM_INITIAL_STATE = {
+ input_processing_labels: "",
+ input_cmr_collection_name: "",
+ input_cmr_search_start_time: "",
+ input_cmr_search_stop_time: "",
+ input_cmr_edl_user: "cmr_user",
+ input_cmr_edl_pass: "cmr_pass",
+ output_collection_id: "",
+ output_data_bucket: "",
+ input_daac_collection_shortname: "CHIRP_L1B",
+ input_daac_collection_sns: "arn:://SNS-arn"
+}
+
+type NewJobChirpRebinningProps = {
+ process:Process
+};
+
+function NewJobChirpRebinning(props:NewJobChirpRebinningProps) {
+
+ const processEndpoint = Config['sps']['endpoint'] + 'processes';
+ const {process} = props;
+ const [form, setForm] = useState(JOB_FORM_PAGE_LOAD_STATE);
+ const [newJobId, setNewJobID] = useState
();
+ const [submittingJob, setSubmittingJob] = useState(false);
+ const tokens = getTokens();
+ const meta:{ [key: string]: string} = {
+ "description": "Create New " + process.title + " Job",
+ "title": "Create New " + process.title + " Job",
+ }
+
+ const handleChange = (e:Event & { target: HTMLInputElement}) => {
+ setForm({
+ ...form,
+ [e.target.id]: e.target.value,
+ });
+ };
+
+ const handleReset = () => {
+ setForm(JOB_FORM_INITIAL_STATE);
+ }
+
+ const setStopDate = () => {
+
+ const endDate = addDays(new Date(form.input_cmr_search_start_time), 16);
+
+ const year = endDate.toLocaleString("default", { year: "numeric" });
+ const month = endDate.toLocaleString("default", { month: "2-digit" });
+ const day = endDate.toLocaleString("default", { day: "2-digit" });
+ const formattedEndDate = year + "-" + month + "-" + day;
+
+ setForm({
+ ...form,
+ ["input_cmr_search_stop_time"]: formattedEndDate
+ })
+
+ }
+
+ const addDays = function(date:Date, days:number) {
+ date.setDate(date.getDate() + days);
+ return date;
+ }
+
+ const handleSubmit = async (e:React.FormEvent) => {
+
+ e.preventDefault();
+ setSubmittingJob(true);
+
+ const data = {
+ "mode": "async",
+ "response": "document",
+ "inputs": [
+ {
+ "id": "input_processing_labels",
+ "data": form.input_processing_labels.split(",")
+ },
+ {
+ "id": "input_cmr_collection_name",
+ "data": form.input_cmr_collection_name
+ },
+ {
+ "id": "input_cmr_search_start_time",
+ "data": form.input_cmr_search_start_time
+ },
+ {
+ "id": "input_cmr_search_stop_time",
+ "data": form.input_cmr_search_stop_time
+ },
+ {
+ "id": "input_cmr_edl_user",
+ "data": form.input_cmr_edl_user
+ },
+ {
+ "id": "input_cmr_edl_pass",
+ "data": form.input_cmr_edl_pass
+ },
+ {
+ "id": "output_collection_id",
+ "data": form.output_collection_id
+ },
+ {
+ "id": "output_data_bucket",
+ "data": form.output_data_bucket
+ },
+ {
+ "id": "input_daac_collection_shortname",
+ "data": form.input_daac_collection_shortname
+ },
+ {
+ "id": "input_daac_collection_sns",
+ "data": form.input_daac_collection_sns
+ }
+ ],
+ "outputs": [
+ {
+ "id": "output",
+ "transmissionMode": "reference"
+ }
+ ]
+ }
+
+ await fetch(
+ processEndpoint + "/" + process.id + ":" + process.version + "/jobs",
+ {
+ method: "POST",
+ headers: {
+ "Authorization": "Bearer " + tokens.accessToken,
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify(data)
+ }
+ ).then( (response:Response) => {
+
+
+ if( response.ok ) {
+ const jobID:string | undefined = response.headers.get("Location")?.replace("http://127.0.0.1:5000/processes/" + process.id.toString() + ":" + process.version.toString() + "/jobs/","")
+ setNewJobID(jobID);
+ setSubmittingJob(false);
+ }
+
+ }).catch( (error:Error) => {
+ console.debug("Error", error.message);
+ setSubmittingJob(false);
+ })
+
+ handleReset()
+
+ }
+
+ return (
+ <>
+
+
+ >
+ )
+}
+
+export default NewJobChirpRebinning;
\ No newline at end of file
diff --git a/src/routes/jobs/new/index.css b/src/routes/jobs/new/index.css
index 4b2130a..4230d12 100644
--- a/src/routes/jobs/new/index.css
+++ b/src/routes/jobs/new/index.css
@@ -1,5 +1,26 @@
-form {
+.app-list-container {
width: 600px;
background-color: white;
padding: 24px;
+}
+
+.app-list {
+ list-style-type: none;
+ padding: 0px;
+ margin: 0px;
+}
+
+.app-list li {
+ margin-bottom: 20px;
+}
+
+.app-list li:last-child {
+ margin-bottom: 0px;
+}
+
+.job-form {
+ background-color: white;
+ margin-top: 24px;
+ padding: 4px 24px 24px 24px;
+ width: 600px;
}
\ No newline at end of file
diff --git a/src/routes/jobs/new/index.tsx b/src/routes/jobs/new/index.tsx
index ac3e190..0dd7c71 100644
--- a/src/routes/jobs/new/index.tsx
+++ b/src/routes/jobs/new/index.tsx
@@ -1,168 +1,15 @@
-import { useState } from "react";
-import { Link } from "react-router-dom";
-import { Button, TextField } from "@nasa-jpl/react-stellar";
+import { useNavigate } from "react-router-dom";
+import { Button } from "@nasa-jpl/react-stellar";
import { DocumentMeta } from "../../../components/DocumentMeta/DocumentMeta";
-import Config from "../../../Config";
-import { getTokens } from '../../../AuthenticationWrapper';
+import { getProcesses } from "../../../utils/processes";
import "./index.css"
-const JOB_FORM_PAGE_LOAD_STATE = {
- input_processing_labels: "label1, label2",
- input_cmr_collection_name: "C2011289787-GES_DISC",
- input_cmr_search_start_time: "2016-08-22T00:10:00Z",
- input_cmr_search_stop_time: "2016-08-22T01:10:00Z",
- input_cmr_edl_user: "cmr_user",
- input_cmr_edl_pass: "cmr_pass",
- output_collection_id: "urn:nasa:unity:uds_local_test:DEV1:CHRP_16_DAY_REBIN___1",
- output_data_bucket: "uds-test-cumulus-sps",
- input_daac_collection_shortname: "CHIRP_L1B",
- input_daac_collection_sns: "arn:://SNS-arn"
-}
-
-const JOB_FORM_INITIAL_STATE = {
- input_processing_labels: "",
- input_cmr_collection_name: "",
- input_cmr_search_start_time: "",
- input_cmr_search_stop_time: "",
- input_cmr_edl_user: "cmr_user",
- input_cmr_edl_pass: "cmr_pass",
- output_collection_id: "",
- output_data_bucket: "",
- input_daac_collection_shortname: "CHIRP_L1B",
- input_daac_collection_sns: "arn:://SNS-arn"
-}
-
function NewJob() {
- const processEndpoint = Config['sps']['endpoint'] + 'processes';
- const process:{ id:string, title:string, version:string} = {
- id: "chirp",
- title: "Chirp Rebinning Workflow",
- version: "develop"
- }
- const [form, setForm] = useState(JOB_FORM_PAGE_LOAD_STATE);
- const [newJobId, setNewJobID] = useState();
- const [submittingJob, setSubmittingJob] = useState(false);
- const tokens = getTokens();
-
- const handleChange = (e:Event & { target: HTMLInputElement}) => {
- setForm({
- ...form,
- [e.target.id]: e.target.value,
- });
- };
-
- const handleReset = () => {
- setForm(JOB_FORM_INITIAL_STATE);
- }
-
- const setStopDate = () => {
-
- const endDate = addDays(new Date(form.input_cmr_search_start_time), 16);
-
- const year = endDate.toLocaleString("default", { year: "numeric" });
- const month = endDate.toLocaleString("default", { month: "2-digit" });
- const day = endDate.toLocaleString("default", { day: "2-digit" });
- const formattedEndDate = year + "-" + month + "-" + day;
-
- setForm({
- ...form,
- ["input_cmr_search_stop_time"]: formattedEndDate
- })
-
- }
-
- const addDays = function(date:Date, days:number) {
- date.setDate(date.getDate() + days);
- return date;
- }
-
- const handleSubmit = async (e:React.FormEvent) => {
-
- e.preventDefault();
- setSubmittingJob(true);
+ const navigate = useNavigate();
- const data = {
- "mode": "async",
- "response": "document",
- "inputs": [
- {
- "id": "input_processing_labels",
- "data": form.input_processing_labels.split(",")
- },
- {
- "id": "input_cmr_collection_name",
- "data": form.input_cmr_collection_name
- },
- {
- "id": "input_cmr_search_start_time",
- "data": form.input_cmr_search_start_time
- },
- {
- "id": "input_cmr_search_stop_time",
- "data": form.input_cmr_search_stop_time
- },
- {
- "id": "input_cmr_edl_user",
- "data": form.input_cmr_edl_user
- },
- {
- "id": "input_cmr_edl_pass",
- "data": form.input_cmr_edl_pass
- },
- {
- "id": "output_collection_id",
- "data": form.output_collection_id
- },
- {
- "id": "output_data_bucket",
- "data": form.output_data_bucket
- },
- {
- "id": "input_daac_collection_shortname",
- "data": form.input_daac_collection_shortname
- },
- {
- "id": "input_daac_collection_sns",
- "data": form.input_daac_collection_sns
- }
- ],
- "outputs": [
- {
- "id": "output",
- "transmissionMode": "reference"
- }
- ]
- }
-
- await fetch(
- processEndpoint + "/" + process.id + ":" + process.version + "/jobs",
- {
- method: "POST",
- headers: {
- "Authorization": "Bearer " + tokens.accessToken,
- "Content-Type": "application/json",
- },
- body: JSON.stringify(data)
- }
- ).then( (response:Response) => {
-
-
- if( response.ok ) {
- const jobID:string | undefined = response.headers.get("Location")?.replace("http://127.0.0.1:5000/processes/" + process.id.toString() + ":" + process.version.toString() + "/jobs/","")
- setNewJobID(jobID);
- setSubmittingJob(false);
- }
-
- }).catch( (error:Error) => {
- console.debug("Error", error.message);
- setSubmittingJob(false);
- })
-
- handleReset()
-
- }
+ const processes = getProcesses();
return (
<>
@@ -170,87 +17,20 @@ function NewJob() {
title="Create New Job"
description="Create a new Job"
/>
-
+
Create New Job
-
+
+
+ {
+ processes.map( (item) => {
+ const path = "/jobs/new/" + item['id'];
+ return (
+
+ )
+ })
+ }
+
+
>
)
diff --git a/src/routes/jobs/new/l1a/index.tsx b/src/routes/jobs/new/l1a/index.tsx
new file mode 100644
index 0000000..204ce28
--- /dev/null
+++ b/src/routes/jobs/new/l1a/index.tsx
@@ -0,0 +1,214 @@
+import { useState } from "react";
+import { Link } from "react-router-dom";
+import { Button, TextField } from "@nasa-jpl/react-stellar";
+import { DocumentMeta } from "../../../../components/DocumentMeta/DocumentMeta";
+import Config from "../../../../Config";
+import { getTokens } from "../../../../AuthenticationWrapper";
+import { BackLink } from "../../../../components/BackLink";
+
+const JOB_FORM_PAGE_LOAD_STATE = {
+ input_ephatt_collection_id: "",
+ input_science_collection_id: "",
+ output_collection_id: "",
+ static_dir: "",
+ start_datetime: "",
+ stop_datetime: "",
+}
+
+const JOB_FORM_INITIAL_STATE = {
+ input_ephatt_collection_id: "",
+ input_science_collection_id: "",
+ output_collection_id: "",
+ static_dir: "",
+ start_datetime: "",
+ stop_datetime: "",
+}
+
+type NewJobL1AProps = {
+ process:Process
+};
+
+function NewJobL1A(props:NewJobL1AProps) {
+
+ const processEndpoint = Config['sps']['endpoint'] + 'processes';
+ const {process} = props;
+ const [form, setForm] = useState(JOB_FORM_PAGE_LOAD_STATE);
+ const [newJobId, setNewJobID] = useState
();
+ const [submittingJob, setSubmittingJob] = useState(false);
+ const tokens = getTokens();
+ const meta:{ [key: string]: string} = {
+ "description": "Create New " + process.title + " Job",
+ "title": "Create New " + process.title + " Job",
+ }
+
+ const handleChange = (e:Event & { target: HTMLInputElement}) => {
+ setForm({
+ ...form,
+ [e.target.id]: e.target.value,
+ });
+ };
+
+ const handleReset = () => {
+ setForm(JOB_FORM_INITIAL_STATE);
+ }
+
+ const handleSubmit = async (e:React.FormEvent) => {
+
+ e.preventDefault();
+ setSubmittingJob(true);
+
+ const data = {
+ "mode": "async",
+ "response": "document",
+ "inputs": [
+ {
+ "id": "input_ephatt_collection_id",
+ "data": form.input_ephatt_collection_id
+ },
+ {
+ "id": "input_science_collection_id",
+ "data": form.input_science_collection_id
+ },
+ {
+ "id": "output_collection_id",
+ "data": form.output_collection_id
+ },
+ {
+ "id": "static_dir",
+ "data": form.static_dir
+ },
+ {
+ "id": "start_datetime",
+ "data": form.start_datetime
+ },
+ {
+ "id": "stop_datetime",
+ "data": form.stop_datetime
+ },
+ ],
+ "outputs": [
+ {
+ "id": "output",
+ "transmissionMode": "reference"
+ }
+ ]
+ }
+
+ await fetch(
+ processEndpoint + "/" + process.id + ":" + process.version + "/jobs",
+ {
+ method: "POST",
+ headers: {
+ "Authorization": "Bearer " + tokens.accessToken,
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify(data)
+ }
+ ).then( (response:Response) => {
+
+
+ if( response.ok ) {
+ const jobID:string | undefined = response.headers.get("Location")?.replace("http://127.0.0.1:5000/processes/" + process.id.toString() + ":" + process.version.toString() + "/jobs/","")
+ setNewJobID(jobID);
+ setSubmittingJob(false);
+ }
+
+ }).catch( (error:Error) => {
+ console.debug("Error", error.message);
+ setSubmittingJob(false);
+ })
+
+ handleReset()
+
+ }
+
+ return (
+ <>
+
+
+ >
+ )
+}
+
+export default NewJobL1A;
\ No newline at end of file
diff --git a/src/routes/jobs/new/l1b/index.tsx b/src/routes/jobs/new/l1b/index.tsx
new file mode 100644
index 0000000..2c68a88
--- /dev/null
+++ b/src/routes/jobs/new/l1b/index.tsx
@@ -0,0 +1,183 @@
+import { useState } from "react";
+import { Link } from "react-router-dom";
+import { Button, TextField } from "@nasa-jpl/react-stellar";
+import { DocumentMeta } from "../../../../components/DocumentMeta/DocumentMeta";
+import Config from "../../../../Config";
+import { getTokens } from "../../../../AuthenticationWrapper";
+import { BackLink } from "../../../../components/BackLink";
+
+const JOB_FORM_PAGE_LOAD_STATE = {
+ input_collection_id: "",
+ start_datetime: "",
+ stop_datetime: "",
+ output_collection_id: "",
+}
+
+const JOB_FORM_INITIAL_STATE = {
+ input_collection_id: "",
+ start_datetime: "",
+ stop_datetime: "",
+ output_collection_id: "",
+}
+
+type NewJobL1BProps = {
+ process:Process
+};
+
+function NewJobL1B(props:NewJobL1BProps) {
+
+ const processEndpoint = Config['sps']['endpoint'] + 'processes';
+ const {process} = props;
+ const [form, setForm] = useState(JOB_FORM_PAGE_LOAD_STATE);
+ const [newJobId, setNewJobID] = useState();
+ const [submittingJob, setSubmittingJob] = useState(false);
+ const tokens = getTokens();
+ const meta:{ [key: string]: string} = {
+ "description": "Create New " + process.title + " Job",
+ "title": "Create New " + process.title + " Job",
+ }
+
+ const handleChange = (e:Event & { target: HTMLInputElement}) => {
+ setForm({
+ ...form,
+ [e.target.id]: e.target.value,
+ });
+ };
+
+ const handleReset = () => {
+ setForm(JOB_FORM_INITIAL_STATE);
+ }
+
+ const handleSubmit = async (e:React.FormEvent) => {
+
+ e.preventDefault();
+ setSubmittingJob(true);
+
+ const data = {
+ "mode": "async",
+ "response": "document",
+ "inputs": [
+ {
+ "id": "input_collection_id",
+ "data": form.input_collection_id
+ },
+ {
+ "id": "start_datetime",
+ "data": form.start_datetime
+ },
+ {
+ "id": "stop_datetime",
+ "data": form.stop_datetime
+ },
+ {
+ "id": "output_collection_id",
+ "data": form.output_collection_id
+ },
+ ],
+ "outputs": [
+ {
+ "id": "output",
+ "transmissionMode": "reference"
+ }
+ ]
+ }
+
+ await fetch(
+ processEndpoint + "/" + process.id + ":" + process.version + "/jobs",
+ {
+ method: "POST",
+ headers: {
+ "Authorization": "Bearer " + tokens.accessToken,
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify(data)
+ }
+ ).then( (response:Response) => {
+
+
+ if( response.ok ) {
+ const jobID:string | undefined = response.headers.get("Location")?.replace("http://127.0.0.1:5000/processes/" + process.id.toString() + ":" + process.version.toString() + "/jobs/","")
+ setNewJobID(jobID);
+ setSubmittingJob(false);
+ }
+
+ }).catch( (error:Error) => {
+ console.debug("Error", error.message);
+ setSubmittingJob(false);
+ })
+
+ handleReset()
+
+ }
+
+ return (
+ <>
+
+
+ >
+ )
+}
+
+export default NewJobL1B;
\ No newline at end of file
diff --git a/src/types/process.d.tsx b/src/types/process.d.tsx
new file mode 100644
index 0000000..a68f27a
--- /dev/null
+++ b/src/types/process.d.tsx
@@ -0,0 +1,5 @@
+interface Process {
+ id:string;
+ title:string;
+ version:string;
+}
\ No newline at end of file
diff --git a/src/utils/processes.tsx b/src/utils/processes.tsx
new file mode 100644
index 0000000..6ccad44
--- /dev/null
+++ b/src/utils/processes.tsx
@@ -0,0 +1,49 @@
+import NewJobChirpRebinning from "../routes/jobs/new/chirp";
+import NewJobL1A from "../routes/jobs/new/l1a";
+import NewJobL1B from "../routes/jobs/new/l1b";
+
+const getProcesses = ():Process[] => {
+ return (
+ [
+ {
+ id: "chirp",
+ title: "CHIRP Rebinning",
+ version: "develop",
+ },
+ {
+ id: "l1a",
+ title: "l1a_pge_cwl",
+ version: "develop",
+ },
+ {
+ id: "l1b",
+ title: "l1b_pge_cwl",
+ version: "develop",
+ },
+ ]
+ )
+};
+
+const getProcess = (processID:string):Process => {
+ const processes:Process[] = getProcesses();
+ const index = processes.findIndex( process => process.id === processID );
+ return processes[index];
+};
+
+
+const getProcessRoute = (processID:string):JSX.Element | null => {
+
+ const process:Process = getProcess(processID)
+ const processRoutes:{ [key: string]: JSX.Element} = {
+ "chirp": ,
+ "l1a": ,
+ "l1b": ,
+ }
+
+ return (
+ (process) ? processRoutes[processID] : null
+ )
+
+};
+
+export { getProcesses, getProcess, getProcessRoute };
\ No newline at end of file