diff --git a/src/api.js b/src/api.js index 7f87ce5..37144ac 100644 --- a/src/api.js +++ b/src/api.js @@ -1,44 +1,20 @@ -import { ApiProxyConnector } from "@elastic/search-ui-elasticsearch-connector"; import { toByteArray } from "base64-js"; -import CustomElasticSearchAPIConnector from "./components/caputlog/CustomElasticSearchAPIConnector"; +import config from "./config"; -const channelFinderURL = import.meta.env.PROD ? import.meta.env.REACT_APP_CF_URL : import.meta.env.REACT_APP_CF_URL_DEV; -const cfMaxResults = parseInt(import.meta.env.REACT_APP_CF_MAX_RESULTS); -const ologURL = import.meta.env.PROD ? import.meta.env.REACT_APP_OLOG_URL : import.meta.env.REACT_APP_OLOG_URL_DEV; -const ologWebURL = import.meta.env.PROD ? import.meta.env.REACT_APP_OLOG_WEB_CLIENT_URL : import.meta.env.REACT_APP_OLOG_WEB_CLIENT_URL_DEV; -const aaViewerURL = import.meta.env.PROD ? import.meta.env.REACT_APP_AA_URL : import.meta.env.REACT_APP_AA_URL_DEV; -const pvwsURL = import.meta.env.PROD ? import.meta.env.REACT_APP_PVWS_URL : import.meta.env.REACT_APP_PVWS_URL_DEV; -const pvwsHTTPURL = import.meta.env.PROD ? import.meta.env.REACT_APP_PVWS_HTTP_URL : import.meta.env.REACT_APP_PVWS_HTTP_URL_DEV; -const serverURL = import.meta.env.PROD ? import.meta.env.REACT_APP_BASE_URL : import.meta.env.REACT_APP_BASE_URL_DEV; -const alarmLogURL = import.meta.env.PROD ? import.meta.env.REACT_APP_AL_URL : import.meta.env.REACT_APP_AL_URL_DEV; -const caputlogURL = import.meta.env.PROD ? import.meta.env.REACT_APP_CAPUTLOG_URL : import.meta.env.REACT_APP_CAPUTLOG_URL_DEV; -const ologStartDays = import.meta.env.REACT_APP_OLOG_START_TIME_DAYS !== '' ? - `&start=${import.meta.env.REACT_APP_OLOG_START_TIME_DAYS}days&end=now` +const cfMaxResults = config.CF_MAX_RESULTS; +const ologStartDays = config.OLOG_START_TIME_DAYS !== null + ? `&start=${config.OLOG_START_TIME_DAYS}days&end=now` : ""; -const ologMaxResults = import.meta.env.REACT_APP_OLOG_MAX_RESULTS !== '' ? - `&size=${import.meta.env.REACT_APP_OLOG_MAX_RESULTS}` +const ologMaxResults = config.OLOG_MAX_RESULTS !== null + ? `&size=${config.OLOG_MAX_RESULTS}` : ""; -const alarmLogStartDays = import.meta.env.REACT_APP_AL_START_TIME_DAYS !== '' ? - `&start=${import.meta.env.REACT_APP_AL_START_TIME_DAYS}days&end=now` +const alarmLogStartDays = config.AL_START_TIME_DAYS !== null + ? `&start=${config.AL_START_TIME_DAYS}days&end=now` : ""; -const alarmLogMaxResults = import.meta.env.REACT_APP_AL_MAX_RESULTS !== '' ? - `&size=${import.meta.env.REACT_APP_AL_MAX_RESULTS}` +const alarmLogMaxResults = config.AL_MAX_RESULTS !== null + ? `&size=${config.AL_MAX_RESULTS}` : ""; -const elasticIndexName = import.meta.env.REACT_APP_ELASTICSEARCH_INDEX_NAME; -const elasticApikey = import.meta.env.REACT_APP_ELASTICSEARCH_API_KEY; -// Choice to use Elasticsearch directly or an API Proxy -const useProxy = import.meta.env.REACT_APP_USE_CAPUT_API_PROXY_CONNNECTOR ?? "false"; -const caputLogConnector = (useProxy === "true") - ? new ApiProxyConnector({ - basePath: `${caputlogURL}` - }) - : CustomElasticSearchAPIConnector({ - host: `${caputlogURL}`, - index: `${elasticIndexName}`, - apiKey: `${elasticApikey}` - }); - function handleParams(params) { let urlParams = { "pvName": "*", "params": "" }; if ("pvName" in params && params.pvName !== "") { @@ -64,11 +40,11 @@ function handleParams(params) { function standardParse(params) { let noWildcard = new Set(["pvStatus", "recordType"]); - if (import.meta.env.REACT_APP_EXTRA_PROP && import.meta.env.REACT_APP_EXTRA_PROP_DROPDOWN_LABELS) { - noWildcard.add(import.meta.env.REACT_APP_EXTRA_PROP); + if (config.EXTRA_PROP && config.EXTRA_PROP_DROPDOWN_LABELS) { + noWildcard.add(config.EXTRA_PROP); } - if (import.meta.env.REACT_APP_SECOND_EXTRA_PROP && import.meta.env.REACT_APP_SECOND_EXTRA_PROP_DROPDOWN_LABELS) { - noWildcard.add(import.meta.env.REACT_APP_SECOND_EXTRA_PROP); + if (config.SECOND_EXTRA_PROP && config.SECOND_EXTRA_PROP_DROPDOWN_LABELS) { + noWildcard.add(config.SECOND_EXTRA_PROP); } let addOn = ""; @@ -111,11 +87,10 @@ function freeformParse(params) { async function queryChannelFinder(params) { let urlParams = handleParams(params); let maxResults = cfMaxResults ? `&~size=${cfMaxResults}` : ""; - let requestURI = `${channelFinderURL}/resources/channels?~name=${urlParams.pvName}${urlParams.params}${maxResults}`; - let options = {}; - options = { method: 'GET' } - if (import.meta.env.REACT_APP_SEND_CREDENTIALS === "true") { - if (import.meta.env.PROD) { + let requestURI = `${config.CF_URL}/resources/channels?~name=${urlParams.pvName}${urlParams.params}${maxResults}`; + let options = { method: 'GET' } + if (config.SEND_CREDENTIALS) { + if (config.IS_PROD) { // credentials header would help if CF, AA, etc are behind a SSO options['credentials'] = 'include'; } @@ -130,9 +105,9 @@ async function queryLog(logType, pvName, extraParams) { let requestURI = "" if (logType === logEnum.ONLINE_LOG) { - requestURI = encodeURI(`${ologURL}/logs/search?text=${pvName}${ologStartDays}${ologMaxResults}`); + requestURI = encodeURI(`${config.OLOG_URL}/logs/search?text=${pvName}${ologStartDays}${ologMaxResults}`); } else if (logType === logEnum.ALARM_LOG) { - requestURI = encodeURI(`${alarmLogURL}/search/alarm?pv=${pvName}${alarmLogStartDays}${alarmLogMaxResults}${extraParams}`); + requestURI = encodeURI(`${config.AL_URL}/search/alarm?pv=${pvName}${alarmLogStartDays}${alarmLogMaxResults}${extraParams}`); } else { return new Promise((resolve, reject) => { reject(); @@ -145,9 +120,9 @@ async function getQueryHelpers(helperType) { let requestURI = "" if (helperType === queryHelperEnum.PROPERTIES) { - requestURI = `${channelFinderURL}/resources/properties` + requestURI = `${config.CF_URL}/resources/properties` } else if (helperType === queryHelperEnum.TAGS) { - requestURI = `${channelFinderURL}/resources/tags` + requestURI = `${config.CF_URL}/resources/tags` } else { return new Promise((resolve, reject) => { reject(); @@ -167,34 +142,34 @@ async function getQueryHelpers(helperType) { async function getCount(params = {}) { let urlParams = handleParams(params) - let requestURI = `${channelFinderURL}/resources/channels/count?~name=${urlParams.pvName}${urlParams.params}`; + let requestURI = `${config.CF_URL}/resources/channels/count?~name=${urlParams.pvName}${urlParams.params}`; return await standardQuery(requestURI); } async function getCFInfo() { - return await standardQuery(channelFinderURL); + return await standardQuery(config.CF_URL); } async function getALInfo() { - const requestURI = alarmLogURL + "/" + const requestURI = config.AL_URL + "/" return await standardQuery(requestURI); } async function getOLOGInfo() { - return await standardQuery(ologURL); + return await standardQuery(config.OLOG_URL); } async function getCaputLogInfo() { - return await standardQuery(caputlogURL); + return await standardQuery(config.CAPUTLOG_URL); } async function getPVWSInfo(path) { - if (pvwsHTTPURL === "") return; - if (pvwsHTTPURL.slice(-1) !== "/") { + if (config.PVWS_HTTP_URL === "") return; + if (config.PVWS_HTTP_URL.slice(-1) !== "/") { path = "/" + path; } - const requestURI = pvwsHTTPURL + path; + const requestURI = config.PVWS_HTTP_URL + path; return await standardQuery(requestURI); } @@ -203,8 +178,8 @@ async function standardQuery(requestURI, options = null, handleData = null) { let error = ""; let errorFlag = false; - if (import.meta.env.REACT_APP_SEND_CREDENTIALS === "true") { - if (import.meta.env.PROD) { + if (config.SEND_CREDENTIALS) { + if (config.IS_PROD) { if (options === null) options = {}; // credentials header would help if CF, AA, etc are behind a SSO options['credentials'] = 'include'; @@ -291,16 +266,16 @@ function parseWebSocketMessage(jsonMessage, fixedPrecision = null) { pvData.pv_value = Array.prototype.slice.call(value_array); } else if ("b64byt" in pvData) { let bytes = toByteArray(pvData.b64byt); - if (import.meta.env.REACT_APP_PVWS_TREAT_BYTE_ARRAY_AS_STRING === "false") { - let value_array = new Uint8Array(bytes.buffer); - pvData.pv_value = Array.prototype.slice.call(value_array); - } else { + if (config.PVWS_TREAT_BYTE_ARRAY_AS_STRING) { try { const decoder = new TextDecoder('utf-8'); pvData.pv_value = decoder.decode(bytes); } catch (error) { console.log("Error decoding byte array: ", error.message); } + } else { + let value_array = new Uint8Array(bytes.buffer); + pvData.pv_value = Array.prototype.slice.call(value_array); } } else if ("b64flt" in pvData) { let bytes = toByteArray(pvData.b64flt); @@ -359,16 +334,8 @@ const api = { OLI_QUERY: getOLOGInfo, CAPUTLOG_QUERY: getCaputLogInfo, PVWSI_QUERY: getPVWSInfo, - CF_URL: channelFinderURL, - OLOG_URL: ologURL, - OLOG_WEB_URL: ologWebURL, - AA_VIEWER: aaViewerURL, - PVWS_URL: pvwsURL, - SERVER_URL: serverURL, LOG_ENUM: logEnum, HELPERS_ENUM: queryHelperEnum, - CAPUTLOG_URL: caputlogURL, - CAPUTLOG_CONNECTOR: caputLogConnector, PARSE_WEBSOCKET_MSG: parseWebSocketMessage, } diff --git a/src/app/App.jsx b/src/app/App.jsx index 5be07a8..2a48ef4 100644 --- a/src/app/App.jsx +++ b/src/app/App.jsx @@ -1,8 +1,9 @@ import './App.css'; import { BrowserRouter as Router, Route, Routes } from "react-router-dom"; import { Grid, Snackbar, Alert } from '@mui/material'; -import React, { useState } from "react"; +import { useState } from "react"; +import config from "../config"; import Home from '../components/home'; import PV from '../components/pv'; import IOC from '../components/ioc'; @@ -37,10 +38,10 @@ function App() { } /> } /> } /> - } /> { - import.meta.env.REACT_APP_USE_CAPUTLOG === "true" ? } /> : null + config.USE_CAPUTLOG ? } /> : null } + } /> } /> } /> diff --git a/src/colors.js b/src/colors.js index f84ff95..84b8f10 100644 --- a/src/colors.js +++ b/src/colors.js @@ -1,9 +1,15 @@ -const okCol = "#" + import.meta.env.REACT_APP_OK_SEVERITY_COLOR; -const minorCol = "#" + import.meta.env.REACT_APP_MINOR_SEVERITY_COLOR; -const majorCol = "#" + import.meta.env.REACT_APP_MAJOR_SEVERITY_COLOR; -const invalidCol = "#" + import.meta.env.REACT_APP_INVALID_SEVERITY_COLOR; -const undefinedCol = "#" + import.meta.env.REACT_APP_UNDEFINED_SEVERITY_COLOR; +import config from "./config"; +const handleColor = (value) => { + if (!value) return null; + return value.startsWith("#") ? value : `#${value}`; +}; + +const okCol = handleColor(config.OK_SEVERITY_COLOR); +const minorCol = handleColor(config.MINOR_SEVERITY_COLOR); +const majorCol = handleColor(config.MAJOR_SEVERITY_COLOR); +const invalidCol = handleColor(config.INVALID_SEVERITY_COLOR); +const undefinedCol = handleColor(config.UNDEFINED_SEVERITY_COLOR); // modBrightness function sourced from https://natclark.com/tutorials/javascript-lighten-darken-hex-color/ const modBrightness = (hexColor, magnitude) => { @@ -39,6 +45,6 @@ const severityColors = { const colors = { SEV_COLORS: severityColors, -} +}; export default colors; diff --git a/src/components/caputlog/CaputLogSearchLayout.jsx b/src/components/caputlog/CaputLogSearchLayout.jsx index 830d9af..ba3bc04 100644 --- a/src/components/caputlog/CaputLogSearchLayout.jsx +++ b/src/components/caputlog/CaputLogSearchLayout.jsx @@ -1,11 +1,13 @@ import React, { useState, useEffect } from "react"; import { Layout } from "@elastic/react-search-ui-views"; import { ErrorBoundary, Facet, PagingInfo, ResultsPerPage, Paging, useSearch, SearchBox } from "@elastic/react-search-ui"; -import { Box, Button, TextField, InputAdornment } from "@mui/material"; +import { Box, Button, TextField, InputAdornment, Tooltip } from "@mui/material"; import CalendarMonthIcon from '@mui/icons-material/CalendarMonth'; +import ClearRoundedIcon from '@mui/icons-material/ClearRounded'; import DatePicker from "react-datepicker"; import "react-datepicker/dist/react-datepicker.css"; import './CaputLogSearchLayout.css'; +import CustomSearchBoxView from "./CustomSearchBoxView"; import moment from "moment"; import PropTypes from "prop-types"; @@ -15,17 +17,19 @@ const propTypes = { field: PropTypes.string.isRequired, label: PropTypes.string.isRequired, })), + initialSearchTerm: PropTypes.string, children: PropTypes.func.isRequired, } function CaputLogSearchLayout({ showSearchBox = false, facetFields = [], + initialSearchTerm = "", children }) { const [startDate, setStartDate] = useState(null); const [endDate, setEndDate] = useState(null); - const { wasSearched, setFilter, removeFilter, results, setSort } = useSearch(); + const { wasSearched, setFilter, removeFilter, results, setSort, setSearchTerm } = useSearch(); // Set initial sort to @timestamp desc on mount useEffect(() => { @@ -49,9 +53,10 @@ function CaputLogSearchLayout({ } }; - const clearDateFilters = () => { + const clearAllFilters = () => { setStartDate(null); setEndDate(null); + setSearchTerm(initialSearchTerm); removeFilter("@timestamp"); }; @@ -67,7 +72,10 @@ function CaputLogSearchLayout({ flexDirection: "column", }} > {showSearchBox && ( - + )} @@ -98,9 +106,17 @@ function CaputLogSearchLayout({ } /> - + + + } diff --git a/src/components/caputlog/CustomSearchBoxView.jsx b/src/components/caputlog/CustomSearchBoxView.jsx new file mode 100644 index 0000000..731f673 --- /dev/null +++ b/src/components/caputlog/CustomSearchBoxView.jsx @@ -0,0 +1,35 @@ +import SearchRoundedIcon from "@mui/icons-material/SearchRounded"; +import { Button, Tooltip, TextField } from "@mui/material"; + +const CustomSearchBoxView = ({ value, onChange, onSubmit }) => ( +
+ onChange(e.target.value)} + placeholder="Search by PV Name, User, or Client" + variant="outlined" + size="small" + /> + + + + +); + +export default CustomSearchBoxView; diff --git a/src/components/caputlog/caputlogSearchConfig.js b/src/components/caputlog/caputlogSearchConfig.js index 5f25957..f20ecf1 100644 --- a/src/components/caputlog/caputlogSearchConfig.js +++ b/src/components/caputlog/caputlogSearchConfig.js @@ -1,9 +1,9 @@ -import api from "../../api"; +import config from "../../config"; export function getCaputLogSearchConfig({ filters = [], search_fields = {}, facets = {}, initialState = {} } = {}) { return { alwaysSearchOnInitialLoad: true, - apiConnector: api.CAPUTLOG_CONNECTOR, + apiConnector: config.CAPUTLOG_CONNECTOR, hasA11yNotifications: true, trackUrlState: false, searchQuery: { diff --git a/src/components/header/Header.jsx b/src/components/header/Header.jsx index 18c2e66..8eaacc4 100644 --- a/src/components/header/Header.jsx +++ b/src/components/header/Header.jsx @@ -9,9 +9,12 @@ import AppsIcon from '@mui/icons-material/Apps'; import SsidChartIcon from '@mui/icons-material/SsidChart'; import HelpIcon from '@mui/icons-material/Help'; import InfoIcon from '@mui/icons-material/Info' +import TroubleshootIcon from '@mui/icons-material/Troubleshoot'; import MenuIcon from '@mui/icons-material/Menu'; import { makeStyles } from 'tss-react/mui'; +import config from '../../config'; + const useStyles = makeStyles()((theme) => ({ root: { flexGrow: 1, @@ -68,10 +71,10 @@ function Header() { - {(import.meta.env.REACT_APP_USE_CAPUTLOG || "").toLowerCase() === "true" && + {config.USE_CAPUTLOG && - + @@ -101,12 +104,12 @@ function Header() { IOCs - {(import.meta.env.REACT_APP_USE_AA || "").toLowerCase() === "true" && + {config.USE_AA && Plotting } - {(import.meta.env.REACT_APP_USE_CAPUTLOG || "").toLowerCase() === "true" && + {config.USE_CAPUTLOG && Caput Log diff --git a/src/components/help/Help.jsx b/src/components/help/Help.jsx index fe6b195..9ef808a 100644 --- a/src/components/help/Help.jsx +++ b/src/components/help/Help.jsx @@ -2,7 +2,7 @@ import React, { Fragment } from "react"; import { Typography, Grid, Link } from "@mui/material"; import { Link as RouterLink } from "react-router-dom"; import archivePlotOptions from "../../assets/archive-options.png"; - +import config from "../../config"; function Help() { @@ -18,7 +18,7 @@ function Help() { Process Variable (PV) is the name applied to the Real-Time Database Records that contain control system information. Like most Database records it has a set of fields. Unlike conventional database records, Process Variables are 'connected' to the 'real world', - so they reflect or control values in the control system. For example, here is a PV at our facility: {import.meta.env.REACT_APP_HELP_EXAMPLE_PV} + so they reflect or control values in the control system. For example, here is a PV at our facility: {config.HELP_EXAMPLE_PV} The Control System is based on the Experimental Physics and Industrial Control System @@ -88,7 +88,7 @@ function Help() {
  • Current status of this PV. Either Active or Inactive. Inactive means the PV is not synchronized with the recsync server and indicates a problem with the IOC or recsync. - { import.meta.env.REACT_APP_PVWS_IGNORE_CF_PVSTATUS !== "true" && + { !config.PVWS_IGNORE_CF_PVSTATUS &&
  • If the PV is inactive then the PV value monitor checkbox will be disabled.
  • } @@ -105,22 +105,22 @@ function Help() { PVs can have alias names. You can click on the alias name to open the details page for the alias (will have the same information as the real PV name). - {import.meta.env.REACT_APP_EXTRA_PROP.length > 0 && + {config.EXTRA_PROP && config.EXTRA_PROP.length > 0 &&
    -
  • {import.meta.env.REACT_APP_EXTRA_PROP_LABEL}
  • +
  • {config.EXTRA_PROP_LABEL}
    • - {import.meta.env.REACT_APP_EXTRA_PROP_HELP_TEXT} + {config.EXTRA_PROP_HELP_TEXT}
    } - {import.meta.env.REACT_APP_SECOND_EXTRA_PROP.length > 0 && + {config.SECOND_EXTRA_PROP && config.SECOND_EXTRA_PROP.length > 0 &&
    -
  • {import.meta.env.REACT_APP_SECOND_EXTRA_PROP_LABEL}
  • +
  • {config.SECOND_EXTRA_PROP_LABEL}
    • - {import.meta.env.REACT_APP_SECOND_EXTRA_PROP_HELP_TEXT} + {config.SECOND_EXTRA_PROP_HELP_TEXT}
    diff --git a/src/components/home/Home.jsx b/src/components/home/Home.jsx index 20c5669..ef8083a 100644 --- a/src/components/home/Home.jsx +++ b/src/components/home/Home.jsx @@ -11,6 +11,7 @@ import ParamSearch from './paramsearch'; import FreeSearch from './freesearch'; // is this needed? import './Home.css'; +import config from "../../config"; const propTypes = { handleOpenErrorAlert: PropTypes.func, @@ -32,8 +33,8 @@ function Home(props) { const [recordTypeAutocompleteKey, setRecordTypeAutocompleteKey] = useState(-1); const [recordDesc, setRecordDesc] = useState(searchParams.get("recordDesc") || ""); const [freeformQuery, setFreeformQuery] = useState(""); - const extraPropAName = import.meta.env.REACT_APP_EXTRA_PROP === "" ? null : import.meta.env.REACT_APP_EXTRA_PROP; - const extraPropBName = import.meta.env.REACT_APP_SECOND_EXTRA_PROP === "" ? null : import.meta.env.REACT_APP_SECOND_EXTRA_PROP; + const extraPropAName = config.EXTRA_PROP === "" || config.EXTRA_PROP === null ? null : config.EXTRA_PROP; + const extraPropBName = config.SECOND_EXTRA_PROP === "" || config.SECOND_EXTRA_PROP === null ? null : config.SECOND_EXTRA_PROP; const [extraPropAValue, setExtraPropAValue] = useState(searchParams.get(extraPropAName) || ""); const [extraPropBValue, setExtraPropBValue] = useState(searchParams.get(extraPropBName) || ""); @@ -177,20 +178,20 @@ function Home(props) { if (e.target.hostName.value) { params[e.target.hostName.name] = e.target.hostName.value; } if (e.target.iocName.value) { params[e.target.iocName.name] = e.target.iocName.value; } if (e.target.pvStatus.value !== "*") { params[e.target.pvStatus.name] = e.target.pvStatus.value; } - if (import.meta.env.REACT_APP_CF_RECORD_TYPE === "true") { + if (config.CF_RECORD_TYPE) { if (e.target.recordType.value) { params[e.target.recordType.name] = e.target.recordType.value; } } - if (import.meta.env.REACT_APP_CF_ALIAS === "true") { + if (config.CF_RECORD_ALIAS) { if (e.target.alias.value) { params[e.target.alias.name] = e.target.alias.value; } } - if (import.meta.env.REACT_APP_CF_RECORD_DESC === "true") { + if (config.CF_RECORD_DESC) { if (e.target.recordDesc.value) { params[e.target.recordDesc.name] = e.target.recordDesc.value; } } if (extraPropAName !== null) { - if (e.target[extraPropAName].value) { params[import.meta.env.REACT_APP_EXTRA_PROP] = e.target[extraPropAName].value; } + if (e.target[extraPropAName].value) { params[config.EXTRA_PROP] = e.target[extraPropAName].value; } } if (extraPropBName !== null) { - if (e.target[extraPropBName].value) { params[import.meta.env.REACT_APP_SECOND_EXTRA_PROP] = e.target[extraPropBName].value; } + if (e.target[extraPropBName].value) { params[config.SECOND_EXTRA_PROP] = e.target[extraPropBName].value; } } return params; } @@ -254,14 +255,14 @@ function Home(props) { align="center" variant="h6" > - {import.meta.env.REACT_APP_HOMEPAGE_HEADER} + {config.HOMEPAGE_HEADER}
    - {import.meta.env.REACT_APP_HOMEPAGE_SUBHEADER} + {config.HOMEPAGE_SUBHEADER} diff --git a/src/components/home/paramsearch/ParamSearch.jsx b/src/components/home/paramsearch/ParamSearch.jsx index 6f2babe..259306b 100644 --- a/src/components/home/paramsearch/ParamSearch.jsx +++ b/src/components/home/paramsearch/ParamSearch.jsx @@ -3,6 +3,7 @@ import { Grid, TextField, MenuItem, Tooltip } from '@mui/material'; import SearchActions from '../searchactions/SearchActions'; import Autocomplete from '@mui/material/Autocomplete'; import PropTypes from "prop-types"; +import config from "../../../config"; const propTypes = { pvName: PropTypes.string, @@ -43,7 +44,7 @@ const pvRecordTypes = ['aai', 'aao', 'ai', 'ao', 'aSub', 'bi', 'bo', 'calcout', function ParamSearch(props) { const recordDescSearchRender = () => { - if (import.meta.env.REACT_APP_CF_RECORD_DESC === "true") { + if (config.CF_RECORD_DESC) { return ( * for any # character wildcard
    ? for single character wildcard
    ! at beginning for not equal
    = at beginning for exactly equal}> { - if (import.meta.env.REACT_APP_CF_RECORD_TYPE === "true") { + if (config.CF_RECORD_TYPE) { return ( // * for any # character wildcard
    ? for single character wildcard
    ! at beginning for not equal
    = at beginning for exactly equal}> @@ -93,7 +94,7 @@ function ParamSearch(props) { } } const aliasOfSearchRender = () => { - if (import.meta.env.REACT_APP_CF_ALIAS === "true") { + if (config.CF_RECORD_ALIAS) { return ( * for any # character wildcard
    ? for single character wildcard
    ! at beginning for not equal
    = at beginning for exactly equal}> { if (props.extraPropAName !== null) { - if (import.meta.env.REACT_APP_EXTRA_PROP_DROPDOWN_LABELS !== "") { + if (config.EXTRA_PROP_DROPDOWN_LABELS.length > 0) { return ( // * for any # character wildcard
    ? for single character wildcard
    ! at beginning for not equal
    = at beginning for exactly equal}> @@ -122,16 +123,16 @@ function ParamSearch(props) { sx={{ display: "flex", flexGrow: 1, minWidth: { xs: '50%', md: '25%', lg: '8%' } }} freeSolo disableClearable - options={import.meta.env.REACT_APP_EXTRA_PROP_DROPDOWN_LABELS.split(",")} + options={config.EXTRA_PROP_DROPDOWN_LABELS} key={props.recordTypeAutocompleteKey} value={props.extraPropAValue} renderInput={(params) => { if (props.extraPropBName !== null) { - if (import.meta.env.REACT_APP_SECOND_EXTRA_PROP_DROPDOWN_LABELS !== "") { + if (config.SECOND_EXTRA_PROP_DROPDOWN_LABELS.length > 0) { return ( // * for any # character wildcard
    ? for single character wildcard
    ! at beginning for not equal
    = at beginning for exactly equal}> @@ -172,16 +173,16 @@ function ParamSearch(props) { sx={{ display: "flex", flexGrow: 1, minWidth: { xs: '50%', md: '25%', lg: '8%' } }} freeSolo disableClearable - options={import.meta.env.REACT_APP_SECOND_EXTRA_PROP_DROPDOWN_LABELS.split(",")} + options={config.SECOND_EXTRA_PROP_DROPDOWN_LABELS} key={props.recordTypeAutocompleteKey} value={props.extraPropBValue} renderInput={(params) => item.trim().length && !isNaN(item)).map(Number); - if (!tablePageSizeOptions.includes(pageSizeEnvValue)) { - tablePageSizeOptions.push(pageSizeEnvValue); - } + const defaultTableDensity = config.RESULTS_TABLE_DENSITY; + let tablePageSizeOptions = config.RESULTS_TABLE_SIZE_OPTIONS; + if (!tablePageSizeOptions.includes(pageSizeEnvValue)) { + tablePageSizeOptions.push(pageSizeEnvValue); tablePageSizeOptions.sort(function (a, b) { return a - b; }); } const [columnVisibilityModel, setColumnVisibilityModel] = useState(); @@ -38,8 +36,8 @@ function QueryResults(props) { const [checked, setChecked] = useState([]); const [currentChecked, setCurrentChecked] = useState(new Set()); const [monitorAllChecked, setMonitorAllChecked] = useState(false); - const liveMonitorMax = import.meta.env.REACT_APP_LIVE_MONITOR_MAX ? import.meta.env.REACT_APP_LIVE_MONITOR_MAX : 100; - const liveMonitorWarn = import.meta.env.REACT_APP_LIVE_MONITOR_WARN ? import.meta.env.REACT_APP_LIVE_MONITOR_WARN : 50; + const liveMonitorMax = config.LIVE_MONITOR_MAX; + const liveMonitorWarn = config.LIVE_MONITOR_WARN; let { handleErrorMessage, handleOpenErrorAlert, handleSeverity } = props; @@ -92,7 +90,7 @@ function QueryResults(props) { for (let i = firstRow; i <= lastRow; ++i) { // only check mark active PVs, unless we are ignoring CF PV status // matters now that we show "Disconnected" for PVs that are not connected to their IOC - if (import.meta.env.REACT_APP_PVWS_IGNORE_CF_PVSTATUS === "true" || pvs[i].pvStatus === "Active") { + if (config.PVWS_IGNORE_CF_PVSTATUS || pvs[i].pvStatus === "Active") { updateCurrentChecked(i, event.target.checked); } } @@ -153,14 +151,13 @@ function QueryResults(props) { color="primary" target="_blank" variant="contained" - href={encodeURI(`${api.AA_VIEWER}?pv=${params.row.name}`)} + href={encodeURI(`${config.AA_VIEWER}?pv=${params.row.name}`)} size="large">
    { - // if PVWS is on, show checkbox for live monitoring, else nothing - import.meta.env.REACT_APP_USE_PVWS === "true" ? :
    @@ -174,7 +171,7 @@ function QueryResults(props) {
    Actions { - import.meta.env.REACT_APP_USE_PVWS === "true" ? + config.USE_PVWS ? :
    @@ -208,7 +205,8 @@ function QueryResults(props) { } const renderValue = (params) => { - if (import.meta.env.REACT_APP_USE_PVWS === "true") { + // if PVWS is on, show checkbox for live monitoring, else nothing + if (config.USE_PVWS) { return ( ); @@ -223,7 +221,7 @@ function QueryResults(props) { let columns = [ { field: "name", headerName: 'PV Name', flex: 15, minWidth: 175, maxWidth: 300, renderCell: renderPVNameLink } ] - if (import.meta.env.REACT_APP_CF_RECORD_DESC === "true") { + if (config.CF_RECORD_DESC) { columns.push({ field: "recordDesc", headerName: 'Description', flex: 18, minWidth: 175, maxWidth: 300 }) } columns.push( @@ -231,19 +229,19 @@ function QueryResults(props) { { field: "iocName", headerName: 'IOC', flex: 8.5, minWidth: 125, maxWidth: 170, renderCell: renderIOCLink }, { field: "pvStatus", headerName: 'Status', flex: 7.5, minWidth: 125, maxWidth: 170 } ) - if (import.meta.env.REACT_APP_CF_RECORD_TYPE === "true") { + if (config.CF_RECORD_TYPE) { columns.push({ field: "recordType", headerName: 'Type', flex: 7.5, minWidth: 125, maxWidth: 170 }) } - if (import.meta.env.REACT_APP_EXTRA_PROP_SHOW_IN_RESULTS.toLowerCase() === "true" && import.meta.env.REACT_APP_EXTRA_PROP !== "") { - columns.push({ field: import.meta.env.REACT_APP_EXTRA_PROP, headerName: import.meta.env.REACT_APP_EXTRA_PROP_LABEL, flex: 7.5, minWidth: 125, maxWidth: 170 }) + if (config.EXTRA_PROP_SHOW_IN_RESULTS && config.EXTRA_PROP) { + columns.push({ field: config.EXTRA_PROP, headerName: config.EXTRA_PROP_LABEL, flex: 7.5, minWidth: 125, maxWidth: 170 }) } - if (import.meta.env.REACT_APP_SECOND_EXTRA_PROP_SHOW_IN_RESULTS.toLowerCase() === "true" && import.meta.env.REACT_APP_SECOND_EXTRA_PROP !== "") { - columns.push({ field: import.meta.env.REACT_APP_SECOND_EXTRA_PROP, headerName: import.meta.env.REACT_APP_SECOND_EXTRA_PROP_LABEL, flex: 7.5, minWidth: 125, maxWidth: 170 }) + if (config.SECOND_EXTRA_PROP_SHOW_IN_RESULTS && config.SECOND_EXTRA_PROP) { + columns.push({ field: config.SECOND_EXTRA_PROP, headerName: config.SECOND_EXTRA_PROP_LABEL, flex: 7.5, minWidth: 125, maxWidth: 170 }) } - if (import.meta.env.REACT_APP_CF_ALIAS === "true") { + if (config.CF_RECORD_ALIAS) { columns.push({ field: "alias", headerName: 'Alias Of', flex: 10.5, minWidth: 175, maxWidth: 300, renderCell: renderAliasLink }) } - if (import.meta.env.REACT_APP_USE_PVWS === "true") { + if (config.USE_PVWS) { columns.push({ field: "value", headerName: 'Value', flex: 7.5, minWidth: 120, maxWidth: 140, renderCell: renderValue }) } columns.push({ field: "button", headerName: 'Actions', flex: 9.5, minWidth: 160, maxWidth: 200, disableClickEventBubbling: true, renderCell: renderButtons, renderHeader: renderActionsHeader }) @@ -271,16 +269,16 @@ function QueryResults(props) { } useEffect(() => { - setCurrentBreakpoint(parseInt(import.meta.env.REACT_APP_LG_BREAKPOINT)); - setPrevBreakpoint(parseInt(import.meta.env.REACT_APP_LG_BREAKPOINT)); + setCurrentBreakpoint(config.LG_BREAKPOINT); + setPrevBreakpoint(config.LG_BREAKPOINT); }, []); useEffect(() => { const handleWindowResize = () => { const windowWidth = window.innerWidth; - const sm = import.meta.env.REACT_APP_SM_BREAKPOINT ? parseInt(import.meta.env.REACT_APP_SM_BREAKPOINT) : 600; - const md = import.meta.env.REACT_APP_MD_BREAKPOINT ? parseInt(import.meta.env.REACT_APP_MD_BREAKPOINT) : 900; - const lg = import.meta.env.REACT_APP_LG_BREAKPOINT ? parseInt(import.meta.env.REACT_APP_LG_BREAKPOINT) : 1536; + const sm = config.SM_BREAKPOINT; + const md = config.MD_BREAKPOINT; + const lg = config.LG_BREAKPOINT; const breakpoint = roundToBreakpoint(windowWidth, [0, sm, md, lg]); setCurrentBreakpoint(breakpoint); @@ -288,9 +286,9 @@ function QueryResults(props) { if (breakpoint !== prevBreakpoint) { setPrevBreakpoint(currentBreakpoint); - const omitExtraSmall = import.meta.env.REACT_APP_OMIT_IN_TABLE_X_SMALL ? import.meta.env.REACT_APP_OMIT_IN_TABLE_X_SMALL.split(',').map(item => item.trim()) : []; - const omitSmall = import.meta.env.REACT_APP_OMIT_IN_TABLE_SMALL ? import.meta.env.REACT_APP_OMIT_IN_TABLE_SMALL.split(',').map(item => item.trim()) : []; - const omitMedium = import.meta.env.REACT_APP_OMIT_IN_TABLE_MEDIUM ? import.meta.env.REACT_APP_OMIT_IN_TABLE_MEDIUM.split(',').map(item => item.trim()) : []; + const omitExtraSmall = config.OMIT_IN_TABLE_X_SMALL; + const omitSmall = config.OMIT_IN_TABLE_SMALL; + const omitMedium = config.OMIT_IN_TABLE_MEDIUM; if (windowWidth <= sm) { for (let i = 0; i < omitExtraSmall.length; ++i) { diff --git a/src/components/home/queryresults/value/Value.jsx b/src/components/home/queryresults/value/Value.jsx index c2b5a22..32da244 100644 --- a/src/components/home/queryresults/value/Value.jsx +++ b/src/components/home/queryresults/value/Value.jsx @@ -1,6 +1,7 @@ import React, { useState, useEffect } from "react"; import useWebSocket from 'react-use-websocket'; import api from '../../../../api'; +import config from "../../../../config"; import colors from '../../../../colors'; import PropTypes from "prop-types"; @@ -22,7 +23,7 @@ function Value(props) { // https://github.com/robtaussig/react-use-websocket/issues/40#issuecomment-616676102 // pattern for sharing web socket among components - const { lastJsonMessage } = useWebSocket(api.PVWS_URL, { + const { lastJsonMessage } = useWebSocket(config.PVWS_URL, { share: true, filter: message => JSON.parse(message.data).pv === props.pvName, }); @@ -34,7 +35,7 @@ function Value(props) { return; } // the debouncing is only useful if we are showing "Disconnected" for non-reachable PVs - if (import.meta.env.REACT_APP_SHOW_DISCONNECTED !== "true" || show) { + if (!config.SHOW_DISCONNECTED || show) { return; } const timeout = setTimeout(() => { @@ -93,7 +94,7 @@ function Value(props) { ); } } else { - if (import.meta.env.REACT_APP_SHOW_DISCONNECTED !== "true") { + if (!config.SHOW_DISCONNECTED) { return (
    ); diff --git a/src/components/home/queryresults/valuecheckbox/ValueCheckbox.jsx b/src/components/home/queryresults/valuecheckbox/ValueCheckbox.jsx index 934ce81..11cd193 100644 --- a/src/components/home/queryresults/valuecheckbox/ValueCheckbox.jsx +++ b/src/components/home/queryresults/valuecheckbox/ValueCheckbox.jsx @@ -2,6 +2,7 @@ import React, { useEffect, useState } from 'react'; import useWebSocket from 'react-use-websocket'; import { Checkbox, Tooltip } from '@mui/material'; import api from '../../../../api'; +import config from '../../../../config'; import PropTypes from "prop-types"; const propTypes = { @@ -19,7 +20,7 @@ function ValueCheckbox(props) { // filter all messages as false since we don't need to read anything in parent component // This still is in QueryResults so even with USE_PVWS===false it will try to connect, would be better to move to ValueCheckbox? const [enabled, setEnabled] = useState(true); - const { sendJsonMessage } = useWebSocket(api.PVWS_URL, { + const { sendJsonMessage } = useWebSocket(config.PVWS_URL, { share: true, filter: (message) => false, }); @@ -29,10 +30,10 @@ function ValueCheckbox(props) { } useEffect(() => { - if (props.pvStatus !== "Active" && import.meta.env.REACT_APP_PVWS_IGNORE_CF_PVSTATUS !== "true") { + if (props.pvStatus !== "Active" && !config.PVWS_IGNORE_CF_PVSTATUS) { setEnabled(false); } - else if (props.recordType === "waveform" && import.meta.env.REACT_APP_PVWS_ALLOW_WAVEFORMS !== "true") { + else if (props.recordType === "waveform" && !config.PVWS_ALLOW_WAVEFORMS) { setEnabled(false); } else { diff --git a/src/components/plot/Plot.jsx b/src/components/plot/Plot.jsx index a72a080..646045b 100644 --- a/src/components/plot/Plot.jsx +++ b/src/components/plot/Plot.jsx @@ -4,14 +4,16 @@ import { Button, Typography, Grid } from "@mui/material"; import { Table, TableCell, TableHead, TableBody, TableRow, TableContainer } from "@mui/material"; import TimelineIcon from '@mui/icons-material/Timeline'; import api from "../../api"; +import config from "../../config"; + function Plot() { const getAAPolicies = () => { - if(import.meta.env.REACT_APP_AA_POLICIES.length <= 0) { + if(config.AA_POLICIES.length <= 0) { return []; } let aaPolicies = []; - let policyList = import.meta.env.REACT_APP_AA_POLICIES.split(","); + let policyList = config.AA_POLICIES; policyList.forEach(policy => { let policyDetails = policy.split("|"); let policyMap = {}; @@ -27,7 +29,7 @@ function Plot() { return ( - + Archiver Storage Policies diff --git a/src/components/pv/PV.jsx b/src/components/pv/PV.jsx index 852f685..612e762 100644 --- a/src/components/pv/PV.jsx +++ b/src/components/pv/PV.jsx @@ -11,6 +11,7 @@ import AlarmLogTable from "./alarmlogtable/AlarmLogTable"; import AlarmConfigTable from "./alarmconfigtable/AlarmConfigTable"; import CaputLogTable from "./caputlogtable/CaputLogTable"; import PropTypes from "prop-types"; +import config from "../../config"; const propTypes = { handleOpenErrorAlert: PropTypes.func, @@ -25,24 +26,24 @@ function PV(props) { const [pvMonitoring, setPVMonitoring] = useState(false); const [snapshot, setSnapshot] = useState(true); const [isLoading, setIsLoading] = useState(true); - const pvHTMLString = encodeURI(`${api.AA_VIEWER}?pv=${id}`); + const pvHTMLString = encodeURI(`${config.AA_VIEWER}?pv=${id}`); const [displayAllVars, setDisplayAllVars] = useState(false); const [dataNamesMapping, setDataNamesMapping] = useState({}); const [dataOrder, setDataOrder] = useState([]) const [detailsExpanded, setDetailsExpanded] = useState(true); let { handleErrorMessage, handleOpenErrorAlert, handleSeverity } = props; - const omitVariables = import.meta.env.REACT_APP_DETAILS_PAGE_PROPERTIES_BLOCKLIST ? new Set(import.meta.env.REACT_APP_DETAILS_PAGE_PROPERTIES_BLOCKLIST.split(',').map(item => item.trim())) : new Set(); + const omitVariables = config.DETAILS_PAGE_PROPERTIES_BLOCKLIST ? new Set(config.DETAILS_PAGE_PROPERTIES_BLOCKLIST) : new Set(); const handleDetailExpandedChange = () => (event, isExpanded) => { setDetailsExpanded(isExpanded); } - // Parse environment variable REACT_APP_DETAILS_PAGE_PROPERTIES to determine what variables to display + // Parse config.DETAILS_PAGE_PROPERTIES to determine what variables to display useEffect(() => { const dict = {} let order = [] - let dataNames = import.meta.env.REACT_APP_DETAILS_PAGE_PROPERTIES ? import.meta.env.REACT_APP_DETAILS_PAGE_PROPERTIES : ""; + let dataNames = config.DETAILS_PAGE_PROPERTIES ? config.DETAILS_PAGE_PROPERTIES : ""; dataNames.split(',').forEach((pair) => { if (pair === "*") { setDisplayAllVars(true); @@ -119,7 +120,7 @@ function PV(props) { return; } if (Object.keys(pvData).length !== 0) { - if (import.meta.env.REACT_APP_DEFAULT_LIVE_MONITORING === "true") { + if (config.DEFAULT_LIVE_MONITORING) { setPVMonitoring(true); setSnapshot(false); } else { @@ -171,14 +172,14 @@ function PV(props) { {id} { - import.meta.env.REACT_APP_USE_AA === "true" ? :
    + config.USE_AA ? :
    }
    { - import.meta.env.REACT_APP_USE_PVWS === "true" ? + config.USE_PVWS ? } - disabled={!((import.meta.env.REACT_APP_PVWS_IGNORE_CF_PVSTATUS === "true" || pvData?.pvStatus?.value === "Active") && (import.meta.env.REACT_APP_PVWS_ALLOW_WAVEFORMS === "true" || pvData?.recordType?.value !== "waveform"))} + disabled={!((config.PVWS_IGNORE_CF_PVSTATUS || pvData?.pvStatus?.value === "Active") && (config.PVWS_ALLOW_WAVEFORMS || pvData?.recordType?.value !== "waveform"))} label="Enable Live PV Monitoring" /> : null @@ -218,7 +219,7 @@ function PV(props) { ) : (null)} { - import.meta.env.REACT_APP_USE_PVWS === "true" ? + config.USE_PVWS ? @@ -229,16 +230,16 @@ function PV(props) { { - import.meta.env.REACT_APP_USE_OLOG === "true" ? : null + config.USE_OLOG ? : null } { - import.meta.env.REACT_APP_USE_AL === "true" ? : null + config.USE_AL ? : null } { - import.meta.env.REACT_APP_USE_AL === "true" ? : null + config.USE_AL ? : null } { - import.meta.env.REACT_APP_USE_CAPUTLOG === "true" ? : null + config.USE_CAPUTLOG ? : null }
    diff --git a/src/components/pv/alarmconfigtable/AlarmConfigTable.jsx b/src/components/pv/alarmconfigtable/AlarmConfigTable.jsx index 6cbf559..ca975aa 100644 --- a/src/components/pv/alarmconfigtable/AlarmConfigTable.jsx +++ b/src/components/pv/alarmconfigtable/AlarmConfigTable.jsx @@ -4,6 +4,7 @@ import { CustomTableContainer, TableHeaderCell } from "../customtablecells/Custo import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; import api from "../../../api"; import PropTypes from "prop-types"; +import config from "../../../config"; const propTypes = { pvName: PropTypes.string @@ -65,8 +66,8 @@ function AlarmConfigTable(props) { {noConfigMessage} { - import.meta.env.REACT_APP_AL_START_TIME_DAYS !== '' ? -  Within {import.meta.env.REACT_APP_AL_START_TIME_DAYS} Days + config.AL_START_TIME_DAYS !== null ? +  Within {config.AL_START_TIME_DAYS} Days : null } diff --git a/src/components/pv/alarmlogtable/AlarmLogTable.jsx b/src/components/pv/alarmlogtable/AlarmLogTable.jsx index 4e0236f..6eeaccb 100644 --- a/src/components/pv/alarmlogtable/AlarmLogTable.jsx +++ b/src/components/pv/alarmlogtable/AlarmLogTable.jsx @@ -5,6 +5,7 @@ import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; import api from "../../../api"; import colors from "../../../colors"; import PropTypes from "prop-types"; +import config from "../../../config"; const propTypes = { pvName: PropTypes.string, @@ -66,8 +67,8 @@ function AlarmLogTable(props) { {noAlarmMessage} { - import.meta.env.REACT_APP_AL_START_TIME_DAYS !== '' ? -  Within {import.meta.env.REACT_APP_AL_START_TIME_DAYS} Days + config.AL_START_TIME_DAYS !== null ? +  Within {config.AL_START_TIME_DAYS} Days : null } diff --git a/src/components/pv/caputlogtable/CaputLogTable.jsx b/src/components/pv/caputlogtable/CaputLogTable.jsx index 46d79ba..15892c5 100644 --- a/src/components/pv/caputlogtable/CaputLogTable.jsx +++ b/src/components/pv/caputlogtable/CaputLogTable.jsx @@ -33,6 +33,7 @@ function CaputLogTable(props) { {noOlogMessage} { - import.meta.env.REACT_APP_OLOG_START_TIME_DAYS !== '' ? -  Within {import.meta.env.REACT_APP_OLOG_START_TIME_DAYS} Days + config.OLOG_START_TIME_DAYS !== null ? +  Within {config.OLOG_START_TIME_DAYS} Days : null } @@ -89,7 +89,7 @@ function OLOGTable(props) { key={row.id} sx={{ '&:last-child td, &:last-child th': { border: 0 } }} > - {new Date(row.createdDate).toLocaleString()} + {new Date(row.createdDate).toLocaleString()} { row.tags.map(function (tag, index) { diff --git a/src/components/pv/valuetable/ValueTable.jsx b/src/components/pv/valuetable/ValueTable.jsx index 7a7f928..0d81970 100644 --- a/src/components/pv/valuetable/ValueTable.jsx +++ b/src/components/pv/valuetable/ValueTable.jsx @@ -5,6 +5,7 @@ import api from "../../../api"; import colors from "../../../colors"; import PropTypes from "prop-types"; import KeyValuePair from "../KeyValuePair"; +import config from "../../../config"; const propTypes = { pvMonitoring: PropTypes.bool, @@ -34,7 +35,7 @@ function ValueTable(props) { const [snapshot, setSnapshot] = useState(true); const [subscribed, setSubscribed] = useState(false); - const { sendJsonMessage, lastJsonMessage } = useWebSocket(api.PVWS_URL, { + const { sendJsonMessage, lastJsonMessage } = useWebSocket(config.PVWS_URL, { onClose: () => { if (props.snapshot && !props.pvMonitoring) return; setPVValue(null); @@ -57,12 +58,12 @@ function ValueTable(props) { } const status = props.pvData.pvStatus?.value || "Unknown"; - if (import.meta.env.REACT_APP_PVWS_ALLOW_WAVEFORMS !== "true" && props.pvData.recordType?.value === "waveform") { + if (!config.PVWS_ALLOW_WAVEFORMS && props.pvData.recordType?.value === "waveform") { handleErrorMessage("Can't show live PV values - Waveform record type not supported"); handleSeverity("warning"); handleOpenErrorAlert(true); } - else if (import.meta.env.REACT_APP_PVWS_IGNORE_CF_PVSTATUS === "true" || status === "Active") { + else if (config.PVWS_IGNORE_CF_PVSTATUS || status === "Active") { if (!subscribed) { sendJsonMessage({ "type": "subscribe", "pvs": [props.pvName] }); setSubscribed(true); diff --git a/src/components/services/Services.jsx b/src/components/services/Services.jsx index fb7dfe7..8ec56d6 100644 --- a/src/components/services/Services.jsx +++ b/src/components/services/Services.jsx @@ -3,6 +3,10 @@ import useWebSocket from "react-use-websocket"; import Service from "./service"; import api from "../../api"; import { Box, Grid, Typography } from "@mui/material"; +import { Accordion, AccordionSummary, AccordionDetails } from "@mui/material"; +import { Table, TableBody, TableCell, TableRow } from "@mui/material"; +import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; +import config from "../../config"; function Services() { const [pvCount, setPVCount] = useState(null); @@ -19,13 +23,7 @@ function Services() { const [caputLogConnected, setCaputLogConnected] = useState(false); const [caputLogData, setCaputLogData] = useState(false); - const useAL = import.meta.env.REACT_APP_USE_AL; - const useOLOG = import.meta.env.REACT_APP_USE_OLOG; - const usePVWS = import.meta.env.REACT_APP_USE_PVWS; - const useCaputlog = import.meta.env.REACT_APP_USE_CAPUTLOG; - const isCaputLogProxy = import.meta.env.REACT_APP_USE_CAPUT_API_PROXY_CONNNECTOR; - - const { sendJsonMessage, lastJsonMessage } = useWebSocket(api.PVWS_URL, { + const { sendJsonMessage, lastJsonMessage } = useWebSocket(config.PVWS_URL, { shouldReconnect: (closeEvent) => true }) @@ -115,9 +113,9 @@ function Services() { Services - PV Info Version: {import.meta.env.REACT_APP_VERSION} - Commit Hash: {import.meta.env.REACT_APP_GIT_SHORT_HASH} - Commit Date: {import.meta.env.REACT_APP_GIT_COMMIT_DATE} + PV Info Version: {config.VERSION} + Commit Hash: {config.GIT_SHORT_HASH} + Commit Date: {config.GIT_COMMIT_DATE} @@ -131,7 +129,7 @@ function Services() { }} /> { - useAL === "true" ? ( + config.USE_AL ? ( + + } aria-controls="preferences-content" id="preferences-header"> + Preference Settings + + + + PV Web Socket: {config.USE_PVWS ? "Enabled" : "Disabled"} + Archiver: {config.USE_AA ? "Enabled" : "Disabled"} + OLOG: {config.USE_OLOG ? "Enabled" : "Disabled"} + Alarm Logger: {config.USE_AL ? "Enabled" : "Disabled"} + CaputLog: {config.USE_CAPUTLOG ? "Enabled" : "Disabled"} + + } + sx={{ minHeight: "32px !important", '& .MuiAccordionSummary-content': { margin: 0 } }} + > + Page Settings + + + + + Home Page + + + Channel Finder Max Results{config.CF_MAX_RESULTS ?? "No Limit"} + Record Type{config.CF_RECORD_TYPE ? "Enabled" : "Disabled"} + Record Description{config.CF_RECORD_DESC ? "Enabled" : "Disabled"} + PV Alias{config.CF_RECORD_ALIAS ? "Enabled" : "Disabled"} + Extra Property{config.EXTRA_PROP || "None"} + Second Extra Property{config.SECOND_EXTRA_PROP || "None"} + PV Results Table Size{config.RESULTS_TABLE_SIZE} + PV Results Table Density{config.RESULTS_TABLE_DENSITY} + Show Disconnected in Live Value Box{config.SHOW_DISCONNECTED ? "Yes" : "No"} + Monitor All Warning Threshold{config.LIVE_MONITOR_WARN} + Monitor All Maximum{config.LIVE_MONITOR_MAX} + +
    +
    + + PV Details Page + + + Enable Live Monitoring by Default{config.DEFAULT_LIVE_MONITORING ? "Yes" : "No"} + Ignore Channel Finder PV Status for Monitoring{config.PVWS_IGNORE_CF_PVSTATUS ? "Yes" : "No"} + Waveform Monitoring{config.PVWS_ALLOW_WAVEFORMS ? "Enabled" : "Disabled"} + Treat Char Waveforms As Strings{config.PVWS_TREAT_BYTE_ARRAY_AS_STRING ? "Yes" : "No"} + Number of Days to Include in Online Log Search{config.OLOG_START_TIME_DAYS ?? "No Limit"} + Online Log Max Results{config.OLOG_MAX_RESULTS ?? "No Limit"} + Number of Days to Include in Alarm Log Search{config.AL_START_TIME_DAYS ?? "No Limit"} + Alarm Log Max Results{config.AL_MAX_RESULTS ?? "No Limit"} + +
    +
    +
    +
    +
    +
    +
    +
    ) } diff --git a/src/config.js b/src/config.js new file mode 100644 index 0000000..6212af4 --- /dev/null +++ b/src/config.js @@ -0,0 +1,162 @@ +import { ApiProxyConnector } from "@elastic/search-ui-elasticsearch-connector"; +import CustomElasticSearchAPIConnector from "./components/caputlog/CustomElasticSearchAPIConnector"; + +const parseBoolean = (value, defaultValue) => { + if (value === null || value === undefined || value === "") { + return defaultValue; + } + if (typeof value === "string") { + return ["true", "1", "yes", "on"].includes(value.trim().toLowerCase()); + } + return Boolean(value); +}; + +const parseIntOrDefault = (value, defaultValue) => { + if (value === null || value === undefined || value === "") { + return defaultValue; + } + const num = parseInt(value.trim(), 10); + return isNaN(num) ? defaultValue : num; +}; + +// parse comma-separated list into array +const parseList = (value) => { + if (value === null || value === undefined || value === "") { + return []; + } + return value.split(",").map((item) => item.trim()).filter(Boolean); +}; + +const getEnv = (prodKey, devKey) => { + return import.meta.env.PROD ? import.meta.env[prodKey] : import.meta.env[devKey]; +}; + +// caputlog elasticsearch connector - either direct or via a back-end proxy service +const elasticIndexName = import.meta.env.REACT_APP_ELASTICSEARCH_INDEX_NAME; +const elasticApikey = import.meta.env.REACT_APP_ELASTICSEARCH_API_KEY; +const useProxy = parseBoolean(import.meta.env.REACT_APP_USE_CAPUT_API_PROXY_CONNNECTOR, true); + +const caputlogURL = getEnv("REACT_APP_CAPUTLOG_URL", "REACT_APP_CAPUTLOG_URL_DEV"); + +const caputLogConnector = useProxy + ? new ApiProxyConnector({ basePath: caputlogURL }) + : CustomElasticSearchAPIConnector({ + host: caputlogURL, + index: elasticIndexName, + apiKey: elasticApikey + }); + +// Global config object +const config = { + // General settings + WEBSITE_NAME: import.meta.env.REACT_APP_WEBSITE_NAME, + WEBSITE_DESC: import.meta.env.REACT_APP_WEBSITE_DESC, + VERSION: import.meta.env.REACT_APP_VERSION, + ENDPOINT: import.meta.env.REACT_APP_ENDPOINT, + DOMAIN: import.meta.env.REACT_APP_DOMAIN, + HTTP_PROTOCOL: import.meta.env.REACT_APP_HTTP_PROTOCOL, + FULL_URL: getEnv("REACT_APP_FULL_URL", "REACT_APP_FULL_URL_DEV"), + IS_PROD: import.meta.env.PROD, + + // URLs + CF_URL: getEnv("REACT_APP_CF_URL", "REACT_APP_CF_URL_DEV"), + OLOG_URL: getEnv("REACT_APP_OLOG_URL", "REACT_APP_OLOG_URL_DEV"), + OLOG_WEB_URL: getEnv("REACT_APP_OLOG_WEB_CLIENT_URL", "REACT_APP_OLOG_WEB_CLIENT_URL_DEV"), + AA_VIEWER: getEnv("REACT_APP_AA_URL", "REACT_APP_AA_URL_DEV"), + PVWS_URL: getEnv("REACT_APP_PVWS_URL", "REACT_APP_PVWS_URL_DEV"), + PVWS_HTTP_URL: getEnv("REACT_APP_PVWS_HTTP_URL", "REACT_APP_PVWS_HTTP_URL_DEV"), + AL_URL: getEnv("REACT_APP_AL_URL", "REACT_APP_AL_URL_DEV"), + SERVER_URL: getEnv("REACT_APP_BASE_URL", "REACT_APP_BASE_URL_DEV"), + CAPUTLOG_URL: caputlogURL, + + // Connectors + CAPUTLOG_CONNECTOR: caputLogConnector, + + // Breakpoints + SM_BREAKPOINT: parseIntOrDefault(import.meta.env.REACT_APP_SM_BREAKPOINT, 600), + MD_BREAKPOINT: parseIntOrDefault(import.meta.env.REACT_APP_MD_BREAKPOINT, 900), + LG_BREAKPOINT: parseIntOrDefault(import.meta.env.REACT_APP_LG_BREAKPOINT, 1536), + + // Table config + OMIT_IN_TABLE_MEDIUM: parseList(import.meta.env.REACT_APP_OMIT_IN_TABLE_MEDIUM), + OMIT_IN_TABLE_SMALL: parseList(import.meta.env.REACT_APP_OMIT_IN_TABLE_SMALL), + OMIT_IN_TABLE_X_SMALL: parseList(import.meta.env.REACT_APP_OMIT_IN_TABLE_X_SMALL), + RESULTS_TABLE_SIZE: parseIntOrDefault(import.meta.env.REACT_APP_RESULTS_TABLE_SIZE, 50), + RESULTS_TABLE_SIZE_OPTIONS: + parseList(import.meta.env.REACT_APP_RESULTS_TABLE_SIZE_OPTIONS).length > 0 + ? parseList(import.meta.env.REACT_APP_RESULTS_TABLE_SIZE_OPTIONS).map(Number) + : [5, 10, 20, 50, 100], + RESULTS_TABLE_DENSITY: import.meta.env.REACT_APP_RESULTS_TABLE_DENSITY || "standard", + + // PV Detail Page + DETAILS_PAGE_PROPERTIES: import.meta.env.REACT_APP_DETAILS_PAGE_PROPERTIES, + DETAILS_PAGE_PROPERTIES_BLOCKLIST: parseList(import.meta.env.REACT_APP_DETAILS_PAGE_PROPERTIES_BLOCKLIST), + DEFAULT_LIVE_MONITORING: parseBoolean(import.meta.env.REACT_APP_DEFAULT_LIVE_MONITORING, false), + + // Channel Finder + CF_MAX_RESULTS: parseIntOrDefault(import.meta.env.REACT_APP_CF_MAX_RESULTS, null), // null means no ~size parameter in queries + CF_RECORD_TYPE: parseBoolean(import.meta.env.REACT_APP_CF_RECORD_TYPE, false), + CF_RECORD_DESC: parseBoolean(import.meta.env.REACT_APP_CF_RECORD_DESC, false), + CF_RECORD_ALIAS: parseBoolean(import.meta.env.REACT_APP_CF_ALIAS, false), + + // CF extra properties for home page + EXTRA_PROP: import.meta.env.REACT_APP_EXTRA_PROP || null, + EXTRA_PROP_LABEL: import.meta.env.REACT_APP_EXTRA_PROP_LABEL || "", + EXTRA_PROP_HELP_TEXT: import.meta.env.REACT_APP_EXTRA_PROP_HELP_TEXT || "", + EXTRA_PROP_SHOW_IN_RESULTS: parseBoolean(import.meta.env.REACT_APP_EXTRA_PROP_SHOW_IN_RESULTS, false), + EXTRA_PROP_DROPDOWN_LABELS: parseList(import.meta.env.REACT_APP_EXTRA_PROP_DROPDOWN_LABELS), + + SECOND_EXTRA_PROP: import.meta.env.REACT_APP_SECOND_EXTRA_PROP || null, + SECOND_EXTRA_PROP_LABEL: import.meta.env.REACT_APP_SECOND_EXTRA_PROP_LABEL || "", + SECOND_EXTRA_PROP_HELP_TEXT: import.meta.env.REACT_APP_SECOND_EXTRA_PROP_HELP_TEXT || "", + SECOND_EXTRA_PROP_SHOW_IN_RESULTS: parseBoolean(import.meta.env.REACT_APP_SECOND_EXTRA_PROP_SHOW_IN_RESULTS, false), + SECOND_EXTRA_PROP_DROPDOWN_LABELS: parseList(import.meta.env.REACT_APP_SECOND_EXTRA_PROP_DROPDOWN_LABELS), + + // PV Web Socket + USE_PVWS: parseBoolean(import.meta.env.REACT_APP_USE_PVWS, false), + LIVE_MONITOR_WARN: parseIntOrDefault(import.meta.env.REACT_APP_LIVE_MONITOR_WARN, 50), + LIVE_MONITOR_MAX: parseIntOrDefault(import.meta.env.REACT_APP_LIVE_MONITOR_MAX, 100), + PVWS_TREAT_BYTE_ARRAY_AS_STRING: parseBoolean(import.meta.env.REACT_APP_PVWS_TREAT_BYTE_ARRAY_AS_STRING, true), + PVWS_ALLOW_WAVEFORMS: parseBoolean(import.meta.env.REACT_APP_PVWS_ALLOW_WAVEFORMS, false), + PVWS_IGNORE_CF_PVSTATUS: parseBoolean(import.meta.env.REACT_APP_PVWS_IGNORE_CF_PVSTATUS, false), + SHOW_DISCONNECTED: parseBoolean(import.meta.env.REACT_APP_SHOW_DISCONNECTED, true), + + // Archiver + USE_AA: parseBoolean(import.meta.env.REACT_APP_USE_AA, false), + AA_POLICIES: parseList(import.meta.env.REACT_APP_AA_POLICIES), + + // OLOG + USE_OLOG: parseBoolean(import.meta.env.REACT_APP_USE_OLOG, false), + OLOG_START_TIME_DAYS: parseIntOrDefault(import.meta.env.REACT_APP_OLOG_START_TIME_DAYS, null), + OLOG_MAX_RESULTS: parseIntOrDefault(import.meta.env.REACT_APP_OLOG_MAX_RESULTS, null), + + // Alarm Logger + USE_AL: parseBoolean(import.meta.env.REACT_APP_USE_AL, false), + AL_START_TIME_DAYS: parseIntOrDefault(import.meta.env.REACT_APP_AL_START_TIME_DAYS, null), + AL_MAX_RESULTS: parseIntOrDefault(import.meta.env.REACT_APP_AL_MAX_RESULTS, null), + + // CaputLog + USE_CAPUTLOG: parseBoolean(import.meta.env.REACT_APP_USE_CAPUTLOG, false), + USE_CAPUT_API_PROXY_CONNNECTOR: useProxy, + + // Severity colors + OK_SEVERITY_COLOR: import.meta.env.REACT_APP_OK_SEVERITY_COLOR, + MINOR_SEVERITY_COLOR: import.meta.env.REACT_APP_MINOR_SEVERITY_COLOR, + MAJOR_SEVERITY_COLOR: import.meta.env.REACT_APP_MAJOR_SEVERITY_COLOR, + INVALID_SEVERITY_COLOR: import.meta.env.REACT_APP_INVALID_SEVERITY_COLOR, + UNDEFINED_SEVERITY_COLOR: import.meta.env.REACT_APP_UNDEFINED_SEVERITY_COLOR, + + // git info from vite build + GIT_COMMIT_DATE: import.meta.env.REACT_APP_GIT_COMMIT_DATE, + GIT_SHORT_HASH: import.meta.env.REACT_APP_GIT_SHORT_HASH, + + // Misc + HELP_EXAMPLE_PV: import.meta.env.REACT_APP_HELP_EXAMPLE_PV, + HELP_PV_NAMING_CONVENTION: import.meta.env.REACT_APP_HELP_PV_NAMING_CONVENTION, + HOMEPAGE_HEADER: import.meta.env.REACT_APP_HOMEPAGE_HEADER, + HOMEPAGE_SUBHEADER: import.meta.env.REACT_APP_HOMEPAGE_SUBHEADER, + SEND_CREDENTIALS: parseBoolean(import.meta.env.REACT_APP_SEND_CREDENTIALS, false) +}; + +// freeze the config object to prevent modifications +export default Object.freeze(config);