From d1adf81e57b5d9eda5aaff1794ac6083c5dd9bc8 Mon Sep 17 00:00:00 2001 From: Kameron Heyen Date: Fri, 18 Oct 2024 14:51:16 -0500 Subject: [PATCH] WIP commit - Base page layout with hardcoded data --- src/DashboardUI/src/App.tsx | 2 + .../src/accessors/regulationAccessor.ts | 40 ++++++ .../components/details-page/detail-page.scss | 5 +- .../details-page/regulation/Layout.tsx | 26 ++++ .../details-page/regulation/Provider.tsx | 93 +++++++++++++ .../details-page/regulation/RegulationMap.tsx | 27 ++++ .../regulation/RegulationProperties.tsx | 99 ++++++++++++++ .../regulation/RegulationTabs.tsx | 122 ++++++++++++++++++ .../regulation/hooks/useMapLegend.tsx | 32 +++++ .../details-page/regulation/regulation.scss | 13 ++ .../src/data-contracts/RegulationDetails.ts | 17 +++ .../RegulatoryOverlayInfoListItem.ts | 14 ++ src/DashboardUI/src/data-contracts/index.ts | 2 + src/DashboardUI/src/hooks/queries/index.ts | 3 +- .../src/hooks/queries/useRegulationQuery.ts | 37 ++++++ .../src/pages/RegulationDetailsPage.tsx | 12 ++ 16 files changed, 540 insertions(+), 4 deletions(-) create mode 100644 src/DashboardUI/src/accessors/regulationAccessor.ts create mode 100644 src/DashboardUI/src/components/details-page/regulation/Layout.tsx create mode 100644 src/DashboardUI/src/components/details-page/regulation/Provider.tsx create mode 100644 src/DashboardUI/src/components/details-page/regulation/RegulationMap.tsx create mode 100644 src/DashboardUI/src/components/details-page/regulation/RegulationProperties.tsx create mode 100644 src/DashboardUI/src/components/details-page/regulation/RegulationTabs.tsx create mode 100644 src/DashboardUI/src/components/details-page/regulation/hooks/useMapLegend.tsx create mode 100644 src/DashboardUI/src/components/details-page/regulation/regulation.scss create mode 100644 src/DashboardUI/src/data-contracts/RegulationDetails.ts create mode 100644 src/DashboardUI/src/data-contracts/RegulatoryOverlayInfoListItem.ts create mode 100644 src/DashboardUI/src/hooks/queries/useRegulationQuery.ts create mode 100644 src/DashboardUI/src/pages/RegulationDetailsPage.tsx diff --git a/src/DashboardUI/src/App.tsx b/src/DashboardUI/src/App.tsx index 38a8cf86..98638bfd 100644 --- a/src/DashboardUI/src/App.tsx +++ b/src/DashboardUI/src/App.tsx @@ -17,6 +17,7 @@ import { useEffect, useState } from "react"; import ReactGA from 'react-ga4'; import WaterRightDetailsPage from "./pages/WaterRightDetailsPage"; import SiteDetailsPage from "./pages/SiteDetailsPage"; +import RegulationDetailsPage from "./pages/RegulationDetailsPage"; import { clarity } from 'clarity-js' export interface AppProps { @@ -74,6 +75,7 @@ function App({ msalInstance }: AppProps) { }> } /> } /> + } /> diff --git a/src/DashboardUI/src/accessors/regulationAccessor.ts b/src/DashboardUI/src/accessors/regulationAccessor.ts new file mode 100644 index 00000000..4d68c98b --- /dev/null +++ b/src/DashboardUI/src/accessors/regulationAccessor.ts @@ -0,0 +1,40 @@ +import { + RegulationDetails, + RegulatoryOverlayInfoListItem, +} from "@data-contracts"; +import axios from "axios"; + +// TODO: Wire to API when route is created +export const getRegulationDetails = async (areaUuid: string) => { + /*const { data } = await axios.get( + `${process.env.REACT_APP_WEBAPI_URL}Regulation/${areaUuid}`, + ); */ + + const data = { + waDEAreaReportingUuid: "C0o_RUCO356", + reportingAreaNativeId: "CO356", + waDEReportingAreaName: "WaDE Blank", + waDEOverlayAreaType: "Administration", + nativeReportingAreaType: "Groundwater Management District", + reportingAreaName: "Arikaree", + reportingAreaState: "CO", + // areaLastUpdated: new Date("08/05/2005"), + managingAgencyOrganizationName: "Colorado Division of Water Resources", + managingAgencyState: "CO", + managingAgencyWebsite: "https://water.state.co.us/", + } as RegulationDetails; + + return data; +}; + +export const getRegulatoryOverlayInfoList = async (areaUuid: string) => { + const data = [ + { + waDEOverlayUuid: "C0o_RUCO356", + overlayNativeId: "WaDE Blank", + overlayType: "Groundwater Management District", + }, + ] as RegulatoryOverlayInfoListItem[]; + + return data; +}; diff --git a/src/DashboardUI/src/components/details-page/detail-page.scss b/src/DashboardUI/src/components/details-page/detail-page.scss index 1db53143..b01a05ec 100644 --- a/src/DashboardUI/src/components/details-page/detail-page.scss +++ b/src/DashboardUI/src/components/details-page/detail-page.scss @@ -11,12 +11,11 @@ padding: 10px; border-bottom: 1px solid $bs-gray-600; } - - .water-rights-card, .site-card { + + .water-rights-card, .site-card, .regulation-card { .card-header{ font-size: 20px; color: $white; - color: $white; padding: 10px; display: flex; align-items: center; diff --git a/src/DashboardUI/src/components/details-page/regulation/Layout.tsx b/src/DashboardUI/src/components/details-page/regulation/Layout.tsx new file mode 100644 index 00000000..033871d4 --- /dev/null +++ b/src/DashboardUI/src/components/details-page/regulation/Layout.tsx @@ -0,0 +1,26 @@ +import { DetailsPage } from "../DetailsPageLayout"; +import RegulationProperties from "./RegulationProperties"; +import RegulationTabs from "./RegulationTabs"; + +//import { useAlerts } from "./hooks/useAlerts"; + +import "./regulation.scss"; +import RegulationMap from "./RegulationMap"; + +export function Layout() { + // useAlerts(); // TODO: Wire up / for the popup alerts for loading/errors + return ( + + Overlay Landing Page + + + + + + + + + + + ); +} diff --git a/src/DashboardUI/src/components/details-page/regulation/Provider.tsx b/src/DashboardUI/src/components/details-page/regulation/Provider.tsx new file mode 100644 index 00000000..9e44c4fa --- /dev/null +++ b/src/DashboardUI/src/components/details-page/regulation/Provider.tsx @@ -0,0 +1,93 @@ +import { useParams } from "react-router-dom"; +import { createContext, FC, useContext, useState } from "react"; +import { UseQueryResult } from "react-query"; +import { FeatureCollection, GeoJsonProperties, Geometry } from "geojson"; +import { + useWaterRightSiteLocations, + useRegulationDetails, + useWaterRightInfoList, + useRegulatoryOverlayInfoList, +} from "../../../hooks/queries"; +import { + RegulationDetails, + RegulatoryOverlayInfoListItem, + WaterRightInfoListItem, +} from "@data-contracts"; + +type Query = Pick< + UseQueryResult, + "data" | "isError" | "isLoading" +>; + +const defaultQuery = { data: undefined, isError: false, isLoading: false }; + +export interface HostData { + detailsQuery: Query; + locationsQuery: Query>; + regulatoryOverlayInfoListQuery: Query; + waterRightInfoListQuery: Query; +} + +type ActiveTabType = "right" | "regulatory"; + +interface RegulationDetailsPageContextState { + allocationUuid: string | undefined; + activeTab: ActiveTabType; + setActiveTab: (tab: ActiveTabType) => void; + hostData: HostData; +} + +const defaultState: RegulationDetailsPageContextState = { + allocationUuid: undefined, + activeTab: "regulatory", + setActiveTab: () => {}, + hostData: { + detailsQuery: defaultQuery, + locationsQuery: defaultQuery, + regulatoryOverlayInfoListQuery: defaultQuery, + waterRightInfoListQuery: defaultQuery, + }, +}; + +const RegulationDetailsContext = + createContext(defaultState); +export const useRegulationDetailsContext = () => + useContext(RegulationDetailsContext); + +export const RegulationDetailsProvider: FC = ({ children }) => { + const { id: areaUuid } = useParams(); + + const [activeTab, setActiveTab] = useState( + defaultState.activeTab, + ); + + const detailsQuery = useRegulationDetails(areaUuid); + const regulationLocationsQuery = useWaterRightSiteLocations(areaUuid); // TODO: Create a hook for RegulatoryOverlay + const regulatoryOverlayInfoListQuery = useRegulatoryOverlayInfoList( + areaUuid, + { + enabled: activeTab === "regulatory", + }, + ); + const waterRightInfoListQuery = useWaterRightInfoList(areaUuid, { + enabled: activeTab === "right", + }); + + const filterContextProviderValue: RegulationDetailsPageContextState = { + allocationUuid: areaUuid, + activeTab, + setActiveTab, + hostData: { + detailsQuery: detailsQuery, + locationsQuery: regulationLocationsQuery, + regulatoryOverlayInfoListQuery: regulatoryOverlayInfoListQuery, + waterRightInfoListQuery: waterRightInfoListQuery, + }, + }; + + return ( + + {children} + + ); +}; diff --git a/src/DashboardUI/src/components/details-page/regulation/RegulationMap.tsx b/src/DashboardUI/src/components/details-page/regulation/RegulationMap.tsx new file mode 100644 index 00000000..9932a9cb --- /dev/null +++ b/src/DashboardUI/src/components/details-page/regulation/RegulationMap.tsx @@ -0,0 +1,27 @@ +import useSiteDigestMapPopup from "../../../hooks/map-popups/useSiteDigestMapPopup"; +import DetailsMap from "../DetailsMap"; +import { useRegulationDetailsContext } from "./Provider"; +import MapProvider from "../../../contexts/MapProvider"; +import { useMapLegend } from "./hooks/useMapLegend"; + +function RegulationMap() { + return ( + + + + ); +} + +function Layout() { + const { + hostData: { locationsQuery }, + } = useRegulationDetailsContext(); + + useSiteDigestMapPopup(); + useMapLegend(); + + if (locationsQuery.isLoading || !locationsQuery.data) return null; + return ; +} + +export default RegulationMap; diff --git a/src/DashboardUI/src/components/details-page/regulation/RegulationProperties.tsx b/src/DashboardUI/src/components/details-page/regulation/RegulationProperties.tsx new file mode 100644 index 00000000..15121feb --- /dev/null +++ b/src/DashboardUI/src/components/details-page/regulation/RegulationProperties.tsx @@ -0,0 +1,99 @@ +import { Card, Col, Row } from "react-bootstrap"; +import Domain from "mdi-react/DomainIcon"; +import HexagonOutlineIcon from "mdi-react/HexagonOutlineIcon"; + +import { PropertyValue } from "../PropertyValue"; + +import { useRegulationDetailsContext } from "./Provider"; + +function RegulationProperties() { + const { + hostData: { + detailsQuery: { data: regulationDetails }, + }, + } = useRegulationDetailsContext(); + + return ( +
+ {regulationDetails && ( + + + + + + + Reporting Area Information + + +
+ + + + + + + + +
+
+
+ +
+ + + + + + Managing Agency + + +
+ + + +
+
+
+ +
+
+ )} +
+ ); +} + +export default RegulationProperties; diff --git a/src/DashboardUI/src/components/details-page/regulation/RegulationTabs.tsx b/src/DashboardUI/src/components/details-page/regulation/RegulationTabs.tsx new file mode 100644 index 00000000..fc268474 --- /dev/null +++ b/src/DashboardUI/src/components/details-page/regulation/RegulationTabs.tsx @@ -0,0 +1,122 @@ +import { Tab, Table, Tabs } from "react-bootstrap"; +import { FormattedDate } from "../../FormattedDate"; +import { useRegulationDetailsContext } from "./Provider"; + +function RegulationTabs() { + const { + activeTab, + setActiveTab, + hostData: { + regulatoryOverlayInfoListQuery: { data: regulatoryOverlayInfoList }, + waterRightInfoListQuery: { data: waterRightInfoList }, + }, + } = useRegulationDetailsContext(); + + const getFormattedBeneficialUses = (beneficialUses: string[]) => { + return beneficialUses.map((use) => + use !== beneficialUses[beneficialUses.length - 1] ? `${use}, ` : use, + ); + }; + + return ( + <> + setActiveTab(a === "right" ? a : "regulatory")} + activeKey={activeTab} + className="mb-3 custom-tabs" + > + + + + + + + + + + + + + + + + + + + {regulatoryOverlayInfoList?.map((source) => ( + + + + + + + + + + + + + + ))} + +
WaDE Overlay UUIDOverlay Native UUIDOverlay NameOverlay TypeWater Source TypeOverlay StatusOverlay StatueStatue LinkStatutory Effected DateStatutory End DateOverlay Statue Description
{source.waDEOverlayUuid}{source.overlayNativeId}{source.overlayName}{source.overlayType}{source.waterSourceType}{source.overlayStatus}{source.overlayStatue}{source.statueLink} + + {source.statutoryEffectedDate} + + + {source.statutoryEndDate} + {source.overlayStatueDescription}
+
+ + + + + + + + + + + + + + + + + {waterRightInfoList?.map((right) => ( + + + + + + + + + + + + ))} + +
WaDE Water Right IdentifierWater Right Native IDOwnerPriority DateExpiration DateLegal StatusFlow (CFS)Volume (AF)Beneficial Use
+ + {right.allocationUuid} + + {right.waterRightNativeId}{right.owner} + {right.priorityDate} + + {right.expirationDate} + {right.legalStatus}{right.flow}{right.volume}{getFormattedBeneficialUses(right.beneficialUses)}
+
+
+ + ); +} + +export default RegulationTabs; diff --git a/src/DashboardUI/src/components/details-page/regulation/hooks/useMapLegend.tsx b/src/DashboardUI/src/components/details-page/regulation/hooks/useMapLegend.tsx new file mode 100644 index 00000000..ea32bf74 --- /dev/null +++ b/src/DashboardUI/src/components/details-page/regulation/hooks/useMapLegend.tsx @@ -0,0 +1,32 @@ +/* +This file is largely duped from other existing maps in site and water rights pages. Potential opportunity to create a more reusable component for this. + */ + +import { useEffect } from "react"; +import { useMapContext } from "../../../../contexts/MapProvider"; +import { nldi } from "../../../../config/constants"; +import { MapLegendMarkerItem } from "../../../map/MapLegendItem"; +import { + mapLayerNames, + siteLocationPointsIconImage, + siteLocationPolygonFillColor, +} from "../../../../config/maps"; + +const defaultPolygonFillColors = { + layer: mapLayerNames.siteLocationsPolygonsLayer, + fillColor: siteLocationPolygonFillColor, +}; +const defaultMapMarkerIcons = { + layer: mapLayerNames.siteLocationsPointsLayer, + iconImages: siteLocationPointsIconImage, +}; + +export function useMapLegend() { + const { setLegend, setLayerFillColors, setLayerIconImages } = useMapContext(); + + useEffect(() => { + setLegend(<>); + setLayerFillColors(defaultPolygonFillColors); + setLayerIconImages(defaultMapMarkerIcons); + }, [setLegend, setLayerFillColors, setLayerIconImages]); +} diff --git a/src/DashboardUI/src/components/details-page/regulation/regulation.scss b/src/DashboardUI/src/components/details-page/regulation/regulation.scss new file mode 100644 index 00000000..bb85798d --- /dev/null +++ b/src/DashboardUI/src/components/details-page/regulation/regulation.scss @@ -0,0 +1,13 @@ +@import '../../../styles/colors'; + +.detail-page { + .regulation-card { + .card-header { + background-color: $card-header-color-red; + } + + .rotate-90 { + transform: rotate(90deg); + } + } +} diff --git a/src/DashboardUI/src/data-contracts/RegulationDetails.ts b/src/DashboardUI/src/data-contracts/RegulationDetails.ts new file mode 100644 index 00000000..f54b9a78 --- /dev/null +++ b/src/DashboardUI/src/data-contracts/RegulationDetails.ts @@ -0,0 +1,17 @@ +// TODO: Update with finalized attribute names +export interface RegulationDetails { + // Reporting Area Information + waDEAreaReportingUuid: string; + reportingAreaNativeId: string; + waDEReportingAreaName: string; + waDEOverlayAreaType: string; + nativeReportingAreaType: string; + reportingAreaName: string; + reportingAreaState: string; + areaLastUpdated: Date; + + // Managing Agency + managingAgencyOrganizationName: string; + managingAgencyState: string; + managingAgencyWebsite: string; +} diff --git a/src/DashboardUI/src/data-contracts/RegulatoryOverlayInfoListItem.ts b/src/DashboardUI/src/data-contracts/RegulatoryOverlayInfoListItem.ts new file mode 100644 index 00000000..04349c55 --- /dev/null +++ b/src/DashboardUI/src/data-contracts/RegulatoryOverlayInfoListItem.ts @@ -0,0 +1,14 @@ +// TODO update with finalized attribute names +export interface RegulatoryOverlayInfoListItem { + waDEOverlayUuid: string; + overlayNativeId: string; + overlayName: string; + overlayType: string; + waterSourceType: string; + overlayStatus: string; + overlayStatue: string; + statueLink: string; + statutoryEffectedDate: Date; + statutoryEndDate: Date; + overlayStatueDescription: string; +} diff --git a/src/DashboardUI/src/data-contracts/index.ts b/src/DashboardUI/src/data-contracts/index.ts index cb5f2676..7945b944 100644 --- a/src/DashboardUI/src/data-contracts/index.ts +++ b/src/DashboardUI/src/data-contracts/index.ts @@ -8,3 +8,5 @@ export * from './feature'; export * from './SiteDetails'; export * from './WaterRightInfoListItem'; export * from './WaterSourceInfoListItem'; +export * from './RegulationDetails'; +export * from './RegulatoryOverlayInfoListItem'; \ No newline at end of file diff --git a/src/DashboardUI/src/hooks/queries/index.ts b/src/DashboardUI/src/hooks/queries/index.ts index 2c2e475b..36326dd5 100644 --- a/src/DashboardUI/src/hooks/queries/index.ts +++ b/src/DashboardUI/src/hooks/queries/index.ts @@ -2,4 +2,5 @@ export * from './useNldiQuery'; export * from './useRiverBasinOptions'; export * from './useSiteQuery'; export * from './useSystemQuery'; -export * from './useWaterRightQuery'; \ No newline at end of file +export * from './useWaterRightQuery'; +export * from './useRegulationQuery'; \ No newline at end of file diff --git a/src/DashboardUI/src/hooks/queries/useRegulationQuery.ts b/src/DashboardUI/src/hooks/queries/useRegulationQuery.ts new file mode 100644 index 00000000..199949a3 --- /dev/null +++ b/src/DashboardUI/src/hooks/queries/useRegulationQuery.ts @@ -0,0 +1,37 @@ +import { useQuery } from "react-query"; +import { + getRegulationDetails, + getRegulatoryOverlayInfoList, +} from "../../accessors/regulationAccessor"; +import { UseQueryOptionsParameter } from "../../HelperTypes"; +import { RegulatoryOverlayInfoListItem } from "@data-contracts"; + +export function useRegulationDetails(areaUuid: string | undefined) { + return useQuery( + ["regulation.Details", areaUuid], + async () => await getRegulationDetails(areaUuid!), + { + enabled: !!areaUuid, + }, + ); +} + +type RegulatoryOverlayInfoListOptionsType = UseQueryOptionsParameter< + undefined, + RegulatoryOverlayInfoListItem[] +>; + +export function useRegulatoryOverlayInfoList( + areaUuid: string | undefined, + options?: RegulatoryOverlayInfoListOptionsType, +) { + const setOptions = { + ...options, + enabled: options?.enabled && !!areaUuid, + }; + return useQuery( + ["regulation.RegulatoryOverlayInfoList", areaUuid], + async () => await getRegulatoryOverlayInfoList(areaUuid!), + setOptions, + ); +} diff --git a/src/DashboardUI/src/pages/RegulationDetailsPage.tsx b/src/DashboardUI/src/pages/RegulationDetailsPage.tsx new file mode 100644 index 00000000..4db45378 --- /dev/null +++ b/src/DashboardUI/src/pages/RegulationDetailsPage.tsx @@ -0,0 +1,12 @@ +import { Layout } from '../components/details-page/regulation/Layout'; +import {RegulationDetailsProvider} from "../components/details-page/regulation/Provider"; + +function RegulationDetailsPage() { + return ( + + + + ) +} + +export default RegulationDetailsPage;