From 3b7cae486ce52eb98c23b7c1583a14ab278ebd15 Mon Sep 17 00:00:00 2001 From: Daniel da Silva Date: Thu, 19 Oct 2023 17:44:33 +0100 Subject: [PATCH] Implement analysis message and convert to control --- .../map/analysis-message-control.tsx | 180 ++++++++++++++++++ .../components/map/analysis-message.tsx | 94 --------- .../exploration/components/map/index.tsx | 5 +- 3 files changed, 182 insertions(+), 97 deletions(-) create mode 100644 app/scripts/components/exploration/components/map/analysis-message-control.tsx delete mode 100644 app/scripts/components/exploration/components/map/analysis-message.tsx diff --git a/app/scripts/components/exploration/components/map/analysis-message-control.tsx b/app/scripts/components/exploration/components/map/analysis-message-control.tsx new file mode 100644 index 000000000..2b7c15597 --- /dev/null +++ b/app/scripts/components/exploration/components/map/analysis-message-control.tsx @@ -0,0 +1,180 @@ +import React, { useEffect } from 'react'; +import { useAtomValue } from 'jotai'; +import styled, { css } from 'styled-components'; +import { glsp, themeVal } from '@devseed-ui/theme-provider'; +import { Button } from '@devseed-ui/button'; +import { VerticalDivider } from '@devseed-ui/toolbar'; +import { + CollecticonArrowLoop, + CollecticonChartLine, + CollecticonCircleInformation, + CollecticonSignDanger, + CollecticonXmarkSmall +} from '@devseed-ui/collecticons'; + +import { selectedIntervalAtom, timelineDatasetsAtom } from '../../atoms/atoms'; + +import useAois from '$components/common/map/controls/hooks/use-aois'; +import { calcFeatCollArea } from '$components/common/aoi/utils'; +import { formatDateRange } from '$utils/date'; +import { useAnalysisController } from '$components/exploration/hooks/use-analysis-data-request'; +import useThemedControl from '$components/common/map/controls/hooks/use-themed-control'; + +const AnalysisMessageWrapper = styled.div` + background-color: ${themeVal('color.base-400a')}; + color: ${themeVal('color.surface')}; + border-radius: ${themeVal('shape.rounded')}; + overflow: hidden; + display: flex; + align-items: center; + min-height: 2rem; + gap: ${glsp(0.5)}; + padding: ${glsp(0, 0.5)}; +`; + +interface MessageStatusIndicatorProps { + status: 'info' | 'analyzing' | 'obsolete'; +} +const MessageStatusIndicator = styled.div` + display: flex; + align-items: center; + padding: ${glsp(0, 0.5)}; + margin-left: ${glsp(-0.5)}; + align-self: stretch; + + ${({ status }) => { + switch (status) { + case 'info': + return css` + background-color: ${themeVal('color.info')}; + `; + case 'analyzing': + return css` + background-color: ${themeVal('color.success')}; + `; + case 'obsolete': + return css` + background-color: ${themeVal('color.danger')}; + `; + } + }} +`; +const MessageContent = styled.div``; +const MessageControls = styled.div` + display: flex; + gap: ${glsp(0.5)}; +`; + +export function AnalysisMessage() { + const { isObsolete, setObsolete, runAnalysis, cancelAnalysis, isAnalyzing } = + useAnalysisController(); + + const datasets = useAtomValue(timelineDatasetsAtom); + const datasetIds = datasets.map((d) => d.data.id); + + const { features } = useAois(); + const selectedInterval = useAtomValue(selectedIntervalAtom); + const dateLabel = + selectedInterval && + formatDateRange(selectedInterval.start, selectedInterval.end); + + const selectedFeatures = features.filter((f) => f.selected); + const selectedFeatureIds = selectedFeatures.map((f) => f.id).join(','); + + useEffect(() => { + // Set the analysis as obsolete when the selected features change. + setObsolete(); + }, [setObsolete, selectedFeatureIds]); + + if (!selectedFeatures.length) return null; + + const area = calcFeatCollArea({ + type: 'FeatureCollection', + features: selectedFeatures + }); + + return ( + + {isAnalyzing ? ( + isObsolete ? ( + + + + ) : ( + + + + ) + ) : ( + + + + )} + + {isAnalyzing ? ( + isObsolete ? ( + <> + Outdated! Refresh to analyze an area covering {area} km + 2 {dateLabel && ` from ${dateLabel}.`} + + ) : ( + <> + Analyzing an area covering {area} km2{' '} + {dateLabel && ` from ${dateLabel}`}. + + ) + ) : ( + <> + An area of {area} km2 {dateLabel && ` from ${dateLabel}`}{' '} + is selected. + + )} + + + + {isAnalyzing ? ( + <> + {isObsolete && ( + + )} + + + ) : ( + + )} + + + ); +} + +export function AnalysisMessageControl() { + useThemedControl(() => , { position: 'top-left' }); + + return null; +} diff --git a/app/scripts/components/exploration/components/map/analysis-message.tsx b/app/scripts/components/exploration/components/map/analysis-message.tsx deleted file mode 100644 index 5f3d19f0f..000000000 --- a/app/scripts/components/exploration/components/map/analysis-message.tsx +++ /dev/null @@ -1,94 +0,0 @@ -import React, { useEffect } from 'react'; -import { useAtomValue } from 'jotai'; -import styled from 'styled-components'; -import { Button } from '@devseed-ui/button'; -import { - CollecticonChartLine, - CollecticonXmarkSmall -} from '@devseed-ui/collecticons'; - -import { selectedIntervalAtom, timelineDatasetsAtom } from '../../atoms/atoms'; - -import useAois from '$components/common/map/controls/hooks/use-aois'; -import { calcFeatCollArea } from '$components/common/aoi/utils'; -import { formatDateRange } from '$utils/date'; -import { useAnalysisController } from '$components/exploration/hooks/use-analysis-data-request'; - -const AnalysisMessageWrapper = styled.div` - position: absolute; - background-color: #fff; - top: 2rem; - left: 5rem; - padding: 0.25rem 0.5rem; -`; - -export function AnalysisMessage() { - const { isObsolete, setObsolete, runAnalysis, cancelAnalysis, isAnalyzing } = - useAnalysisController(); - - const datasets = useAtomValue(timelineDatasetsAtom); - const datasetIds = datasets.map((d) => d.data.id); - - const { features } = useAois(); - const selectedInterval = useAtomValue(selectedIntervalAtom); - const dateLabel = - selectedInterval && - formatDateRange(selectedInterval.start, selectedInterval.end); - - const selectedFeatures = features.filter((f) => f.selected); - const selectedFeatureIds = selectedFeatures.map((f) => f.id).join(','); - - useEffect(() => { - // Set the analysis as obsolete when the selected features change. - setObsolete(); - }, [setObsolete, selectedFeatureIds]); - - if (!selectedFeatures.length) return null; - - const area = calcFeatCollArea({ - type: 'FeatureCollection', - features: selectedFeatures - }); - - return ( - - An area of {area} km2 is selected - {dateLabel && ` from ${dateLabel}`}.{' '} - {isAnalyzing ? ( - - ) : ( - - )} - {isAnalyzing && isObsolete && ( - <> - The current analysis is obsolete.{' '} - - - )} - - ); -} diff --git a/app/scripts/components/exploration/components/map/index.tsx b/app/scripts/components/exploration/components/map/index.tsx index 9342d85ad..6d89f6430 100644 --- a/app/scripts/components/exploration/components/map/index.tsx +++ b/app/scripts/components/exploration/components/map/index.tsx @@ -9,7 +9,7 @@ import { TimelineDatasetSuccess } from '../../types.d.ts'; import { Layer } from './layer'; -import { AnalysisMessage } from './analysis-message'; +import { AnalysisMessageControl } from './analysis-message-control'; import Map, { Compare } from '$components/common/map'; import { Basemap } from '$components/common/map/style-generators/basemap'; @@ -53,8 +53,6 @@ export function ExplorationMap(props: { comparing: boolean }) { return ( - - {/* Map layers */} +