diff --git a/API/Backend/Datasets/routes/datasets.js b/API/Backend/Datasets/routes/datasets.js index fd5221d0..436c73c1 100644 --- a/API/Backend/Datasets/routes/datasets.js +++ b/API/Backend/Datasets/routes/datasets.js @@ -10,6 +10,7 @@ const inspect = require("util").inspect; const { sequelize } = require("../../../connection"); +const Utils = require("../../../utils.js"); const logger = require("../../../logger"); const datasets = require("../models/datasets"); const csvtojson = require("csvtojson"); @@ -88,10 +89,63 @@ router.post("/entries", function (req, res, next) { for (let i = 0; i < sets.length; i++) { entries.push({ name: sets[i].name, updated: sets[i].updatedAt }); } - res.send({ - status: "success", - body: { entries: entries }, - }); + // For each entry, list all occurrences in latest configuration objects + sequelize + .query( + ` + SELECT t1.* + FROM configs AS t1 + INNER JOIN ( + SELECT mission, MAX(version) AS max_version + FROM configs + GROUP BY mission + ) AS t2 + ON t1.mission = t2.mission AND t1.version = t2.max_version ORDER BY mission ASC; + ` + ) + .then(([results]) => { + // Populate occurrences + results.forEach((m) => { + Utils.traverseLayers(m.config.layers, (layer, path) => { + entries.forEach((entry) => { + entry.occurrences = entry.occurrences || {}; + entry.occurrences[m.mission] = + entry.occurrences[m.mission] || []; + if (layer?.variables?.datasetLinks?.length != null) { + layer.variables.datasetLinks.forEach((d) => { + if (d.dataset === entry.name) { + entry.occurrences[m.mission].push({ + name: layer.name, + uuid: layer.uuid, + path: path, + }); + } + }); + } + }); + }); + }); + + res.send({ + status: "success", + body: { entries: entries }, + }); + return null; + }) + .catch((err) => { + logger( + "error", + "Failed to find missions.", + req.originalUrl, + req, + err + ); + res.send({ + status: "failure", + message: "Failed to find missions.", + }); + return null; + }); } else { res.send({ status: "failure", @@ -175,6 +229,56 @@ router.post("/search", function (req, res, next) { }); }); +/* + * req.query.layer + */ +router.get("/download", function (req, res, next) { + //First Find the table name + Datasets.findOne({ where: { name: req.query.layer } }) + .then((result) => { + if (result) { + let table = result.dataValues.table; + + sequelize + .query("SELECT * FROM " + table) + .then(([results]) => { + res.send({ + status: "success", + body: results, + }); + + return null; + }) + .catch((err) => { + logger( + "error", + "SQL error downloading dataset.", + req.originalUrl, + req, + err + ); + res.send({ + status: "failure", + message: "SQL error.", + }); + }); + } else { + res.send({ + status: "failure", + message: "Layer not found.", + }); + } + + return null; + }) + .catch((err) => { + logger("error", "Failure finding dataset.", req.originalUrl, req, err); + res.send({ + status: "failure", + }); + }); +}); + router.post("/upload", function (req, res, next) { // Disable timeout req.setTimeout(0); diff --git a/API/Backend/Geodatasets/routes/geodatasets.js b/API/Backend/Geodatasets/routes/geodatasets.js index a57f10dc..360b640c 100644 --- a/API/Backend/Geodatasets/routes/geodatasets.js +++ b/API/Backend/Geodatasets/routes/geodatasets.js @@ -291,7 +291,16 @@ router.post("/entries", function (req, res, next) { // For each entry, list all occurrences in latest configuration objects sequelize .query( - "SELECT DISTINCT ON (mission) mission, version, config FROM configs ORDER BY mission ASC" + ` + SELECT t1.* + FROM configs AS t1 + INNER JOIN ( + SELECT mission, MAX(version) AS max_version + FROM configs + GROUP BY mission + ) AS t2 + ON t1.mission = t2.mission AND t1.version = t2.max_version ORDER BY mission ASC; + ` ) .then(([results]) => { // Populate occurrences diff --git a/configure/src/components/Map/Map.js b/configure/src/components/Map/Map.js index 8eb8fd8b..2132053f 100644 --- a/configure/src/components/Map/Map.js +++ b/configure/src/components/Map/Map.js @@ -1,8 +1,6 @@ import React, { useEffect, useState } from "react"; import * as L from "leaflet"; -import clsx from "clsx"; - import ReactJson from "react-json-view"; import { makeStyles } from "@mui/styles"; diff --git a/configure/src/components/Panel/Panel.js b/configure/src/components/Panel/Panel.js index 56a8a867..c435df58 100644 --- a/configure/src/components/Panel/Panel.js +++ b/configure/src/components/Panel/Panel.js @@ -6,12 +6,7 @@ import mmgisLogo from "../../images/mmgis.png"; import clsx from "clsx"; -import { - setConfiguration, - setMission, - setModal, - setPage, -} from "../../core/ConfigureStore"; +import { setMission, setModal, setPage } from "../../core/ConfigureStore"; import NewMissionModal from "./Modals/NewMissionModal/NewMissionModal"; @@ -222,7 +217,7 @@ export default function Panel() { dispatch(setPage({ page: "webhooks" })); }} > - WebHooks + Webhooks diff --git a/configure/src/components/SaveBar/Modals/PreviewModal/PreviewModal.js b/configure/src/components/SaveBar/Modals/PreviewModal/PreviewModal.js index 22c6e3a6..473bea70 100644 --- a/configure/src/components/SaveBar/Modals/PreviewModal/PreviewModal.js +++ b/configure/src/components/SaveBar/Modals/PreviewModal/PreviewModal.js @@ -1,18 +1,9 @@ -import React, { useState } from "react"; +import React from "react"; import { useSelector, useDispatch } from "react-redux"; -import { calls } from "../../../../core/calls"; +import { setModal } from "../../../../core/ConfigureStore"; -import { - setMissions, - setModal, - setSnackBarText, -} from "../../../../core/ConfigureStore"; - -import Typography from "@mui/material/Typography"; -import Button from "@mui/material/Button"; import Dialog from "@mui/material/Dialog"; -import DialogActions from "@mui/material/DialogActions"; import DialogContent from "@mui/material/DialogContent"; import DialogTitle from "@mui/material/DialogTitle"; import IconButton from "@mui/material/IconButton"; @@ -20,11 +11,6 @@ import IconButton from "@mui/material/IconButton"; import CloseSharpIcon from "@mui/icons-material/CloseSharp"; import PreviewIcon from "@mui/icons-material/Preview"; -import TextField from "@mui/material/TextField"; -import FormGroup from "@mui/material/FormGroup"; -import FormControlLabel from "@mui/material/FormControlLabel"; -import Checkbox from "@mui/material/Checkbox"; - import { makeStyles, useTheme } from "@mui/styles"; import useMediaQuery from "@mui/material/useMediaQuery"; diff --git a/configure/src/components/SaveBar/SaveBar.js b/configure/src/components/SaveBar/SaveBar.js index dd0f426f..3af7c5ef 100644 --- a/configure/src/components/SaveBar/SaveBar.js +++ b/configure/src/components/SaveBar/SaveBar.js @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from "react"; +import React from "react"; import { useSelector, useDispatch } from "react-redux"; import {} from "./SaveBarSlice"; import { makeStyles } from "@mui/styles"; diff --git a/configure/src/components/Tabs/Coordinates/Coordinates.js b/configure/src/components/Tabs/Coordinates/Coordinates.js index 1cee7ad9..6df92656 100644 --- a/configure/src/components/Tabs/Coordinates/Coordinates.js +++ b/configure/src/components/Tabs/Coordinates/Coordinates.js @@ -1,10 +1,8 @@ -import React, { useEffect, useState } from "react"; -import { useSelector, useDispatch } from "react-redux"; +import React from "react"; +import { useDispatch } from "react-redux"; import { makeStyles } from "@mui/styles"; -import { calls } from "../../../core/calls"; import Maker from "../../../core/Maker"; -import { setSnackBarText } from "../../../core/ConfigureStore"; import config from "../../../metaconfigs/tab-coordinates-config.json"; diff --git a/configure/src/components/Tabs/Home/Home.js b/configure/src/components/Tabs/Home/Home.js index 32398123..015c3cec 100644 --- a/configure/src/components/Tabs/Home/Home.js +++ b/configure/src/components/Tabs/Home/Home.js @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from "react"; +import React from "react"; import { useSelector, useDispatch } from "react-redux"; import { setVersions } from "./HomeSlice"; import { makeStyles } from "@mui/styles"; diff --git a/configure/src/components/Tabs/Home/Modals/DeleteConfigModal/DeleteConfigModal.js b/configure/src/components/Tabs/Home/Modals/DeleteConfigModal/DeleteConfigModal.js index 276e0b2e..2d216934 100644 --- a/configure/src/components/Tabs/Home/Modals/DeleteConfigModal/DeleteConfigModal.js +++ b/configure/src/components/Tabs/Home/Modals/DeleteConfigModal/DeleteConfigModal.js @@ -20,7 +20,6 @@ import IconButton from "@mui/material/IconButton"; import CloseSharpIcon from "@mui/icons-material/CloseSharp"; import ShapeLineIcon from "@mui/icons-material/ShapeLine"; -import WarningIcon from "@mui/icons-material/Warning"; import DeleteForeverIcon from "@mui/icons-material/DeleteForever"; import TextField from "@mui/material/TextField"; diff --git a/configure/src/components/Tabs/Home/Modals/UploadConfigModal/UploadConfigModal.js b/configure/src/components/Tabs/Home/Modals/UploadConfigModal/UploadConfigModal.js index 5ba726e4..0fdcb23e 100644 --- a/configure/src/components/Tabs/Home/Modals/UploadConfigModal/UploadConfigModal.js +++ b/configure/src/components/Tabs/Home/Modals/UploadConfigModal/UploadConfigModal.js @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from "react"; +import React, { useState } from "react"; import { useSelector, useDispatch } from "react-redux"; import { calls } from "../../../../../core/calls"; diff --git a/configure/src/components/Tabs/Home/Versions.js b/configure/src/components/Tabs/Home/Versions.js index 0eb464b9..05dff816 100644 --- a/configure/src/components/Tabs/Home/Versions.js +++ b/configure/src/components/Tabs/Home/Versions.js @@ -1,9 +1,7 @@ -import React, { useEffect, useState } from "react"; +import React, { useEffect } from "react"; import { useSelector, useDispatch } from "react-redux"; import { makeStyles } from "@mui/styles"; -import clsx from "clsx"; - import { calls } from "../../../core/calls"; import { downloadObject } from "../../../core/utils"; import { @@ -12,10 +10,8 @@ import { setConfiguration, clearLockConfig, } from "../../../core/ConfigureStore"; -import { setVersions } from "./HomeSlice"; import PropTypes from "prop-types"; -import { alpha } from "@mui/material/styles"; import Box from "@mui/material/Box"; import Table from "@mui/material/Table"; import TableBody from "@mui/material/TableBody"; @@ -25,31 +21,15 @@ import TableHead from "@mui/material/TableHead"; import TablePagination from "@mui/material/TablePagination"; import TableRow from "@mui/material/TableRow"; import TableSortLabel from "@mui/material/TableSortLabel"; -import Toolbar from "@mui/material/Toolbar"; -import Typography from "@mui/material/Typography"; import Paper from "@mui/material/Paper"; -import Checkbox from "@mui/material/Checkbox"; import IconButton from "@mui/material/IconButton"; -import Button from "@mui/material/Button"; import Tooltip from "@mui/material/Tooltip"; -import FormControlLabel from "@mui/material/FormControlLabel"; -import Switch from "@mui/material/Switch"; -import DeleteIcon from "@mui/icons-material/Delete"; -import FilterListIcon from "@mui/icons-material/FilterList"; import Divider from "@mui/material/Divider"; -import Badge from "@mui/material/Badge"; import { visuallyHidden } from "@mui/utils"; -import InventoryIcon from "@mui/icons-material/Inventory"; import PreviewIcon from "@mui/icons-material/Preview"; import DownloadIcon from "@mui/icons-material/Download"; -import UploadIcon from "@mui/icons-material/Upload"; -import DriveFileRenameOutlineIcon from "@mui/icons-material/DriveFileRenameOutline"; -import DeleteForeverIcon from "@mui/icons-material/DeleteForever"; -import AddIcon from "@mui/icons-material/Add"; -import ShapeLineIcon from "@mui/icons-material/ShapeLine"; import LowPriorityIcon from "@mui/icons-material/LowPriority"; -import ControlPointDuplicateIcon from "@mui/icons-material/ControlPointDuplicate"; function descendingComparator(a, b, orderBy) { if (b[orderBy] < a[orderBy]) { diff --git a/configure/src/components/Tabs/Layers/Layers.js b/configure/src/components/Tabs/Layers/Layers.js index 705bc6a9..2fbf350e 100644 --- a/configure/src/components/Tabs/Layers/Layers.js +++ b/configure/src/components/Tabs/Layers/Layers.js @@ -3,19 +3,13 @@ import { useSelector, useDispatch } from "react-redux"; import {} from "./LayersSlice"; import { makeStyles } from "@mui/styles"; -import { calls } from "../../../core/calls"; import { reorderArray } from "../../../core/utils"; -import { - setSnackBarText, - setModal, - setConfiguration, -} from "../../../core/ConfigureStore"; +import { setModal, setConfiguration } from "../../../core/ConfigureStore"; import LayerModal from "./Modals/LayerModal/LayerModal"; import { DragDropContext, Droppable, Draggable } from "react-beautiful-dnd"; -import List from "@mui/material/List"; import ListItem from "@mui/material/ListItem"; import ListItemButton from "@mui/material/ListItemButton"; import ListItemIcon from "@mui/material/ListItemIcon"; diff --git a/configure/src/components/Tabs/Layers/Modals/LayerModal/LayerModal.js b/configure/src/components/Tabs/Layers/Modals/LayerModal/LayerModal.js index ff0666a1..906c1894 100644 --- a/configure/src/components/Tabs/Layers/Modals/LayerModal/LayerModal.js +++ b/configure/src/components/Tabs/Layers/Modals/LayerModal/LayerModal.js @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import React from "react"; import { useSelector, useDispatch } from "react-redux"; import { getLayerByUUID, traverseLayers } from "../../../../../core/utils"; diff --git a/configure/src/components/Tabs/Time/Time.js b/configure/src/components/Tabs/Time/Time.js index f00380a6..f7c73a19 100644 --- a/configure/src/components/Tabs/Time/Time.js +++ b/configure/src/components/Tabs/Time/Time.js @@ -1,10 +1,8 @@ -import React, { useEffect, useState } from "react"; -import { useSelector, useDispatch } from "react-redux"; +import React from "react"; +import { useDispatch } from "react-redux"; import { makeStyles } from "@mui/styles"; -import { calls } from "../../../core/calls"; import Maker from "../../../core/Maker"; -import { setSnackBarText } from "../../../core/ConfigureStore"; import config from "../../../metaconfigs/tab-time-config.json"; diff --git a/configure/src/components/Tabs/Tools/Modals/ToolModal/ToolModal.js b/configure/src/components/Tabs/Tools/Modals/ToolModal/ToolModal.js index bdee5389..af767220 100644 --- a/configure/src/components/Tabs/Tools/Modals/ToolModal/ToolModal.js +++ b/configure/src/components/Tabs/Tools/Modals/ToolModal/ToolModal.js @@ -1,20 +1,12 @@ -import React, { useState } from "react"; +import React from "react"; import { useSelector, useDispatch } from "react-redux"; -import { calls } from "../../../../../core/calls"; import { - getLayerByUUID, - getIn, - setIn, getToolFromConfiguration, updateToolInConfiguration, } from "../../../../../core/utils"; -import { - setModal, - setSnackBarText, - setConfiguration, -} from "../../../../../core/ConfigureStore"; +import { setModal, setConfiguration } from "../../../../../core/ConfigureStore"; import Typography from "@mui/material/Typography"; import Button from "@mui/material/Button"; diff --git a/configure/src/components/Tabs/Tools/Tools.js b/configure/src/components/Tabs/Tools/Tools.js index e3bcc285..6aa63e68 100644 --- a/configure/src/components/Tabs/Tools/Tools.js +++ b/configure/src/components/Tabs/Tools/Tools.js @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from "react"; +import React, { useEffect } from "react"; import { useSelector, useDispatch } from "react-redux"; import {} from "./ToolsSlice"; import { makeStyles } from "@mui/styles"; @@ -15,7 +15,6 @@ import { import ToolModal from "./Modals/ToolModal/ToolModal"; import Grid from "@mui/material/Grid"; -import Paper from "@mui/material/Paper"; import Box from "@mui/material/Box"; const useStyles = makeStyles((theme) => ({ diff --git a/configure/src/components/Tabs/UserInterface/UserInterface.js b/configure/src/components/Tabs/UserInterface/UserInterface.js index 80d941aa..2e403612 100644 --- a/configure/src/components/Tabs/UserInterface/UserInterface.js +++ b/configure/src/components/Tabs/UserInterface/UserInterface.js @@ -1,10 +1,7 @@ -import React, { useEffect, useState } from "react"; -import { useSelector, useDispatch } from "react-redux"; +import React from "react"; import { makeStyles } from "@mui/styles"; -import { calls } from "../../../core/calls"; import Maker from "../../../core/Maker"; -import { setSnackBarText } from "../../../core/ConfigureStore"; import config from "../../../metaconfigs/tab-ui-config.json"; @@ -22,8 +19,6 @@ const useStyles = makeStyles((theme) => ({ export default function UserInterface() { const c = useStyles(); - const dispatch = useDispatch(); - return (
diff --git a/configure/src/core/Configure.js b/configure/src/core/Configure.js index 5af56e77..153d7cf7 100644 --- a/configure/src/core/Configure.js +++ b/configure/src/core/Configure.js @@ -1,6 +1,6 @@ -import { useEffect, useState } from "react"; -import { useSelector, useDispatch } from "react-redux"; -import clsx from "clsx"; +import { useEffect } from "react"; +import { useDispatch } from "react-redux"; + import { makeStyles } from "@mui/styles"; import Main from "../components/Main/Main"; diff --git a/configure/src/core/ConfigureStore.js b/configure/src/core/ConfigureStore.js index 60ec2f97..c431a9c0 100644 --- a/configure/src/core/ConfigureStore.js +++ b/configure/src/core/ConfigureStore.js @@ -27,6 +27,9 @@ export const ConfigureStore = createSlice({ appendGeoDataset: false, updateGeoDataset: false, newDataset: false, + layersUsedByDataset: false, + updateDataset: false, + deleteDataset: false, uploadConfig: false, cloneConfig: false, deleteConfig: false, diff --git a/configure/src/core/Maker.js b/configure/src/core/Maker.js index 15abd1b7..09720faf 100644 --- a/configure/src/core/Maker.js +++ b/configure/src/core/Maker.js @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from "react"; +import React, { useState } from "react"; import { useSelector, useDispatch } from "react-redux"; import { makeStyles } from "@mui/styles"; @@ -7,7 +7,6 @@ import "leaflet/dist/leaflet.css"; import clsx from "clsx"; import Box from "@mui/material/Box"; -import Paper from "@mui/material/Paper"; import Grid from "@mui/material/Grid"; import Tabs from "@mui/material/Tabs"; import Tab from "@mui/material/Tab"; @@ -29,8 +28,7 @@ import Slider from "@mui/material/Slider"; import AddIcon from "@mui/icons-material/Add"; import CloseIcon from "@mui/icons-material/Close"; -import { calls } from "./calls"; -import { setConfiguration, setSnackBarText } from "./ConfigureStore"; +import { setConfiguration } from "./ConfigureStore"; import { getIn, setIn, @@ -198,6 +196,9 @@ const useStyles = makeStyles((theme) => ({ json: { border: `1px solid ${theme.palette.swatches.grey[850]}`, boxShadow: `0px 2px 4px 0px rgba(0, 0, 0, 0.2)`, + "& > div:first-child": { + margin: "4px 8px 0px 8px", + }, }, slider: { flexFlow: "column !important", @@ -315,11 +316,20 @@ const getComponent = ( ); case "json": let json_f = value || getIn(directConf, com.field, {}); + try { + if (typeof json_f === "string") json_f = JSON.parse(json_f); + } catch (err) {} return (
+
+ {com.name || ""} + + {com.description || ""} + +
{ try { diff --git a/configure/src/core/Websocket.js b/configure/src/core/Websocket.js index 5fbaa8d7..848a544c 100644 --- a/configure/src/core/Websocket.js +++ b/configure/src/core/Websocket.js @@ -1,14 +1,10 @@ -import React, { useEffect, useState } from "react"; +import React, { useEffect } from "react"; import { useSelector, useDispatch } from "react-redux"; import { makeStyles } from "@mui/styles"; import ReportIcon from "@mui/icons-material/Report"; -import { - setSnackBarText, - clearLockConfig, - setLockConfig, -} from "./ConfigureStore"; +import { clearLockConfig, setLockConfig } from "./ConfigureStore"; const vars = { initialWebSocketRetryInterval: 20000, // 1 minute diff --git a/configure/src/core/calls.js b/configure/src/core/calls.js index db1e96f0..16b2dd3e 100644 --- a/configure/src/core/calls.js +++ b/configure/src/core/calls.js @@ -71,9 +71,13 @@ const c = { url: "api/datasets/entries", }, datasets_get: { - type: "GET", + type: "POST", url: "api/datasets/get", }, + datasets_download: { + type: "GET", + url: "api/datasets/download", + }, longtermtoken_get: { type: "GET", url: "api/longtermtoken/get", diff --git a/configure/src/core/routes/routes.js b/configure/src/core/routes/routes.js index a861e651..8d38a290 100644 --- a/configure/src/core/routes/routes.js +++ b/configure/src/core/routes/routes.js @@ -1,4 +1,4 @@ -import React, { useEffect } from "react"; +import React from "react"; import { BrowserRouter as Router, Routes, Route } from "react-router-dom"; import SnackBar from "../../components/SnackBar/SnackBar"; diff --git a/configure/src/pages/APITokens/APITokens.js b/configure/src/pages/APITokens/APITokens.js index ab121576..74d6312a 100644 --- a/configure/src/pages/APITokens/APITokens.js +++ b/configure/src/pages/APITokens/APITokens.js @@ -1,5 +1,5 @@ import React, { useEffect, useState } from "react"; -import { useSelector, useDispatch } from "react-redux"; +import { useDispatch } from "react-redux"; import { makeStyles } from "@mui/styles"; import clsx from "clsx"; @@ -39,11 +39,11 @@ const useStyles = makeStyles((theme) => ({ }, top: { display: "flex", - borderBottom: `1px solid ${theme.palette.swatches.grey[800]}`, paddingBottom: "10px", + height: "48px", }, title: { - margin: "40px 60px 0px 60px", + margin: "18px 0px 0px 60px", paddingBottom: "5px", fontSize: "22px", letterSpacing: "1px", @@ -54,13 +54,10 @@ const useStyles = makeStyles((theme) => ({ margin: "20px 60px", }, subtitle: { - margin: "40px 60px 0px 60px", + margin: "0px 60px 0px 60px !important", + fontSize: "13px !important", paddingBottom: "5px", - fontSize: "12px", - letterSpacing: "1px", - color: theme.palette.swatches.p[12], - fontWeight: "bold", - textTransform: "uppercase", + fontStyle: "italic", }, subtitle2: { fontSize: "12px !important", @@ -77,8 +74,8 @@ const useStyles = makeStyles((theme) => ({ }, generate: { width: "100%", height: "48px" }, generated: { - height: "48px", - lineHeight: "48px", + height: "42px", + lineHeight: "42px", display: "flex", marginBottom: "20px", border: `2px solid ${theme.palette.swatches.p[0]}`, @@ -94,7 +91,7 @@ const useStyles = makeStyles((theme) => ({ padding: "0px 16px", }, "& > div:nth-child(3)": { - height: "48px", + height: "42px", }, }, genTitle: {}, @@ -103,18 +100,18 @@ const useStyles = makeStyles((theme) => ({ tokenList: {}, tokenListItem: { display: "flex", - height: "48px", - lineHeight: "48px", + height: "42px", + lineHeight: "42px", width: "100%", background: theme.palette.swatches.grey[900], border: `1px solid ${theme.palette.swatches.grey[700]}`, - boxShadow: `0px 1px 4px 0px rgba(0,0,0,0.3)`, - marginBottom: "8px", + boxShadow: `0px 1px 3px 0px rgba(0,0,0,0.15)`, + marginBottom: "3px", "& > div:nth-child(1)": { background: theme.palette.swatches.grey[150], color: "white", textAlign: "center", - width: "48px", + width: "42px", }, "& > div:nth-child(2)": { flex: 1, padding: "0px 16px" }, "& > div:nth-child(3)": { @@ -139,10 +136,28 @@ const useStyles = makeStyles((theme) => ({ }, "& > div:nth-child(5)": { borderLeft: `1px solid ${theme.palette.swatches.grey[700]}`, - width: "48px", + width: "42px", textAlign: "center", }, }, + examples: { + margin: "20px 0px", + "& > ul": { + listStyleType: "none", + padding: "0px", + }, + "& > ul > li > div:first-child": { + fontWeight: "bold", + padding: "10px 0px", + }, + }, + examplesTitle: { + fontSize: "20px", + color: theme.palette.swatches.grey[150], + borderBottom: `1px solid ${theme.palette.swatches.grey[150]}`, + padding: "10px 0px", + fontWeight: "bold", + }, code: { fontFamily: "monospace", padding: "8px", @@ -265,22 +280,16 @@ export default function APITokens() {
API Tokens
-
+
+
+ { "Generate an authentication token for programmatic control over the configuration and data endpoints. The generated token may be used it requests via the header: 'Authorization:Bearer ' and more information can be found at https://nasa-ammos.github.io/MMGIS/apis/configure#api-tokens" } -
-
-
+ - - + + - + {"Expire After"} - {isDragAccept &&

All files will be accepted

} - {isDragReject &&

Some files will be rejected

} - {!isDragActive && ( -
-

Drag 'n' drop or click to select files...

-

Only *.json and *.geojson files are accepted.

-
- )} -
-
- -
- -
{fileName || "No File Selected"}
-
- -
-
- { - setStartTimeField(e.target.value); - }} - /> - - {`If this GeoDataset already has a Start Time Field attached, the name of that start time field inside each feature's "properties" object for which to create a temporal index for the geodataset. Take care in using time field names for the appended GeoJSON features that are different from that of the existing features.`} - -
-
- { - setEndTimeField(e.target.value); - }} - /> - - {`If this GeoDataset already has a End Time Field attached, the name of that end time field inside each feature's "properties" object for which to create a temporal index for the geodataset. Take care in using time field names for the appended GeoJSON features that are different from that of the existing features.`} - -
-
- - - - - - ); -}; - -export default AppendGeoDatasetModal; diff --git a/configure/src/pages/Datasets/Modals/DeleteGeoDatasetModal/DeleteGeoDatasetModal.js b/configure/src/pages/Datasets/Modals/DeleteDatasetModal/DeleteDatasetModal.js similarity index 83% rename from configure/src/pages/Datasets/Modals/DeleteGeoDatasetModal/DeleteGeoDatasetModal.js rename to configure/src/pages/Datasets/Modals/DeleteDatasetModal/DeleteDatasetModal.js index 16133650..22286621 100644 --- a/configure/src/pages/Datasets/Modals/DeleteGeoDatasetModal/DeleteGeoDatasetModal.js +++ b/configure/src/pages/Datasets/Modals/DeleteDatasetModal/DeleteDatasetModal.js @@ -161,40 +161,40 @@ const useStyles = makeStyles((theme) => ({ cancel: {}, })); -const MODAL_NAME = "deleteGeoDataset"; -const DeleteGeoDatasetModal = (props) => { - const { queryGeoDatasets } = props; +const MODAL_NAME = "deleteDataset"; +const DeleteDatasetModal = (props) => { + const { queryDatasets } = props; const c = useStyles(); const modal = useSelector((state) => state.core.modal[MODAL_NAME]); - const geodatasets = useSelector((state) => state.core.geodatasets); + const datasets = useSelector((state) => state.core.datasets); const theme = useTheme(); const isMobile = useMediaQuery(theme.breakpoints.down("md")); const dispatch = useDispatch(); - const [geoDatasetName, setGeoDatasetName] = useState(null); + const [datasetName, setDatasetName] = useState(null); const handleClose = () => { // close modal dispatch(setModal({ name: MODAL_NAME, on: false })); }; const handleSubmit = () => { - if (!modal?.geoDataset?.name) { + if (!modal?.dataset?.name) { dispatch( setSnackBarText({ - text: "Cannot delete undefined GeoDataset.", + text: "Cannot delete undefined Dataset.", severity: "error", }) ); return; } - if (geoDatasetName !== modal.geoDataset.name) { + if (datasetName !== modal.dataset.name) { dispatch( setSnackBarText({ - text: "Confirmation GeoDataset name does not match.", + text: "Confirmation Dataset name does not match.", severity: "error", }) ); @@ -202,26 +202,26 @@ const DeleteGeoDatasetModal = (props) => { } calls.api( - "geodatasets_remove", + "datasets_remove", { urlReplacements: { - name: modal.geoDataset.name, + name: modal.dataset.name, }, }, (res) => { if (res.status === "success") { dispatch( setSnackBarText({ - text: `Successfully deleted the '${modal.geoDataset.name}' GeoDataset.`, + text: `Successfully deleted the '${modal.dataset.name}' Dataset.`, severity: "success", }) ); - queryGeoDatasets(); + queryDatasets(); handleClose(); } else { dispatch( setSnackBarText({ - text: `Failed to delete the '${modal.geoDataset.name}' GeoDataset.`, + text: `Failed to delete the '${modal.dataset.name}' Dataset.`, severity: "error", }) ); @@ -230,7 +230,7 @@ const DeleteGeoDatasetModal = (props) => { (res) => { dispatch( setSnackBarText({ - text: `Failed to delete the '${modal.geoDataset.name}' GeoDataset.`, + text: `Failed to delete the '${modal.dataset.name}' Dataset.`, severity: "error", }) ); @@ -240,10 +240,10 @@ const DeleteGeoDatasetModal = (props) => { let occurrences = []; - if (modal?.geoDataset?.occurrences) - occurrences = Object.keys(modal?.geoDataset?.occurrences) + if (modal?.dataset?.occurrences) + occurrences = Object.keys(modal?.dataset?.occurrences) .map((mission) => { - const m = modal?.geoDataset?.occurrences[mission]; + const m = modal?.dataset?.occurrences[mission]; if (m.length == 0) return null; else { const items = [
{mission}
]; @@ -277,7 +277,7 @@ const DeleteGeoDatasetModal = (props) => {
-
Delete a GeoDataset
+
Delete a Dataset
{ {`Deleting: ${modal?.geoDataset?.name}`} + >{`Deleting: ${modal?.dataset?.name}`} {occurrences.length > 0 && ( <>
- {`This GeoDataset is currently in use in the following layers:`} + {`This Dataset is currently in use in the following layers:`}
{occurrences}
@@ -306,25 +306,26 @@ const DeleteGeoDatasetModal = (props) => { )} { - setGeoDatasetName(e.target.value); + setDatasetName(e.target.value); }} /> {`Enter '${modal?.geoDataset?.name}' above and click 'Delete' to confirm the permanent deletion of this GeoDataset.`} + >{`Enter '${modal?.dataset?.name}' above and click 'Delete' to confirm the permanent deletion of this Dataset.`}
{ - {`Overwrites the contents of the existing GeoDataset with that of the uploaded file.`} + {`Overwrites the contents of the existing Dataset with that of the uploaded file.`}
@@ -303,7 +349,7 @@ const UpdateGeoDatasetModal = (props) => { {!isDragActive && (

Drag 'n' drop or click to select files...

-

Only *.json and *.geojson files are accepted.

+

Only *.csv files are accepted.

)}
@@ -313,49 +359,21 @@ const UpdateGeoDatasetModal = (props) => {
{fileName || "No File Selected"}
- -
-
- { - setStartTimeField(e.target.value); - }} - /> - - {`If this GeoDataset already has a Start Time Field attached, the name of that start time field inside each feature's "properties" object for which to create a temporal index for the geodataset. `} - -
-
- { - setEndTimeField(e.target.value); - }} - /> - - {`If this GeoDataset already has a End Time Field attached, the name of that end time field inside each feature's "properties" object for which to create a temporal index for the geodataset. This enables time queries on GeoDatasets. `} - -
-
+ {progress != null && ( + + )} ); }; -export default UpdateGeoDatasetModal; +export default UpdateDatasetModal; diff --git a/configure/src/pages/GeoDatasets/GeoDatasets.js b/configure/src/pages/GeoDatasets/GeoDatasets.js index dee4d482..b8fe5fa3 100644 --- a/configure/src/pages/GeoDatasets/GeoDatasets.js +++ b/configure/src/pages/GeoDatasets/GeoDatasets.js @@ -1,9 +1,7 @@ -import React, { useEffect, useState } from "react"; +import React, { useEffect } from "react"; import { useSelector, useDispatch } from "react-redux"; import { makeStyles } from "@mui/styles"; -import clsx from "clsx"; - import { calls } from "../../core/calls"; import { downloadObject } from "../../core/utils"; import { @@ -26,14 +24,9 @@ import TableSortLabel from "@mui/material/TableSortLabel"; import Toolbar from "@mui/material/Toolbar"; import Typography from "@mui/material/Typography"; import Paper from "@mui/material/Paper"; -import Checkbox from "@mui/material/Checkbox"; import IconButton from "@mui/material/IconButton"; import Button from "@mui/material/Button"; import Tooltip from "@mui/material/Tooltip"; -import FormControlLabel from "@mui/material/FormControlLabel"; -import Switch from "@mui/material/Switch"; -import DeleteIcon from "@mui/icons-material/Delete"; -import FilterListIcon from "@mui/icons-material/FilterList"; import Divider from "@mui/material/Divider"; import Badge from "@mui/material/Badge"; import { visuallyHidden } from "@mui/utils"; @@ -42,7 +35,6 @@ import InventoryIcon from "@mui/icons-material/Inventory"; import PreviewIcon from "@mui/icons-material/Preview"; import DownloadIcon from "@mui/icons-material/Download"; import UploadIcon from "@mui/icons-material/Upload"; -import DriveFileRenameOutlineIcon from "@mui/icons-material/DriveFileRenameOutline"; import DeleteForeverIcon from "@mui/icons-material/DeleteForever"; import AddIcon from "@mui/icons-material/Add"; import ShapeLineIcon from "@mui/icons-material/ShapeLine"; @@ -174,15 +166,12 @@ const useStyles = makeStyles((theme) => ({ minHeight: "48px !important", display: "flex", justifyContent: "space-between", - background: theme.palette.swatches.grey[1000], - boxShadow: `inset 10px 0px 10px -5px rgba(0,0,0,0.3)`, - borderBottom: `2px solid ${theme.palette.swatches.grey[800]} !important`, padding: `0px 20px`, boxSizing: `border-box !important`, }, topbarTitle: { display: "flex", - color: theme.palette.accent.main, + color: theme.palette.swatches.grey[150], "& > svg": { color: theme.palette.swatches.grey[150], margin: "3px 10px 0px 2px", diff --git a/configure/src/pages/GeoDatasets/Modals/AppendGeoDatasetModal/AppendGeoDatasetModal.js b/configure/src/pages/GeoDatasets/Modals/AppendGeoDatasetModal/AppendGeoDatasetModal.js index 1a52f3e4..cb96b837 100644 --- a/configure/src/pages/GeoDatasets/Modals/AppendGeoDatasetModal/AppendGeoDatasetModal.js +++ b/configure/src/pages/GeoDatasets/Modals/AppendGeoDatasetModal/AppendGeoDatasetModal.js @@ -3,11 +3,7 @@ import { useSelector, useDispatch } from "react-redux"; import { calls } from "../../../../core/calls"; -import { - setMissions, - setModal, - setSnackBarText, -} from "../../../../core/ConfigureStore"; +import { setModal, setSnackBarText } from "../../../../core/ConfigureStore"; import Typography from "@mui/material/Typography"; import Button from "@mui/material/Button"; @@ -24,9 +20,6 @@ import InsertDriveFileIcon from "@mui/icons-material/InsertDriveFile"; import { useDropzone } from "react-dropzone"; import TextField from "@mui/material/TextField"; -import FormGroup from "@mui/material/FormGroup"; -import FormControlLabel from "@mui/material/FormControlLabel"; -import Checkbox from "@mui/material/Checkbox"; import { makeStyles, useTheme } from "@mui/styles"; import useMediaQuery from "@mui/material/useMediaQuery"; diff --git a/configure/src/pages/GeoDatasets/Modals/LayersUsedByModal/LayersUsedByModal.js b/configure/src/pages/GeoDatasets/Modals/LayersUsedByModal/LayersUsedByModal.js index 558bd613..45130b7b 100644 --- a/configure/src/pages/GeoDatasets/Modals/LayersUsedByModal/LayersUsedByModal.js +++ b/configure/src/pages/GeoDatasets/Modals/LayersUsedByModal/LayersUsedByModal.js @@ -1,9 +1,7 @@ -import React, { useState } from "react"; +import React from "react"; import { useSelector, useDispatch } from "react-redux"; -import { calls } from "../../../../core/calls"; - -import { setModal, setSnackBarText } from "../../../../core/ConfigureStore"; +import { setModal } from "../../../../core/ConfigureStore"; import Typography from "@mui/material/Typography"; import Button from "@mui/material/Button"; @@ -15,10 +13,6 @@ import IconButton from "@mui/material/IconButton"; import CloseSharpIcon from "@mui/icons-material/CloseSharp"; import ShapeLineIcon from "@mui/icons-material/ShapeLine"; -import WarningIcon from "@mui/icons-material/Warning"; -import DeleteForeverIcon from "@mui/icons-material/DeleteForever"; - -import TextField from "@mui/material/TextField"; import { makeStyles, useTheme } from "@mui/styles"; import useMediaQuery from "@mui/material/useMediaQuery"; diff --git a/configure/src/pages/GeoDatasets/Modals/NewGeoDatasetModal/NewGeoDatasetModal.js b/configure/src/pages/GeoDatasets/Modals/NewGeoDatasetModal/NewGeoDatasetModal.js index 3b6d267f..3bf1b8fa 100644 --- a/configure/src/pages/GeoDatasets/Modals/NewGeoDatasetModal/NewGeoDatasetModal.js +++ b/configure/src/pages/GeoDatasets/Modals/NewGeoDatasetModal/NewGeoDatasetModal.js @@ -3,11 +3,7 @@ import { useSelector, useDispatch } from "react-redux"; import { calls } from "../../../../core/calls"; -import { - setMissions, - setModal, - setSnackBarText, -} from "../../../../core/ConfigureStore"; +import { setModal, setSnackBarText } from "../../../../core/ConfigureStore"; import Typography from "@mui/material/Typography"; import Button from "@mui/material/Button"; @@ -24,9 +20,6 @@ import InsertDriveFileIcon from "@mui/icons-material/InsertDriveFile"; import { useDropzone } from "react-dropzone"; import TextField from "@mui/material/TextField"; -import FormGroup from "@mui/material/FormGroup"; -import FormControlLabel from "@mui/material/FormControlLabel"; -import Checkbox from "@mui/material/Checkbox"; import { makeStyles, useTheme } from "@mui/styles"; import useMediaQuery from "@mui/material/useMediaQuery"; diff --git a/configure/src/pages/GeoDatasets/Modals/PreviewGeoDatasetModal/PreviewGeoDatasetModal.js b/configure/src/pages/GeoDatasets/Modals/PreviewGeoDatasetModal/PreviewGeoDatasetModal.js index 61c09848..3c0918e4 100644 --- a/configure/src/pages/GeoDatasets/Modals/PreviewGeoDatasetModal/PreviewGeoDatasetModal.js +++ b/configure/src/pages/GeoDatasets/Modals/PreviewGeoDatasetModal/PreviewGeoDatasetModal.js @@ -5,10 +5,7 @@ import { calls } from "../../../../core/calls"; import { setModal, setSnackBarText } from "../../../../core/ConfigureStore"; -import Typography from "@mui/material/Typography"; -import Button from "@mui/material/Button"; import Dialog from "@mui/material/Dialog"; -import DialogActions from "@mui/material/DialogActions"; import DialogContent from "@mui/material/DialogContent"; import DialogTitle from "@mui/material/DialogTitle"; import IconButton from "@mui/material/IconButton"; diff --git a/configure/src/pages/GeoDatasets/Modals/UpdateGeoDatasetModal/UpdateGeoDatasetModal.js b/configure/src/pages/GeoDatasets/Modals/UpdateGeoDatasetModal/UpdateGeoDatasetModal.js index 49905d69..62e0c68b 100644 --- a/configure/src/pages/GeoDatasets/Modals/UpdateGeoDatasetModal/UpdateGeoDatasetModal.js +++ b/configure/src/pages/GeoDatasets/Modals/UpdateGeoDatasetModal/UpdateGeoDatasetModal.js @@ -3,11 +3,7 @@ import { useSelector, useDispatch } from "react-redux"; import { calls } from "../../../../core/calls"; -import { - setMissions, - setModal, - setSnackBarText, -} from "../../../../core/ConfigureStore"; +import { setModal, setSnackBarText } from "../../../../core/ConfigureStore"; import Typography from "@mui/material/Typography"; import Button from "@mui/material/Button"; @@ -24,9 +20,6 @@ import InsertDriveFileIcon from "@mui/icons-material/InsertDriveFile"; import { useDropzone } from "react-dropzone"; import TextField from "@mui/material/TextField"; -import FormGroup from "@mui/material/FormGroup"; -import FormControlLabel from "@mui/material/FormControlLabel"; -import Checkbox from "@mui/material/Checkbox"; import { makeStyles, useTheme } from "@mui/styles"; import useMediaQuery from "@mui/material/useMediaQuery"; diff --git a/configure/src/pages/WebHooks/WebHooks.js b/configure/src/pages/WebHooks/WebHooks.js index 221faef6..38b4e70d 100644 --- a/configure/src/pages/WebHooks/WebHooks.js +++ b/configure/src/pages/WebHooks/WebHooks.js @@ -1,23 +1,29 @@ -import React, { useEffect, useState } from "react"; +import React, { useEffect } from "react"; import { useSelector, useDispatch } from "react-redux"; import { makeStyles } from "@mui/styles"; import { calls } from "../../core/calls"; +import { getIn } from "../../core/utils"; import Maker from "../../core/Maker"; -import { setSnackBarText } from "../../core/ConfigureStore"; +import { setSnackBarText, setConfiguration } from "../../core/ConfigureStore"; + +import Button from "@mui/material/Button"; + +import SaveIcon from "@mui/icons-material/Save"; +import PhishingIcon from "@mui/icons-material/Phishing"; const config = { rows: [ { - name: "WebHooks", + name: "Webhooks", description: - "Configures the available functionalities of the Map's and Globe's right-click context menu.", + "Trigger HTTP requests whenever certain actions are executed in MMGIS.", components: [ { - field: "webhooks", - name: "Context Menu Actions", + field: "temp.webhooks", + name: "Webhooks", description: - "When right-clicking on the Map or Globe, a custom context-menu appears. By default it only offers 'Copy Coordinates'. By adding objects to the rightClickMenuActions array, entries can be added to the context-menu to send users to links with parameters populated with the current coordinates.", + "Configure HTTP requests whenever certain actions are executed in MMGIS.", type: "objectarray", width: 12, object: [ @@ -25,7 +31,7 @@ const config = { field: "action", name: "Action", description: - "Optionally restrict some right-click menu actions to only be supported when clicking on a polygon.", + "Which action upon which to trigger the webhook request.", type: "dropdown", width: 2, options: ["DrawFileAdd", "DrawFileChange", "DrawFileDelete"], @@ -33,8 +39,7 @@ const config = { { field: "type", name: "HTTP Method", - description: - "Optionally restrict some right-click menu actions to only be supported when clicking on a polygon.", + description: "The HTTP method for which to send the request.", type: "dropdown", width: 2, options: ["GET", "POST", "PUT", "DELETE", "PATCH"], @@ -43,25 +48,27 @@ const config = { field: "url", name: "URL", description: - "The text for this menu entry when users right-click.", + "The URL for which to send the request. Valid injectable variables for URL: {created_on}, {efolders}, {file_description}, {file_id}, {file_name}, {file_owner}, {file_owner_group}, {folders}, {geojson}, {hidden}, {intent}, {is_master}, {public}, {public_editors}, {publicity_type}, {raw_file_description}, {tags}, {template}, {updated_on}.", type: "text", - width: 2, + width: 8, }, { field: "header", name: "Header JSON", description: - "Vector Tiles are styled differently than Vectors. Raw variables here takes an object that maps internal vector tile layer names to styles. All raw variables are optional.", + 'A JSON object of request headers to use with NodeJS fetch. For example: {"Content-Type": "application/json"}', type: "json", width: 12, + height: "100px", }, { field: "body", name: "Body JSON", description: - "Vector Tiles are styled differently than Vectors. Raw variables here takes an object that maps internal vector tile layer names to styles. All raw variables are optional.", + "A JSON object to be the body of the request. MMGIS will auto-replace the following strings contained anywhere in the body before sending it to the URL. Valid injectable variables for Body fields: {created_on}, {efolders}, {file_description}, {file_id}, {file_name}, {file_owner}, {file_owner_group}, {folders}, {geojson}, {hidden}, {intent}, {is_master}, {public}, {public_editors}, {publicity_type}, {raw_file_description}, {tags}, {template}, {updated_on}", type: "json", width: 12, + height: "180px", }, ], }, @@ -76,21 +83,126 @@ const useStyles = makeStyles((theme) => ({ height: "100%", overflowY: "auto", display: "flex", + flexFlow: "column", background: theme.palette.swatches.grey[1000], padding: "0px 32px 64px 32px", boxSizing: "border-box", backgroundImage: "url(configure/build/gridlines.png)", }, + gap: { + height: "64px", + width: "100%", + }, + save: { + margin: "8px !important", + height: "32px", + borderRadius: "3px !important", + background: `${theme.palette.swatches.p[11]} !important`, + color: "white !important", + position: "absolute !important", + bottom: "0px", + right: "5px", + }, })); export default function WebHooks() { const c = useStyles(); + const configuration = useSelector((state) => state.core.configuration); + const dispatch = useDispatch(); + const saveWebHooks = () => { + let webhooks = getIn(configuration, "temp.webhooks", null); + + if (webhooks != null) { + webhooks = JSON.parse(JSON.stringify(webhooks)); + + calls.api( + "webhooks_save", + { + config: JSON.stringify({ webhooks }), + }, + (res) => { + if (res.status === "success") { + dispatch( + setSnackBarText({ + text: "Successfully saved webhooks.", + severity: "success", + }) + ); + } else + dispatch( + setSnackBarText({ + text: res?.message || "Failed to save webhooks.", + severity: "error", + }) + ); + }, + (res) => { + dispatch( + setSnackBarText({ + text: res?.message || "Failed to save webhooks.", + severity: "error", + }) + ); + } + ); + } + }; + const queryWebHooks = () => { + calls.api( + "webhooks_entries", + {}, + (res) => { + if (res.status === "success") { + if (res.body && res.body.entries && res.body.entries.length > 0) { + const config = JSON.parse(res.body.entries[0].config); + dispatch( + setConfiguration({ + temp: { + webhooks: config.webhooks, + }, + }) + ); + } + } else + dispatch( + setSnackBarText({ + text: res?.message || "Failed to get webhooks.", + severity: "error", + }) + ); + }, + (res) => { + dispatch( + setSnackBarText({ + text: res?.message || "Failed to get webhooks.", + severity: "error", + }) + ); + } + ); + }; + useEffect(() => { + queryWebHooks(); + }, []); + return (
+
+
); }