diff --git a/backend/api/Pims.Api.csproj b/backend/api/Pims.Api.csproj index b5d4f5852a..432d51471c 100644 --- a/backend/api/Pims.Api.csproj +++ b/backend/api/Pims.Api.csproj @@ -4,7 +4,7 @@ net5.0 0ef6255f-9ea0-49ec-8c65-c172304b4926 8.0 - 0.2.0-9.0 + 0.2.0-9.1 0.2.0.9 true $(NoWarn);1591 diff --git a/frontend/package.json b/frontend/package.json index ebcc793187..bae879ae24 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "frontend", - "version": "0.2.0-9.0", + "version": "0.2.0-9.1", "private": true, "dependencies": { "@bcgov/bc-sans": "1.0.1", diff --git a/frontend/public/tenants/tenant.json b/frontend/public/tenants/tenant.json index 05739e22c0..2b39ff9ffc 100644 --- a/frontend/public/tenants/tenant.json +++ b/frontend/public/tenants/tenant.json @@ -10,5 +10,7 @@ "title": "", "heading": "", "body": "" - } + }, + "layers": [], + "propertiesUrl": "ogs-internal/ows?service=wfs&request=GetFeature&typeName=PIMS_PROPERTY_LOCATION_VW&outputformat=json&srsName=EPSG:4326&version=2.0.0&" } diff --git a/frontend/src/components/maps/leaflet/LayersControl/LayersTree.tsx b/frontend/src/components/maps/leaflet/LayersControl/LayersTree.tsx index ec41519afb..fbd66254b5 100644 --- a/frontend/src/components/maps/leaflet/LayersControl/LayersTree.tsx +++ b/frontend/src/components/maps/leaflet/LayersControl/LayersTree.tsx @@ -5,13 +5,15 @@ import { Form as FormikForm, Formik, getIn, useFormikContext } from 'formik'; import L from 'leaflet'; import flatten from 'lodash/flatten'; import noop from 'lodash/noop'; -import React, { useEffect } from 'react'; +import React, { useEffect, useMemo } from 'react'; +import { useContext } from 'react'; import Form from 'react-bootstrap/Form'; import ListGroup from 'react-bootstrap/ListGroup'; import { FaAngleDown, FaAngleRight } from 'react-icons/fa'; import { useMap } from 'react-leaflet'; import TreeMenu, { TreeMenuItem, TreeNode } from 'react-simple-tree-menu'; import styled from 'styled-components'; +import { TenantContext } from 'tenants'; import { layersTree } from './data'; import { ILayerItem } from './types'; @@ -239,27 +241,38 @@ const LayersTree: React.FC<{ items: TreeMenuItem[] }> = ({ items }) => { ); }; -const layers = layersTree.map((parent, parentIndex) => { - return { - ...parent, - nodes: parent.nodes?.map((node: any, index) => ({ - ...node, - zIndex: (parentIndex + 1) * index, - opacity: 0.3, - })), - }; -}); - /** * This component displays the layers group menu */ const LayersMenu: React.FC = () => { + const { + tenant: { layers: confLayers }, + } = useContext(TenantContext); + const layers = useMemo( + () => + layersTree.map((parent, parentIndex) => { + //add any layers defined in the configuration. + const layer = confLayers?.find(cl => cl.key === parent.key); + + const allNodes = [...(parent.nodes ?? []), ...(layer?.nodes ?? [])]; + return { + ...parent, + nodes: allNodes?.map((node: any, index) => ({ + ...node, + zIndex: (parentIndex + 1) * index, + opacity: 0.3, + })), + }; + }), + [confLayers], + ); + return ( {({ values }) => ( - + {({ items }) => { return ; }} diff --git a/frontend/src/hooks/useApi.ts b/frontend/src/hooks/useApi.ts index 5cb2aa98ee..2f042ee41c 100644 --- a/frontend/src/hooks/useApi.ts +++ b/frontend/src/hooks/useApi.ts @@ -4,10 +4,11 @@ import CustomAxios from 'customAxios'; import { FeatureCollection } from 'geojson'; import { LatLngLiteral } from 'leaflet'; import _ from 'lodash'; -import { useCallback } from 'react'; +import { useCallback, useContext } from 'react'; import { useDispatch } from 'react-redux'; import { hideLoading, showLoading } from 'react-redux-loading-bar'; import { store } from 'store/store'; +import { TenantContext } from 'tenants'; import { toCqlFilter } from './../components/maps/leaflet/mapUtils'; @@ -49,6 +50,9 @@ export interface IPimsAPI { */ export const useApi = (): IPimsAPI => { const dispatch = useDispatch(); + const { + tenant: { propertiesUrl }, + } = useContext(TenantContext); const getAxios = useCallback(() => { const axios = CustomAxios(); @@ -159,17 +163,16 @@ export const useApi = (): IPimsAPI => { [], ); - const loadProperties = useCallback(async (params: IGeoSearchParams): Promise< - FeatureCollection - > => { - const { BBOX, ...rest } = params; - const { data } = await CustomAxios().get( - `ogs-internal/ows?service=wfs&request=GetFeature&typeName=PIMS_PROPERTY_LOCATION_VW&outputformat=json&srsName=EPSG:4326&version=2.0.0&${ - rest ? toCqlFilter(rest) : '' - }`, - ); - return data; - }, []); + const loadProperties = useCallback( + async (params: IGeoSearchParams): Promise => { + const { BBOX, ...rest } = params; + const { data } = await CustomAxios().get( + `${propertiesUrl}${rest ? toCqlFilter(rest) : ''}`, + ); + return data; + }, + [propertiesUrl], + ); // The below memo is only intended to run once, at startup. Or when the jwt is updated. // eslint-disable-next-line react-hooks/exhaustive-deps return { diff --git a/frontend/src/tenants/ITenantConfig.ts b/frontend/src/tenants/ITenantConfig.ts index f23a2a4b91..3f308bb6d8 100644 --- a/frontend/src/tenants/ITenantConfig.ts +++ b/frontend/src/tenants/ITenantConfig.ts @@ -1,3 +1,5 @@ +import { ILayerItem } from 'components/maps/leaflet/LayersControl/types'; + /** * Interface for tenant configuration settings. */ @@ -14,6 +16,10 @@ export interface ITenantConfig { logo: ITenantLogoConfig; // Login page settings. login: ITenantLoginConfig; + // optional additional layers to add using config. + layers: ILayerItem[]; + // the url that should be used to query the PSP properties layer. + propertiesUrl?: string; } export interface ITenantLoginConfig { diff --git a/frontend/src/tenants/__snapshots__/tenant.test.tsx.snap b/frontend/src/tenants/__snapshots__/tenant.test.tsx.snap index 480abec830..14b2814bbd 100644 --- a/frontend/src/tenants/__snapshots__/tenant.test.tsx.snap +++ b/frontend/src/tenants/__snapshots__/tenant.test.tsx.snap @@ -2,18 +2,18 @@ exports[`Tenant configuration Tenant returns correct MOTI configuration 1`] = `
- {"id":"MOTI","title":"Property Information Management System","shortName":"PIMS","colour":"#003366","logo":{"favicon":"/tenants/MOTI/favicon.ico","image":"/tenants/MOTI/PIMS-logo.png","imageWithText":"/tenants/MOTI/PIMS-logo-with-text.png"},"login":{"title":"TRAN Property Information Management System (PIMS)","heading":"PIMS enables you to view highways and properties owned by the Ministry of Transportation and Infrastructure","body":"WARNING: Not all data included within has been vetted for accuracy and completeness. Please use caution when proceeding and confirm data before relying on it.","backgroundImage":"/tenants/MOTI/background-image.jpg"}} + {"id":"MOTI","title":"Property Information Management System","shortName":"PIMS","colour":"#003366","logo":{"favicon":"/tenants/MOTI/favicon.ico","image":"/tenants/MOTI/PIMS-logo.png","imageWithText":"/tenants/MOTI/PIMS-logo-with-text.png"},"login":{"title":"TRAN Property Information Management System (PIMS)","heading":"PIMS enables you to view highways and properties owned by the Ministry of Transportation and Infrastructure","body":"WARNING: Not all data included within has been vetted for accuracy and completeness. Please use caution when proceeding and confirm data before relying on it.","backgroundImage":"/tenants/MOTI/background-image.jpg"},"layers":[],"propertiesUrl":"ogs-internal/ows?service=wfs&request=GetFeature&typeName=PIMS_PROPERTY_LOCATION_VW&outputformat=json&srsName=EPSG:4326&version=2.0.0&"}
`; exports[`Tenant configuration Tenant returns correct default configuration 1`] = `
- {"0":"{","1":"\\"","2":"i","3":"d","4":"\\"","5":":","6":"\\"","7":"D","8":"F","9":"L","10":"T","11":"\\"","12":",","13":"\\"","14":"t","15":"i","16":"t","17":"l","18":"e","19":"\\"","20":":","21":"\\"","22":"D","23":"e","24":"f","25":"a","26":"u","27":"l","28":"t","29":" ","30":"T","31":"e","32":"n","33":"a","34":"n","35":"t","36":" ","37":"N","38":"a","39":"m","40":"e","41":"\\"","42":",","43":"\\"","44":"s","45":"h","46":"o","47":"r","48":"t","49":"N","50":"a","51":"m","52":"e","53":"\\"","54":":","55":"\\"","56":"P","57":"I","58":"M","59":"S","60":"\\"","61":",","62":"\\"","63":"c","64":"o","65":"l","66":"o","67":"u","68":"r","69":"\\"","70":":","71":"\\"","72":"#","73":"0","74":"0","75":"3","76":"3","77":"6","78":"6","79":"\\"","80":",","81":"\\"","82":"l","83":"o","84":"g","85":"o","86":"\\"","87":":","88":"{","89":"\\"","90":"f","91":"a","92":"v","93":"i","94":"c","95":"o","96":"n","97":"\\"","98":":","99":"\\"","100":"\\"","101":",","102":"\\"","103":"i","104":"m","105":"a","106":"g","107":"e","108":"\\"","109":":","110":"\\"","111":"\\"","112":",","113":"\\"","114":"i","115":"m","116":"a","117":"g","118":"e","119":"W","120":"i","121":"t","122":"h","123":"T","124":"e","125":"x","126":"t","127":"\\"","128":":","129":"\\"","130":"\\"","131":"}","132":",","133":"\\"","134":"l","135":"o","136":"g","137":"i","138":"n","139":"\\"","140":":","141":"{","142":"\\"","143":"t","144":"i","145":"t","146":"l","147":"e","148":"\\"","149":":","150":"\\"","151":"\\"","152":",","153":"\\"","154":"h","155":"e","156":"a","157":"d","158":"i","159":"n","160":"g","161":"\\"","162":":","163":"\\"","164":"\\"","165":",","166":"\\"","167":"b","168":"o","169":"d","170":"y","171":"\\"","172":":","173":"\\"","174":"\\"","175":"}","176":"}","id":"DFLT","title":"Default Tenant Name","shortName":"PIMS","colour":"#003366","logo":{"favicon":"","image":"","imageWithText":""},"login":{"title":"","heading":"","body":""}} + {"0":"{","1":"\\"","2":"i","3":"d","4":"\\"","5":":","6":"\\"","7":"D","8":"F","9":"L","10":"T","11":"\\"","12":",","13":"\\"","14":"t","15":"i","16":"t","17":"l","18":"e","19":"\\"","20":":","21":"\\"","22":"D","23":"e","24":"f","25":"a","26":"u","27":"l","28":"t","29":" ","30":"T","31":"e","32":"n","33":"a","34":"n","35":"t","36":" ","37":"N","38":"a","39":"m","40":"e","41":"\\"","42":",","43":"\\"","44":"s","45":"h","46":"o","47":"r","48":"t","49":"N","50":"a","51":"m","52":"e","53":"\\"","54":":","55":"\\"","56":"P","57":"I","58":"M","59":"S","60":"\\"","61":",","62":"\\"","63":"c","64":"o","65":"l","66":"o","67":"u","68":"r","69":"\\"","70":":","71":"\\"","72":"#","73":"0","74":"0","75":"3","76":"3","77":"6","78":"6","79":"\\"","80":",","81":"\\"","82":"l","83":"o","84":"g","85":"o","86":"\\"","87":":","88":"{","89":"\\"","90":"f","91":"a","92":"v","93":"i","94":"c","95":"o","96":"n","97":"\\"","98":":","99":"\\"","100":"\\"","101":",","102":"\\"","103":"i","104":"m","105":"a","106":"g","107":"e","108":"\\"","109":":","110":"\\"","111":"\\"","112":",","113":"\\"","114":"i","115":"m","116":"a","117":"g","118":"e","119":"W","120":"i","121":"t","122":"h","123":"T","124":"e","125":"x","126":"t","127":"\\"","128":":","129":"\\"","130":"\\"","131":"}","132":",","133":"\\"","134":"l","135":"o","136":"g","137":"i","138":"n","139":"\\"","140":":","141":"{","142":"\\"","143":"t","144":"i","145":"t","146":"l","147":"e","148":"\\"","149":":","150":"\\"","151":"\\"","152":",","153":"\\"","154":"h","155":"e","156":"a","157":"d","158":"i","159":"n","160":"g","161":"\\"","162":":","163":"\\"","164":"\\"","165":",","166":"\\"","167":"b","168":"o","169":"d","170":"y","171":"\\"","172":":","173":"\\"","174":"\\"","175":"}","176":",","177":"\\"","178":"l","179":"a","180":"y","181":"e","182":"r","183":"s","184":"\\"","185":":","186":"[","187":"]","188":",","189":"\\"","190":"p","191":"r","192":"o","193":"p","194":"e","195":"r","196":"t","197":"i","198":"e","199":"s","200":"U","201":"r","202":"l","203":"\\"","204":":","205":"\\"","206":"o","207":"g","208":"s","209":"-","210":"i","211":"n","212":"t","213":"e","214":"r","215":"n","216":"a","217":"l","218":"/","219":"o","220":"w","221":"s","222":"?","223":"s","224":"e","225":"r","226":"v","227":"i","228":"c","229":"e","230":"=","231":"w","232":"f","233":"s","234":"&","235":"r","236":"e","237":"q","238":"u","239":"e","240":"s","241":"t","242":"=","243":"G","244":"e","245":"t","246":"F","247":"e","248":"a","249":"t","250":"u","251":"r","252":"e","253":"&","254":"t","255":"y","256":"p","257":"e","258":"N","259":"a","260":"m","261":"e","262":"=","263":"P","264":"I","265":"M","266":"S","267":"_","268":"P","269":"R","270":"O","271":"P","272":"E","273":"R","274":"T","275":"Y","276":"_","277":"L","278":"O","279":"C","280":"A","281":"T","282":"I","283":"O","284":"N","285":"_","286":"V","287":"W","288":"&","289":"o","290":"u","291":"t","292":"p","293":"u","294":"t","295":"f","296":"o","297":"r","298":"m","299":"a","300":"t","301":"=","302":"j","303":"s","304":"o","305":"n","306":"&","307":"s","308":"r","309":"s","310":"N","311":"a","312":"m","313":"e","314":"=","315":"E","316":"P","317":"S","318":"G","319":":","320":"4","321":"3","322":"2","323":"6","324":"&","325":"v","326":"e","327":"r","328":"s","329":"i","330":"o","331":"n","332":"=","333":"2","334":".","335":"0","336":".","337":"0","338":"&","339":"\\"","340":"}","id":"DFLT","title":"Default Tenant Name","shortName":"PIMS","colour":"#003366","logo":{"favicon":"","image":"","imageWithText":""},"login":{"title":"","heading":"","body":""},"layers":[],"propertiesUrl":"ogs-internal/ows?service=wfs&request=GetFeature&typeName=PIMS_PROPERTY_LOCATION_VW&outputformat=json&srsName=EPSG:4326&version=2.0.0&"}
`; exports[`Tenant configuration Tenant returns correct non-existing configuration 1`] = `
- {"id":"DFLT","title":"Default Tenant Name","shortName":"PIMS","colour":"#003366","logo":{"favicon":"","image":"","imageWithText":""},"login":{"title":"","heading":"","body":""}} + {"id":"DFLT","title":"Default Tenant Name","shortName":"PIMS","colour":"#003366","logo":{"favicon":"","image":"","imageWithText":""},"login":{"title":"","heading":"","body":""},"layers":[],"propertiesUrl":"ogs-internal/ows?service=wfs&request=GetFeature&typeName=PIMS_PROPERTY_LOCATION_VW&outputformat=json&srsName=EPSG:4326&version=2.0.0&"}
`; diff --git a/frontend/src/tenants/config/default.ts b/frontend/src/tenants/config/default.ts index 9748abd7a9..ecd44f64aa 100644 --- a/frontend/src/tenants/config/default.ts +++ b/frontend/src/tenants/config/default.ts @@ -18,6 +18,9 @@ export const defaultTenant: ITenantConfig = { heading: '', body: '', }, + layers: [], + propertiesUrl: + 'ogs-internal/ows?service=wfs&request=GetFeature&typeName=PIMS_PROPERTY_LOCATION_VW&outputformat=json&srsName=EPSG:4326&version=2.0.0&', }; export default defaultTenant; diff --git a/frontend/src/tenants/useTenant.test.tsx b/frontend/src/tenants/useTenant.test.tsx index 05068e2f41..662e11448c 100644 --- a/frontend/src/tenants/useTenant.test.tsx +++ b/frontend/src/tenants/useTenant.test.tsx @@ -41,7 +41,10 @@ describe('useTenant hook', () => { await act(async () => { const { container } = testRender(); const title = getByTestId(container, 'tenant'); - expect(title).toContainHTML(JSON.stringify(defaultTenant)); + expect({ ...JSON.parse(title.innerHTML), propertiesUrl: undefined }).toStrictEqual({ + ...defaultTenant, + propertiesUrl: undefined, + }); }); }); @@ -50,7 +53,10 @@ describe('useTenant hook', () => { process.env.REACT_APP_TENANT = 'FAKE'; const { container } = testRender(); const title = getByTestId(container, 'tenant'); - expect(title).toContainHTML(JSON.stringify(defaultTenant)); + expect({ ...JSON.parse(title.innerHTML), propertiesUrl: undefined }).toStrictEqual({ + ...defaultTenant, + propertiesUrl: undefined, + }); }); }); @@ -59,7 +65,11 @@ describe('useTenant hook', () => { process.env.REACT_APP_TENANT = 'MOTI'; const { container } = testRender(); const title = getByTestId(container, 'tenant'); - expect(title).toContainHTML(JSON.stringify({ ...defaultTenant, ...config['MOTI'] })); + expect({ ...JSON.parse(title.innerHTML), propertiesUrl: undefined }).toStrictEqual({ + ...defaultTenant, + ...config['MOTI'], + propertiesUrl: undefined, + }); }); }); @@ -68,7 +78,11 @@ describe('useTenant hook', () => { process.env.REACT_APP_TENANT = 'CITZ'; const { container } = testRender(); const title = getByTestId(container, 'tenant'); - expect(title).toContainHTML(JSON.stringify({ ...defaultTenant, ...config['CITZ'] })); + expect({ ...JSON.parse(title.innerHTML), propertiesUrl: undefined }).toStrictEqual({ + ...defaultTenant, + ...config['CITZ'], + propertiesUrl: undefined, + }); }); }); });