From 76eb99b9e7ab2b089728dd233c50f900f84c52dd Mon Sep 17 00:00:00 2001 From: Stefanos Hadjipetrou Date: Mon, 10 Apr 2023 13:54:04 +0300 Subject: [PATCH] feat: breadcrumbs refactor --- .../Treemap/components/tooltip/index.tsx | 1 + .../components/tooltip/index.tsx | 3 + .../Charts/common/breadcrumbs/index.tsx | 144 +++++++++------ .../Charts/common/dialogBox/index.tsx | 34 +++- src/app/components/PageHeader/index.tsx | 2 - .../components/datapath/index.tsx | 12 -- src/app/components/ToolBoxPanel/index.tsx | 106 +++++------ .../useClearDataPathStepsOnDatasetChange.tsx | 52 +++++- src/app/modules/about-module/index.tsx | 13 +- .../modules/country-detail-module/index.tsx | 68 +++---- src/app/modules/datasets-module/index.tsx | 10 +- src/app/modules/documents-module/index.tsx | 27 +-- src/app/modules/grant-detail-module/index.tsx | 125 ++++++------- src/app/modules/grants-module/index.tsx | 50 +++-- src/app/modules/landing-module/index.tsx | 9 +- .../modules/partner-detail-module/index.tsx | 34 ++-- src/app/modules/results-module/index.tsx | 46 +++-- src/app/modules/viz-module/index.tsx | 102 +---------- .../sub-modules/allocations/index.tsx | 66 ++++--- .../budgets/flow/data-wrappers/generic.tsx | 81 ++++++-- .../flow/data-wrappers/grantDetail.tsx | 82 +++++++-- .../flow/data-wrappers/locationDetail.tsx | 75 +++++++- .../flow/data-wrappers/partnerDetail.tsx | 75 +++++++- .../sub-modules/budgets/flow/index.tsx | 138 +++++--------- .../sub-modules/budgets/geomap/index.tsx | 4 +- .../time-cycle/data-wrappers/generic.tsx | 35 ++-- .../time-cycle/data-wrappers/grantDetail.tsx | 35 ++-- .../data-wrappers/locationDetail.tsx | 26 ++- .../data-wrappers/partnerDetail.tsx | 26 ++- .../sub-modules/budgets/time-cycle/index.tsx | 132 +++++-------- .../sub-modules/eligibility/index.tsx | 22 +++ .../table/data-wrappers/generic.tsx | 22 +++ .../disbursed/data-wrappers/generic.tsx | 45 +++-- .../data-wrappers/locationDetail.tsx | 100 +++++----- .../disbursed/data-wrappers/partnerDetail.tsx | 100 +++++----- .../investments/disbursed/index.tsx | 104 +++++------ .../sub-modules/investments/geomap/index.tsx | 4 +- .../time-cycle/data-wrappers/generic.tsx | 108 +++++++++-- .../time-cycle/data-wrappers/grantDetail.tsx | 8 +- .../data-wrappers/partnerDetail.tsx | 30 ++- .../investments/time-cycle/index.tsx | 173 ++++++++++-------- .../pledgescontributions/geomap/index.tsx | 28 +++ .../pledgescontributions/table/index.tsx | 29 ++- .../pledgescontributions/time-cycle/index.tsx | 27 +++ .../pledgescontributions/treemap/index.tsx | 28 +++ .../action-reducers/sync/dataPath/index.ts | 12 -- .../api/action-reducers/viz/commitment.ts | 4 + .../viz/disbursementsTimeCycle.ts | 6 + .../state/api/action-reducers/viz/signed.ts | 4 + src/app/state/api/interfaces/index.ts | 5 +- src/app/state/store/index.ts | 8 +- 51 files changed, 1415 insertions(+), 1065 deletions(-) diff --git a/src/app/components/Charts/Budgets/Treemap/components/tooltip/index.tsx b/src/app/components/Charts/Budgets/Treemap/components/tooltip/index.tsx index 3a6e03b74..f9c63365f 100644 --- a/src/app/components/Charts/Budgets/Treemap/components/tooltip/index.tsx +++ b/src/app/components/Charts/Budgets/Treemap/components/tooltip/index.tsx @@ -13,6 +13,7 @@ export function TreemapTooltip(props: TreemapTooltipProps) {
state.DataPathSteps.steps); + const setDataPathSteps = useStoreActions( + (actions) => actions.DataPathSteps.setSteps + ); + const setActiveStep = useStoreActions( + (actions) => actions.DataPathActiveStep.setStep + ); + const clearActiveStep = useStoreActions( + (actions) => actions.DataPathActiveStep.clear + ); + + function onItemClick(index: number, item: DrilldownModelUpdated) { + if (index > 0) { + const fItemIndex = findIndex(dataPathSteps, { id: item.id }); + if (fItemIndex > -1) { + setDataPathSteps(dataPathSteps.slice(0, fItemIndex + 1)); + } + setActiveStep(item); + if ( + item && + item.path !== `${history.location.pathname}${history.location.search}` + ) { + history.push(item.path); + } + } else { + setDataPathSteps([]); + clearActiveStep(); + history.push("/"); + } + } + return (
- {breadCrumbList && - breadCrumbList.map((item, index) => ( -
( +
+ + {index === dataPathSteps.length ? null : ( +
{ - if (item?.path !== "#") { - history.push(item.path); - } - setBreadCrumbList([...breadCrumbList.slice(0, index + 1)]); - }} > - {item?.name} - - {index === breadCrumbList.length - 1 ? null : ( -
- -
- )} -
- ))} + +
+ )} +
+ ))}
); diff --git a/src/app/components/Charts/common/dialogBox/index.tsx b/src/app/components/Charts/common/dialogBox/index.tsx index ff2abc74c..56054cb1f 100644 --- a/src/app/components/Charts/common/dialogBox/index.tsx +++ b/src/app/components/Charts/common/dialogBox/index.tsx @@ -1,35 +1,53 @@ import React from "react"; -import { modalContainercss, overlaycss } from "./style"; +import { useStoreActions } from "app/state/store/hooks"; +import { + overlaycss, + modalContainercss, +} from "app/components/Charts/common/dialogBox/style"; interface Props { display: { display: boolean; code: string; + pageType?: string; clickthroughPath?: string; }; setDisplay: React.Dispatch< React.SetStateAction<{ display: boolean; code: string; + pageType?: string; clickthroughPath?: string; }> >; - handleClick: React.MouseEventHandler | undefined; + handleClick?: () => void; } + export default function ReRouteDialogBox(props: Props) { + const clearDataPathSteps = useStoreActions( + (actions) => actions.DataPathSteps.clear + ); + + const handleYesClick = () => { + clearDataPathSteps(); + if (props.handleClick) { + props.handleClick(); + } + }; + return (
props.setDisplay({ ...props.display, display: false })} css={overlaycss} - >
+ />

- You are navigating to a grant page. + You are navigating to a {props.display.pageType || "grant"} page.

- Continue to grant page? + Continue to {props.display.pageType || "grant"} page?

-
- No + No -
diff --git a/src/app/components/PageHeader/index.tsx b/src/app/components/PageHeader/index.tsx index d3a10d7cc..8526d2bf2 100644 --- a/src/app/components/PageHeader/index.tsx +++ b/src/app/components/PageHeader/index.tsx @@ -5,7 +5,6 @@ import { appColors } from "app/theme"; import Grid from "@material-ui/core/Grid"; import { css } from "styled-components/macro"; import { useHistory } from "react-router-dom"; -import { BreadcrumbModel } from "app/interfaces"; import Container from "@material-ui/core/Container"; import useMediaQuery from "@material-ui/core/useMediaQuery"; import { TabProps } from "app/components/PageHeader/components/tabs/data"; @@ -17,7 +16,6 @@ interface PageHeaderProps { tabs?: TabProps[]; isDetail?: boolean; partialTitle?: string; - breadcrumbs: BreadcrumbModel[]; } const styles = { diff --git a/src/app/components/ToolBoxPanel/components/datapath/index.tsx b/src/app/components/ToolBoxPanel/components/datapath/index.tsx index dd43e7b24..d90045bfd 100644 --- a/src/app/components/ToolBoxPanel/components/datapath/index.tsx +++ b/src/app/components/ToolBoxPanel/components/datapath/index.tsx @@ -23,9 +23,6 @@ export function DataPathPanel() { const setActiveStep = useStoreActions( (actions) => actions.DataPathActiveStep.setStep ); - const setShowDataPath = useStoreActions( - (state) => state.DataPathPanelVisibilityState.setValue - ); function onItemClick(index: number, item: DrilldownModelUpdated) { if (index > 0) { @@ -66,15 +63,6 @@ export function DataPathPanel() { `} > Your data path - setShowDataPath(false)} - > - -
{dataPathSteps.length > 0 && (
diff --git a/src/app/components/ToolBoxPanel/index.tsx b/src/app/components/ToolBoxPanel/index.tsx index 4f5efd598..8264f0d5b 100644 --- a/src/app/components/ToolBoxPanel/index.tsx +++ b/src/app/components/ToolBoxPanel/index.tsx @@ -12,7 +12,6 @@ import { TriangleXSIcon } from "app/assets/icons/TriangleXS"; import { filterExpandedGroup } from "app/state/recoil/atoms"; import CloseOutlinedIcon from "@material-ui/icons/CloseOutlined"; import { useMediaQuery, IconButton, Slide } from "@material-ui/core"; -import { useStoreActions, useStoreState } from "app/state/store/hooks"; import { FilterGroupProps } from "app/components/ToolBoxPanel/components/filters/data"; import { SubToolBoxPanel } from "app/components/ToolBoxPanel/components/subtoolboxpanel"; import { ToolBoxPanelIconButtons } from "app/components/ToolBoxPanel/components/iconbuttons"; @@ -37,14 +36,6 @@ export function ToolBoxPanel(props: ToolBoxPanelProps) { document.body.scrollHeight > document.body.clientHeight ); - const dataPathSteps = useStoreState((state) => state.DataPathSteps.steps); - const showDataPath = useStoreState( - (state) => state.DataPathPanelVisibilityState.value - ); - const setShowDataPath = useStoreActions( - (state) => state.DataPathPanelVisibilityState.setValue - ); - React.useLayoutEffect(() => { setVisibleVScrollbar( document.body.scrollHeight > document.body.clientHeight @@ -57,12 +48,6 @@ export function ToolBoxPanel(props: ToolBoxPanelProps) { ); }, [history.location.pathname]); - React.useEffect(() => { - if (dataPathSteps.length < 2 && showDataPath) { - setShowDataPath(false); - } - }, [dataPathSteps]); - const isMobile = useMediaQuery("(max-width: 767px)"); const isSmallScreen = useMediaQuery("(max-width: 960px)"); @@ -180,57 +165,50 @@ export function ToolBoxPanel(props: ToolBoxPanelProps) {
)} - {!showDataPath ? ( - - {isMobile && ( -
-
- Toolbox -
- props.onCloseBtnClick()} - > - - -
- )} - {!expandedGroup && ( - +
+ Toolbox +
+ props.onCloseBtnClick()} + > + - )} - -
- ) : ( - "" - // + +
+ )} + {!expandedGroup && ( + )} +
diff --git a/src/app/hooks/useClearDataPathStepsOnDatasetChange.tsx b/src/app/hooks/useClearDataPathStepsOnDatasetChange.tsx index f687560d4..50cda0f71 100644 --- a/src/app/hooks/useClearDataPathStepsOnDatasetChange.tsx +++ b/src/app/hooks/useClearDataPathStepsOnDatasetChange.tsx @@ -1,6 +1,8 @@ import React from "react"; +import find from "lodash/find"; import { useHistory } from "react-router-dom"; -import { useStoreActions } from "app/state/store/hooks"; +import { DrilldownModelUpdated } from "app/interfaces"; +import { useStoreActions, useStoreState } from "app/state/store/hooks"; /* app route templates @@ -16,14 +18,28 @@ import { useStoreActions } from "app/state/store/hooks"; /grant/code/period/vizType/subType */ +const irrelevantPaths = [ + "/", + "/about", + "/datasets", + "/grants", + "/results", + "/documents", +]; + export function useClearDataPathStepsOnDatasetChange() { const history = useHistory(); const [prevLocation, setPrevLocation] = React.useState({ type: "", pathname: "", + view: "", }); + const dataPathSteps = useStoreState((state) => state.DataPathSteps.steps); + const setDataPathSteps = useStoreActions( + (actions) => actions.DataPathSteps.setSteps + ); const clearDataPathSteps = useStoreActions( (actions) => actions.DataPathSteps.clear ); @@ -32,28 +48,54 @@ export function useClearDataPathStepsOnDatasetChange() { const newLocation = { type: "", pathname: location.pathname, + view: "", }; const splits = location.pathname.split("/"); if (splits.length > 1) { switch (splits[1]) { case "viz": newLocation.type = splits[2]; + newLocation.view = splits[3]; break; case "location": - newLocation.type = splits[3]; - break; case "partner": newLocation.type = splits[3]; + newLocation.view = splits[4]; break; case "grant": newLocation.type = splits[4]; + newLocation.view = splits[5]; break; default: newLocation.type = ""; } } - if (newLocation.type !== prevLocation.type) { - clearDataPathSteps(); + if (dataPathSteps.length > 0) { + if ( + newLocation.type !== prevLocation.type || + irrelevantPaths.indexOf(newLocation.pathname) > -1 + ) { + clearDataPathSteps(); + } else if ( + newLocation.type === prevLocation.type && + newLocation.view !== prevLocation.view + ) { + const sliceIndex = find( + dataPathSteps, + (step: DrilldownModelUpdated) => { + return Boolean(step.drilldownVizSelected); + } + ) + ? 2 + : 1; + const newDataPathSteps = dataPathSteps.slice(0, sliceIndex * -1); + if (newDataPathSteps.length > 0) { + newDataPathSteps[newDataPathSteps.length - 1].path = + location.pathname; + console.log("newDataPathSteps", newDataPathSteps); + setDataPathSteps(newDataPathSteps); + } + } } setPrevLocation(newLocation); }, [history.location]); diff --git a/src/app/modules/about-module/index.tsx b/src/app/modules/about-module/index.tsx index 125c4c32f..8b0f2f5ce 100644 --- a/src/app/modules/about-module/index.tsx +++ b/src/app/modules/about-module/index.tsx @@ -37,18 +37,7 @@ export default function About() { `} > {!isMobile && ( - + )}
state.DataPathSteps.steps); + const addDataPathSteps = useStoreActions( + (actions) => actions.DataPathSteps.addSteps + ); + // api call & data const fetchLocationInfoData = useStoreActions( (store) => store.LocationDetailInfo.fetch @@ -156,38 +154,30 @@ export default function CountryDetail() { return 0; } - const breadcrumbID = React.useMemo(() => v4(), []); - useUpdateEffect(() => { - if (breadCrumbList.length === 0) { - setBreadCrumbList([ - { name: "Datasets", path: "/", id: v4() }, - { - name: locationInfoData.locationName, - path: location.pathname, - id: v4(), - }, - ]); - } else if ( - !breadCrumbList.find((item) => item.id === breadcrumbID) && - components + if ( + locationInfoData && + locationInfoData.locationName && + !dataPathSteps.find((item) => item.id === "location") ) { - setBreadCrumbList([ - ...breadCrumbList, + addDataPathSteps([ { - name: components.toString(), + id: "location", + name: locationInfoData.locationName, path: location.pathname, - id: breadcrumbID, }, ]); } }, [locationInfoData]); - const tabs = countryDetailTabs; - - if (params.code.length > 3) { - tabs[3].tabs = tabs[3].tabs?.slice(0, 1); - } + const tabs = React.useMemo(() => { + if (params.code.length > 3) { + const updatedTabs = countryDetailTabs; + updatedTabs[3].tabs = updatedTabs[3].tabs?.slice(0, 1); + return updatedTabs; + } + return countryDetailTabs; + }, [params.code]); return (
{loading && } - + {isMobile && ( diff --git a/src/app/modules/datasets-module/index.tsx b/src/app/modules/datasets-module/index.tsx index dcb8fa484..30af2bdcb 100644 --- a/src/app/modules/datasets-module/index.tsx +++ b/src/app/modules/datasets-module/index.tsx @@ -26,15 +26,7 @@ export default function Datasets() { return (
{!isMobile && ( - + )} diff --git a/src/app/modules/documents-module/index.tsx b/src/app/modules/documents-module/index.tsx index 1d97a02f8..f0be10dc7 100644 --- a/src/app/modules/documents-module/index.tsx +++ b/src/app/modules/documents-module/index.tsx @@ -1,9 +1,6 @@ /* third-party */ import React from "react"; -import { v4 } from "uuid"; import get from "lodash/get"; -import { useRecoilState } from "recoil"; -import { breadCrumbItems } from "app/state/recoil/atoms"; import useMediaQuery from "@material-ui/core/useMediaQuery"; import TablePagination from "@material-ui/core/TablePagination"; import { useTitle, useDebounce, useUpdateEffect } from "react-use"; @@ -16,7 +13,6 @@ import { PageLoader } from "app/modules/common/page-loader"; import { DocumentsSubModule } from "app/modules/common/documents"; import BreadCrumbs from "app/components/Charts/common/breadcrumbs"; import { PageTopSpacer } from "app/modules/common/page-top-spacer"; -import { useDatasetMenuItems } from "app/hooks/useDatasetMenuItems"; import { getAPIFormattedFilters } from "app/utils/getAPIFormattedFilters"; import { ExpandableTableRowProps } from "app/components/Table/Expandable/data"; import { pathnameToFilterGroups } from "app/components/ToolBoxPanel/components/filters/data"; @@ -24,11 +20,13 @@ import { pathnameToFilterGroups } from "app/components/ToolBoxPanel/components/f export default function DocumentsModule() { useTitle("The Data Explorer - Documents"); const vizWrapperRef = React.useRef(null); - const datasetMenuItems = useDatasetMenuItems(); const [search, setSearch] = React.useState(""); const isMobile = useMediaQuery("(max-width: 767px)"); const [openToolboxPanel, setOpenToolboxPanel] = React.useState(!isMobile); - const [_, setBreadCrumbList] = useRecoilState(breadCrumbItems); + + const addDataPathSteps = useStoreActions( + (actions) => actions.DataPathSteps.addSteps + ); // api call & data const fetchData = useStoreActions((store) => store.Documents.fetch); @@ -106,12 +104,11 @@ export default function DocumentsModule() { } React.useEffect(() => { - setBreadCrumbList([ - { name: "Datasets", path: "/", id: v4() }, + addDataPathSteps([ { name: "Documents", path: location.pathname, - id: v4(), + id: "documents", }, ]); }, []); @@ -128,17 +125,7 @@ export default function DocumentsModule() { `} > - + (); const [openToolboxPanel, setOpenToolboxPanel] = React.useState( !isMobile && params.vizType !== "overview" ); - const [breadCrumbList, setBreadCrumbList] = useRecoilState(breadCrumbItems); + + const dataPathSteps = useStoreState((state) => state.DataPathSteps.steps); + const addDataPathSteps = useStoreActions( + (actions) => actions.DataPathSteps.addSteps + ); + const setDataPathSteps = useStoreActions( + (actions) => actions.DataPathSteps.setSteps + ); // api call & data const fetchGrantInfoData = useStoreActions( @@ -59,15 +62,18 @@ export default function GrantDetail() { get(state.GrantDetailPeriods.data, "data", []) as GrantDetailPeriod[] ); - const selectedPeriod = find( - periods, - (p: GrantDetailPeriod) => p.number.toString() === params.period - ) || { startDate: "", endDate: "" }; + const selectedPeriod = React.useMemo(() => { + return ( + find( + periods, + (p: GrantDetailPeriod) => p.number.toString() === params.period + ) || { startDate: "", endDate: "" } + ); + }, [periods, params.period]); const formatPeriod = (date: string) => { return date.split("-")[0]; }; - console.log(formatPeriod(selectedPeriod.startDate), "start year"); const grantInfoData = useStoreState((state) => get(state.GrantDetailInfo.data, "data[0]", { @@ -136,54 +142,47 @@ export default function GrantDetail() { if (openToolboxPanel && widthThreshold < 0) return 1; return 0; } - const breadCrumbId = React.useMemo(() => v4(), []); useUpdateEffect(() => { - if (grantInfoData) { - if (breadCrumbList.length === 0) { - setBreadCrumbList([ - { - name: "Datasets", - path: "/", - id: breadCrumbId, - }, + if ( + grantInfoData && + grantInfoData.code && + !dataPathSteps.find((item) => item.id === "grant") + ) { + addDataPathSteps([ + { + id: "grant", + name: `${params.code} - ${formatPeriod( + selectedPeriod.startDate + )} - ${formatPeriod(selectedPeriod.endDate)}`, + path: location.pathname, + }, + ]); + } + }, [grantInfoData]); - { - name: grantInfoData.code, - path: location.pathname, - id: v4(), - }, - ]); - } else { - if (!breadCrumbList.find((list) => list.id === breadCrumbId)) { - setBreadCrumbList([ - { - name: "Datasets", - path: "/", - id: v4(), - }, - { - name: "Grant Implementation: Grants", - path: "/grants", - id: v4(), - }, - { - name: `${grantInfoData.code} ${ - selectedPeriod ? "-" : "" - } ${formatPeriod(selectedPeriod.startDate)} - ${formatPeriod( - selectedPeriod.endDate - )}`, - path: location.pathname, - id: v4(), - vizLevel: breadCrumbList[breadCrumbList.length - 1]?.vizLevel, - vizSelected: - breadCrumbList[breadCrumbList.length - 1]?.vizSelected, - }, - ]); - } - } + useUpdateEffect(() => { + const value = `${params.code} - ${formatPeriod( + selectedPeriod.startDate + )} - ${formatPeriod(selectedPeriod.endDate)}`; + const fIndex = findIndex(dataPathSteps, { + id: "grant", + }); + if (fIndex > -1) { + const newDataPathSteps = [...dataPathSteps]; + newDataPathSteps[fIndex].name = value; + newDataPathSteps[fIndex].path = location.pathname; + setDataPathSteps(newDataPathSteps); + } else { + addDataPathSteps([ + { + id: "grant", + name: value, + path: location.pathname, + }, + ]); } - }, [grantInfoData, selectedPeriod]); + }, [selectedPeriod]); return (
- + {isMobile && ( diff --git a/src/app/modules/grants-module/index.tsx b/src/app/modules/grants-module/index.tsx index ea6aac5ea..522279062 100644 --- a/src/app/modules/grants-module/index.tsx +++ b/src/app/modules/grants-module/index.tsx @@ -1,13 +1,11 @@ /* third-party */ import React from "react"; -import { v4 } from "uuid"; import get from "lodash/get"; -import { useRecoilState } from "recoil"; +import find from "lodash/find"; import { useLocation } from "react-router-dom"; import { useCMSData } from "app/hooks/useCMSData"; import { useMediaQuery } from "@material-ui/core"; import Pagination from "@material-ui/lab/Pagination"; -import { breadCrumbItems } from "app/state/recoil/atoms"; import { useStoreActions, useStoreState } from "app/state/store/hooks"; import { useTitle, @@ -22,7 +20,6 @@ import { ToolBoxPanel } from "app/components/ToolBoxPanel"; import { PageLoader } from "app/modules/common/page-loader"; import BreadCrumbs from "app/components/Charts/common/breadcrumbs"; import { PageTopSpacer } from "app/modules/common/page-top-spacer"; -import { useDatasetMenuItems } from "app/hooks/useDatasetMenuItems"; import { GrantListItemModel } from "app/modules/grants-module/data"; import { Search } from "app/modules/grants-module/components/Search"; import { NoDataLabel } from "app/components/Charts/common/nodatalabel"; @@ -41,7 +38,6 @@ interface GrantsModuleProps { export default function GrantsModule(props: GrantsModuleProps) { const location = useLocation(); const cmsData = useCMSData({ returnData: true }); - const [_, setBreadCrumbList] = useRecoilState(breadCrumbItems); useTitle( `${get(cmsData, "modulesGrants.titleStart", "")}${ @@ -56,7 +52,6 @@ export default function GrantsModule(props: GrantsModuleProps) { } ${get(cmsData, "modulesGrants.titleEnd", "")}` ); const vizWrapperRef = React.useRef(null); - const datasetMenuItems = useDatasetMenuItems(); const [page, setPage] = React.useState(1); const [pages, setPages] = React.useState(1); const [search, setSearch] = React.useState(""); @@ -68,6 +63,11 @@ export default function GrantsModule(props: GrantsModuleProps) { props.detailFilterType ); + const dataPathSteps = useStoreState((state) => state.DataPathSteps.steps); + const setDataPathSteps = useStoreActions( + (actions) => actions.DataPathSteps.setSteps + ); + // api call & data const fetchData = useStoreActions((store) => store.GrantsList.fetch); const data = useStoreState( @@ -111,14 +111,22 @@ export default function GrantsModule(props: GrantsModuleProps) { }; React.useEffect(() => { - setBreadCrumbList([ - { name: "Datasets", path: "/", id: v4() }, - { - name: "Grant Implementation: Grants", - path: location.pathname, - id: v4(), - }, - ]); + setTimeout(() => { + if ( + dataPathSteps.length === 0 || + !find(dataPathSteps, { + name: "Grant Implementation: Grants", + }) + ) { + setDataPathSteps([ + { + name: "Grant Implementation: Grants", + path: location.pathname, + id: "grants", + }, + ]); + } + }, 500); }, []); useEffectOnce(() => { @@ -187,19 +195,7 @@ export default function GrantsModule(props: GrantsModuleProps) { {(isLoading || loading) && } {!props.code && ( <> - + actions.DataPathSteps.setSteps + ); React.useEffect(() => { document.body.style.background = appColors.COMMON.SECONDARY_COLOR_7; - setBreadCrumbList([]); + setDataPathSteps([]); }, []); return ; diff --git a/src/app/modules/partner-detail-module/index.tsx b/src/app/modules/partner-detail-module/index.tsx index f05b84c07..91255e323 100644 --- a/src/app/modules/partner-detail-module/index.tsx +++ b/src/app/modules/partner-detail-module/index.tsx @@ -1,9 +1,7 @@ /* third-party */ import React from "react"; -import { v4 } from "uuid"; import get from "lodash/get"; import { appColors } from "app/theme"; -import { useRecoilState } from "recoil"; import { useMediaQuery } from "@material-ui/core"; import { useTitle, useUpdateEffect } from "react-use"; import { useStoreActions, useStoreState } from "app/state/store/hooks"; @@ -17,11 +15,9 @@ import { /* project */ import GrantsModule from "app/modules/grants-module"; import { PageHeader } from "app/components/PageHeader"; -import { breadCrumbItems } from "app/state/recoil/atoms"; import { ToolBoxPanel } from "app/components/ToolBoxPanel"; import { PageTopSpacer } from "app/modules/common/page-top-spacer"; import BreadCrumbs from "app/components/Charts/common/breadcrumbs"; -import { useDatasetMenuItems } from "app/hooks/useDatasetMenuItems"; import { MobileViewControl } from "app/components/Mobile/ViewsControl"; import { BudgetsGeoMap } from "app/modules/viz-module/sub-modules/budgets/geomap"; import { partnerDetailTabs } from "app/components/PageHeader/components/tabs/data"; @@ -41,10 +37,8 @@ export default function PartnerDetail() { useTitle("The Data Explorer - Partner"); const location = useLocation(); const vizWrapperRef = React.useRef(null); - const datasetMenuItems = useDatasetMenuItems(); const isMobile = useMediaQuery("(max-width: 767px)"); const [openToolboxPanel, setOpenToolboxPanel] = React.useState(!isMobile); - const [breadCrumbList, setBreadCrumbList] = useRecoilState(breadCrumbItems); const params = useParams<{ code: string; @@ -52,6 +46,11 @@ export default function PartnerDetail() { subType?: string; }>(); + const dataPathSteps = useStoreState((state) => state.DataPathSteps.steps); + const addDataPathSteps = useStoreActions( + (actions) => actions.DataPathSteps.addSteps + ); + // api call & data const fetchPartnerInfoData = useStoreActions( (store) => store.PartnerDetailInfo.fetch @@ -98,13 +97,16 @@ export default function PartnerDetail() { } useUpdateEffect(() => { - if (!breadCrumbList.find((item) => item.path === location.pathname)) { - setBreadCrumbList([ - { name: "Datasets", path: "/", id: v4() }, + if ( + partnerInfoData && + partnerInfoData.partnerName && + !dataPathSteps.find((item) => item.id === partnerInfoData.partnerName) + ) { + addDataPathSteps([ { + id: "partner", name: partnerInfoData.partnerName, path: location.pathname, - id: v4(), }, ]); } @@ -124,18 +126,8 @@ export default function PartnerDetail() { {isMobile && ( diff --git a/src/app/modules/results-module/index.tsx b/src/app/modules/results-module/index.tsx index af9916b3c..99de08412 100644 --- a/src/app/modules/results-module/index.tsx +++ b/src/app/modules/results-module/index.tsx @@ -1,21 +1,19 @@ /* third-party */ import React from "react"; -import { v4 } from "uuid"; import get from "lodash/get"; +import find from "lodash/find"; import { appColors } from "app/theme"; +import uniqueId from "lodash/uniqueId"; import { useLocation } from "react-router-dom"; -import { useRecoilState } from "recoil"; import useMediaQuery from "@material-ui/core/useMediaQuery"; import { useTitle, useDebounce, useUpdateEffect } from "react-use"; import { useStoreActions, useStoreState } from "app/state/store/hooks"; /* project */ import { PageHeader } from "app/components/PageHeader"; -import { breadCrumbItems } from "app/state/recoil/atoms"; import { ToolBoxPanel } from "app/components/ToolBoxPanel"; import { DataList } from "app/modules/results-module/datalist"; import BreadCrumbs from "app/components/Charts/common/breadcrumbs"; import { PageTopSpacer } from "app/modules/common/page-top-spacer"; -import { useDatasetMenuItems } from "app/hooks/useDatasetMenuItems"; import { ResultListItemModel } from "app/modules/results-module/data"; import { getAPIFormattedFilters } from "app/utils/getAPIFormattedFilters"; import { pathnameToFilterGroups } from "app/components/ToolBoxPanel/components/filters/data"; @@ -24,11 +22,9 @@ export default function ResultsModule() { useTitle("The Data Explorer - Results"); const location = useLocation(); const vizWrapperRef = React.useRef(null); - const datasetMenuItems = useDatasetMenuItems(); const [search, setSearch] = React.useState(""); const isMobile = useMediaQuery("(max-width: 767px)"); const [openToolboxPanel, setOpenToolboxPanel] = React.useState(!isMobile); - const [_, setBreadCrumbList] = useRecoilState(breadCrumbItems); const selectedYear = useStoreState( (state) => state.ToolBoxPanelResultsYearState.value @@ -45,18 +41,28 @@ export default function ResultsModule() { ); const isLoading = useStoreState((state) => state.ResultsList.loading); const appliedFilters = useStoreState((state) => state.AppliedFiltersState); + const dataPathSteps = useStoreState((state) => state.DataPathSteps.steps); + const addDataPathSteps = useStoreActions( + (actions) => actions.DataPathSteps.addSteps + ); React.useEffect(() => { document.body.style.background = appColors.COMMON.PAGE_BACKGROUND_COLOR_1; fetchYearOptionsData({}); - setBreadCrumbList([ - { name: "Datasets", path: "/", id: v4() }, - { - name: "Annual results", - path: location.pathname, - id: v4(), - }, - ]); + if ( + dataPathSteps.length === 0 || + !find(dataPathSteps, { + name: "Annual Results", + }) + ) { + addDataPathSteps([ + { + id: uniqueId(), + name: "Annual Results", + path: `${location.pathname}${location.search}`, + }, + ]); + } }, []); React.useEffect(() => { @@ -139,17 +145,7 @@ export default function ResultsModule() { `} > - + (); const [openToolboxPanel, setOpenToolboxPanel] = React.useState(!isMobile); - const [_, setBreadCrumbList] = useRecoilState(breadCrumbItems); - const [subTypeCopy, setSubTypeCopy] = React.useState(params.subType); React.useEffect(() => { document.body.style.background = appColors.COMMON.PAGE_BACKGROUND_COLOR_1; @@ -78,78 +70,6 @@ export default function VizModule() { return 0; } - const vizType = `${params.vizType - .slice(0, 1) - .toUpperCase()}${params.vizType.slice(1)}`; - - const vizTypePretext = (value: string) => { - const localVizType = startCase(value); - - switch (vizType) { - case "Pledges-contributions": - return `Resource Mobilization: ${localVizType}`; - case "Allocations": - return `Access to funding: ${localVizType}`; - case "Eligibility": - return `Access to funding: ${localVizType}`; - case "Documents": - return "Documents"; - case "Results": - return "Results"; - default: - return `Grant Implementation: ${localVizType} `; - } - }; - - React.useEffect(() => { - setBreadCrumbList((list) => { - if (list[list.length - 1]?.vizSelected) { - return [ - { name: "Datasets", path: "/", id: v4() }, - { - name: vizTypePretext(vizType), - path: list[list.length - 2].path, - id: v4(), - }, - { - name: list[list.length - 1]?.name || "", - path: location.pathname, - id: v4(), - vizLevel: list[list.length - 1]?.vizLevel, - vizSelected: list[list.length - 1]?.vizSelected, - }, - ]; - } else { - return [ - { name: "Datasets", path: "/", id: v4() }, - { - name: vizTypePretext(vizType), - path: location.pathname, - id: v4(), - vizSelected: undefined, - vizLevel: 0, - }, - ]; - } - }); - }, [vizType]); - - React.useEffect(() => { - if (params.subType !== subTypeCopy) { - setBreadCrumbList([ - { name: "Datasets", path: "/", id: v4() }, - { - name: vizTypePretext(vizType), - path: location.pathname, - id: v4(), - vizSelected: undefined, - vizLevel: 0, - }, - ]); - setSubTypeCopy(params.subType); - } - }, [params.subType]); - return (
- + {isMobile && ( diff --git a/src/app/modules/viz-module/sub-modules/allocations/index.tsx b/src/app/modules/viz-module/sub-modules/allocations/index.tsx index a7b7fc341..dfaa4bdd1 100644 --- a/src/app/modules/viz-module/sub-modules/allocations/index.tsx +++ b/src/app/modules/viz-module/sub-modules/allocations/index.tsx @@ -16,6 +16,7 @@ import { isTouchDevice } from "app/utils/isTouchDevice"; import { getIso3FromName } from "app/utils/getIso3FromName"; import { PageLoader } from "app/modules/common/page-loader"; import { XsContainer } from "app/components/Charts/common/styles"; +import ReRouteDialogBox from "app/components/Charts/common/dialogBox"; import { formatFinancialValue } from "app/utils/formatFinancialValue"; import { NoDataLabel } from "app/components/Charts/common/nodatalabel"; import { useStoreActions, useStoreState } from "app/state/store/hooks"; @@ -108,6 +109,10 @@ export function AllocationsModule(props: AllocationsModuleProps) { undefined ); const [xsTooltipData, setXsTooltipData] = React.useState(null); + const [reRouteDialog, setReRouteDialog] = React.useState({ + display: false, + code: "", + }); const options: ApexOptions = { plotOptions: { @@ -321,12 +326,12 @@ export function AllocationsModule(props: AllocationsModuleProps) { if (vizLevel === 0) { if ( dataPathSteps.length === 0 || - !find(dataPathSteps, { name: "Allocation-radial" }) + !find(dataPathSteps, { name: "Access to Funding: Allocations" }) ) { addDataPathSteps([ { id: uniqueId(), - name: "Allocation-radial", + name: "Access to Funding: Allocations", path: `${history.location.pathname}${history.location.search}`, }, ]); @@ -356,9 +361,6 @@ export function AllocationsModule(props: AllocationsModuleProps) { setVizLevel(0); setVizSelected(undefined); clearDataPathActiveStep(); - if (dataPathSteps.length > 0) { - addDataPathSteps([dataPathActiveStep]); - } } } }, [dataPathActiveStep]); @@ -452,26 +454,27 @@ export function AllocationsModule(props: AllocationsModuleProps) { options={[...keys, "Total"]} selected={vizSelected || ""} onChange={(value: string) => { - setVizSelected(value); + const prevValue = vizSelected || ""; + console.log("prevValue", prevValue); const fItemIndex = findIndex(dataPathSteps, { - name: value, - path: `${history.location.pathname}${history.location.search}`, + vizSelected: { id: prevValue, filterStr: prevValue }, }); + console.log("fItemIndex", fItemIndex); + setVizSelected(value); + let newDataPathSteps = [...dataPathSteps]; if (fItemIndex > -1) { - setDataPathSteps(dataPathSteps.slice(0, fItemIndex + 1)); - } else { - addDataPathSteps([ - { - id: uniqueId(), - name: value, - path: `${history.location.pathname}${history.location.search}`, - vizSelected: { - id: value, - filterStr: value, - }, - }, - ]); + newDataPathSteps = dataPathSteps.slice(0, fItemIndex); } + newDataPathSteps.push({ + id: uniqueId(), + name: value, + path: `${history.location.pathname}${history.location.search}`, + vizSelected: { + id: value, + filterStr: value, + }, + }); + setDataPathSteps(newDataPathSteps); }} /> @@ -481,14 +484,10 @@ export function AllocationsModule(props: AllocationsModuleProps) { onNodeClick={(node: string) => { const name = node.split("-")[0]; const code = getIso3FromName(name); - addDataPathSteps([ - { - id: uniqueId(), - name: name, - path: `/location/${code}/allocations`, - }, - ]); - history.push(`/location/${code}/allocations`); + setReRouteDialog({ + display: true, + code, + }); }} /> @@ -517,6 +516,15 @@ export function AllocationsModule(props: AllocationsModuleProps) { } `} > + {reRouteDialog.display && ( + + history.push(`/location/${reRouteDialog.code}/overview`) + } + /> + )}
state.DataPathSteps.steps); const [drilldownVizSelected, setDrilldownVizSelected] = - React.useState( - breadcrumbList[breadcrumbList.length - 1] - ?.vizSelected as DrilldownVizSelectedType - ); + React.useState({ + id: dataPathSteps[dataPathSteps.length - 1]?.drilldownVizSelected?.id, + filterStr: + dataPathSteps[dataPathSteps.length - 1]?.drilldownVizSelected + ?.filterStr, + }); + const [vizLevel, setVizLevel] = React.useState(0); + + useMount(() => { + setVizSelected({ + id: dataPathSteps[dataPathSteps.length - 1]?.vizSelected?.filterStr, + filterStr: + dataPathSteps[dataPathSteps.length - 1]?.vizSelected?.filterStr, + }); + }); React.useEffect(() => { - setDrilldownVizSelected( - breadcrumbList[breadcrumbList.length - 1] - ?.vizSelected as DrilldownVizSelectedType - ); - setVizLevel(breadcrumbList[breadcrumbList.length - 1]?.vizLevel || 0); - }, [breadcrumbList]); + const newVizSelectedId = + dataPathSteps[dataPathSteps.length - 1]?.vizSelected?.id; + const newVizSelectedFilterStr = + dataPathSteps[dataPathSteps.length - 1]?.vizSelected?.filterStr; + const newDrilldownVizSelectedId = + dataPathSteps[dataPathSteps.length - 1]?.drilldownVizSelected?.id; + const newDrilldownVizSelectedFilterStr = + dataPathSteps[dataPathSteps.length - 1]?.drilldownVizSelected?.filterStr; + if ( + !isEqual( + { + id: newVizSelectedId, + filterStr: newVizSelectedFilterStr, + }, + vizSelected + ) + ) { + setVizSelected({ + id: newVizSelectedId, + filterStr: newVizSelectedFilterStr, + }); + } + if ( + !isEqual( + { + id: newDrilldownVizSelectedId, + filterStr: newDrilldownVizSelectedFilterStr, + }, + drilldownVizSelected + ) + ) { + setDrilldownVizSelected({ + id: newDrilldownVizSelectedId, + filterStr: newDrilldownVizSelectedFilterStr, + }); + } + if (newDrilldownVizSelectedFilterStr) { + setVizLevel(2); + } else if (newVizSelectedFilterStr) { + setVizLevel(1); + } else { + setVizLevel(0); + } + }, [dataPathSteps]); // api call & data const fetchData = useStoreActions((store) => store.BudgetsFlow.fetch); diff --git a/src/app/modules/viz-module/sub-modules/budgets/flow/data-wrappers/grantDetail.tsx b/src/app/modules/viz-module/sub-modules/budgets/flow/data-wrappers/grantDetail.tsx index 67df667ad..61100f63f 100644 --- a/src/app/modules/viz-module/sub-modules/budgets/flow/data-wrappers/grantDetail.tsx +++ b/src/app/modules/viz-module/sub-modules/budgets/flow/data-wrappers/grantDetail.tsx @@ -1,14 +1,13 @@ /* third-party */ import React from "react"; import get from "lodash/get"; -import { useTitle, useUpdateEffect } from "react-use"; +import isEqual from "lodash/isEqual"; +import { useTitle, useUpdateEffect, useMount } from "react-use"; import { useStoreActions, useStoreState } from "app/state/store/hooks"; /* project */ import { BudgetsTreemapDataItem } from "app/components/Charts/Budgets/Treemap/data"; import { BudgetsFlowModule } from "app/modules/viz-module/sub-modules/budgets/flow"; import { getDrilldownPanelOptions } from "app/modules/viz-module/sub-modules/budgets/flow/utils"; -import { useRecoilValue } from "recoil"; -import { breadCrumbItems } from "app/state/recoil/atoms"; interface Props { code: string; @@ -20,28 +19,73 @@ interface DrilldownVizSelectedType { id: string | undefined; filterStr: string | undefined; } + export function GrantDetailBudgetsFlowWrapper(props: Props) { useTitle("The Data Explorer - Grant Budgets Flow"); - const breadcrumbList = useRecoilValue(breadCrumbItems); - - const [vizLevel, setVizLevel] = React.useState( - breadcrumbList[breadcrumbList.length - 1]?.vizLevel || 0 - ); - + const dataPathSteps = useStoreState((state) => state.DataPathSteps.steps); const [drilldownVizSelected, setDrilldownVizSelected] = - React.useState( - breadcrumbList[breadcrumbList.length - 1] - ?.vizSelected as DrilldownVizSelectedType - ); + React.useState({ + id: dataPathSteps[dataPathSteps.length - 1]?.drilldownVizSelected?.id, + filterStr: + dataPathSteps[dataPathSteps.length - 1]?.drilldownVizSelected + ?.filterStr, + }); + const [vizLevel, setVizLevel] = React.useState(0); + + useMount(() => { + setVizSelected({ + id: dataPathSteps[dataPathSteps.length - 1]?.vizSelected?.filterStr, + filterStr: + dataPathSteps[dataPathSteps.length - 1]?.vizSelected?.filterStr, + }); + }); React.useEffect(() => { - setDrilldownVizSelected( - breadcrumbList[breadcrumbList.length - 1] - ?.vizSelected as DrilldownVizSelectedType - ); - setVizLevel(breadcrumbList[breadcrumbList.length - 1]?.vizLevel || 0); - }, [breadcrumbList]); + const newVizSelectedId = + dataPathSteps[dataPathSteps.length - 1]?.vizSelected?.id; + const newVizSelectedFilterStr = + dataPathSteps[dataPathSteps.length - 1]?.vizSelected?.filterStr; + const newDrilldownVizSelectedId = + dataPathSteps[dataPathSteps.length - 1]?.drilldownVizSelected?.id; + const newDrilldownVizSelectedFilterStr = + dataPathSteps[dataPathSteps.length - 1]?.drilldownVizSelected?.filterStr; + if ( + !isEqual( + { + id: newVizSelectedId, + filterStr: newVizSelectedFilterStr, + }, + vizSelected + ) + ) { + setVizSelected({ + id: newVizSelectedId, + filterStr: newVizSelectedFilterStr, + }); + } + if ( + !isEqual( + { + id: newDrilldownVizSelectedId, + filterStr: newDrilldownVizSelectedFilterStr, + }, + drilldownVizSelected + ) + ) { + setDrilldownVizSelected({ + id: newDrilldownVizSelectedId, + filterStr: newDrilldownVizSelectedFilterStr, + }); + } + if (newDrilldownVizSelectedFilterStr) { + setVizLevel(2); + } else if (newVizSelectedFilterStr) { + setVizLevel(1); + } else { + setVizLevel(0); + } + }, [dataPathSteps]); // api call & data const fetchData = useStoreActions( diff --git a/src/app/modules/viz-module/sub-modules/budgets/flow/data-wrappers/locationDetail.tsx b/src/app/modules/viz-module/sub-modules/budgets/flow/data-wrappers/locationDetail.tsx index 9b56089a5..dff80c04f 100644 --- a/src/app/modules/viz-module/sub-modules/budgets/flow/data-wrappers/locationDetail.tsx +++ b/src/app/modules/viz-module/sub-modules/budgets/flow/data-wrappers/locationDetail.tsx @@ -1,7 +1,8 @@ /* third-party */ import React from "react"; import get from "lodash/get"; -import { useTitle, useUpdateEffect } from "react-use"; +import isEqual from "lodash/isEqual"; +import { useTitle, useUpdateEffect, useMount } from "react-use"; import { useStoreActions, useStoreState } from "app/state/store/hooks"; /* project */ import { getAPIFormattedFilters } from "app/utils/getAPIFormattedFilters"; @@ -14,13 +15,77 @@ interface Props { toolboxOpen?: boolean; } +interface DrilldownVizSelectedType { + id: string | undefined; + filterStr: string | undefined; +} + export function LocationDetailBudgetsFlowWrapper(props: Props) { useTitle("The Data Explorer - Location Budgets Flow"); + + const dataPathSteps = useStoreState((state) => state.DataPathSteps.steps); + const [drilldownVizSelected, setDrilldownVizSelected] = + React.useState({ + id: dataPathSteps[dataPathSteps.length - 1]?.drilldownVizSelected?.id, + filterStr: + dataPathSteps[dataPathSteps.length - 1]?.drilldownVizSelected + ?.filterStr, + }); const [vizLevel, setVizLevel] = React.useState(0); - const [drilldownVizSelected, setDrilldownVizSelected] = React.useState<{ - id: string | undefined; - filterStr: string | undefined; - }>({ id: undefined, filterStr: undefined }); + + useMount(() => { + setVizSelected({ + id: dataPathSteps[dataPathSteps.length - 1]?.vizSelected?.filterStr, + filterStr: + dataPathSteps[dataPathSteps.length - 1]?.vizSelected?.filterStr, + }); + }); + + React.useEffect(() => { + const newVizSelectedId = + dataPathSteps[dataPathSteps.length - 1]?.vizSelected?.id; + const newVizSelectedFilterStr = + dataPathSteps[dataPathSteps.length - 1]?.vizSelected?.filterStr; + const newDrilldownVizSelectedId = + dataPathSteps[dataPathSteps.length - 1]?.drilldownVizSelected?.id; + const newDrilldownVizSelectedFilterStr = + dataPathSteps[dataPathSteps.length - 1]?.drilldownVizSelected?.filterStr; + if ( + !isEqual( + { + id: newVizSelectedId, + filterStr: newVizSelectedFilterStr, + }, + vizSelected + ) + ) { + setVizSelected({ + id: newVizSelectedId, + filterStr: newVizSelectedFilterStr, + }); + } + if ( + !isEqual( + { + id: newDrilldownVizSelectedId, + filterStr: newDrilldownVizSelectedFilterStr, + }, + drilldownVizSelected + ) + ) { + setDrilldownVizSelected({ + id: newDrilldownVizSelectedId, + filterStr: newDrilldownVizSelectedFilterStr, + }); + } + if (newDrilldownVizSelectedFilterStr) { + setVizLevel(2); + } else if (newVizSelectedFilterStr) { + setVizLevel(1); + } else { + setVizLevel(0); + } + }, [dataPathSteps]); // api call & data const fetchData = useStoreActions( diff --git a/src/app/modules/viz-module/sub-modules/budgets/flow/data-wrappers/partnerDetail.tsx b/src/app/modules/viz-module/sub-modules/budgets/flow/data-wrappers/partnerDetail.tsx index 5117559ce..249dae6d1 100644 --- a/src/app/modules/viz-module/sub-modules/budgets/flow/data-wrappers/partnerDetail.tsx +++ b/src/app/modules/viz-module/sub-modules/budgets/flow/data-wrappers/partnerDetail.tsx @@ -1,7 +1,8 @@ /* third-party */ import React from "react"; import get from "lodash/get"; -import { useTitle, useUpdateEffect } from "react-use"; +import isEqual from "lodash/isEqual"; +import { useTitle, useUpdateEffect, useMount } from "react-use"; import { useStoreActions, useStoreState } from "app/state/store/hooks"; /* project */ import { getAPIFormattedFilters } from "app/utils/getAPIFormattedFilters"; @@ -14,13 +15,77 @@ interface Props { toolboxOpen?: boolean; } +interface DrilldownVizSelectedType { + id: string | undefined; + filterStr: string | undefined; +} + export function PartnerDetailBudgetsFlowWrapper(props: Props) { useTitle("The Data Explorer - Partner Budgets Flow"); + + const dataPathSteps = useStoreState((state) => state.DataPathSteps.steps); + const [drilldownVizSelected, setDrilldownVizSelected] = + React.useState({ + id: dataPathSteps[dataPathSteps.length - 1]?.drilldownVizSelected?.id, + filterStr: + dataPathSteps[dataPathSteps.length - 1]?.drilldownVizSelected + ?.filterStr, + }); const [vizLevel, setVizLevel] = React.useState(0); - const [drilldownVizSelected, setDrilldownVizSelected] = React.useState<{ - id: string | undefined; - filterStr: string | undefined; - }>({ id: undefined, filterStr: undefined }); + + useMount(() => { + setVizSelected({ + id: dataPathSteps[dataPathSteps.length - 1]?.vizSelected?.filterStr, + filterStr: + dataPathSteps[dataPathSteps.length - 1]?.vizSelected?.filterStr, + }); + }); + + React.useEffect(() => { + const newVizSelectedId = + dataPathSteps[dataPathSteps.length - 1]?.vizSelected?.id; + const newVizSelectedFilterStr = + dataPathSteps[dataPathSteps.length - 1]?.vizSelected?.filterStr; + const newDrilldownVizSelectedId = + dataPathSteps[dataPathSteps.length - 1]?.drilldownVizSelected?.id; + const newDrilldownVizSelectedFilterStr = + dataPathSteps[dataPathSteps.length - 1]?.drilldownVizSelected?.filterStr; + if ( + !isEqual( + { + id: newVizSelectedId, + filterStr: newVizSelectedFilterStr, + }, + vizSelected + ) + ) { + setVizSelected({ + id: newVizSelectedId, + filterStr: newVizSelectedFilterStr, + }); + } + if ( + !isEqual( + { + id: newDrilldownVizSelectedId, + filterStr: newDrilldownVizSelectedFilterStr, + }, + drilldownVizSelected + ) + ) { + setDrilldownVizSelected({ + id: newDrilldownVizSelectedId, + filterStr: newDrilldownVizSelectedFilterStr, + }); + } + if (newDrilldownVizSelectedFilterStr) { + setVizLevel(2); + } else if (newVizSelectedFilterStr) { + setVizLevel(1); + } else { + setVizLevel(0); + } + }, [dataPathSteps]); // api call & data const fetchData = useStoreActions( diff --git a/src/app/modules/viz-module/sub-modules/budgets/flow/index.tsx b/src/app/modules/viz-module/sub-modules/budgets/flow/index.tsx index fb06464fe..51c94c68b 100644 --- a/src/app/modules/viz-module/sub-modules/budgets/flow/index.tsx +++ b/src/app/modules/viz-module/sub-modules/budgets/flow/index.tsx @@ -1,27 +1,22 @@ /* third-party */ -import React, { useState } from "react"; +import React from "react"; import find from "lodash/find"; -import { v4 } from "uuid"; import sumBy from "lodash/sumBy"; - import filter from "lodash/filter"; import uniqueId from "lodash/uniqueId"; +import Grid from "@material-ui/core/Grid"; import { useHistory } from "react-router-dom"; import { TreeMapNodeDatum } from "@nivo/treemap"; import { useStoreActions, useStoreState } from "app/state/store/hooks"; /* project */ -import { DrilldownModelUpdated } from "app/interfaces"; +import { appColors } from "app/theme"; import { PageLoader } from "app/modules/common/page-loader"; import { getNameFromIso3 } from "app/utils/getIso3FromName"; import { BudgetsFlow } from "app/components/Charts/Budgets/Flow"; -import { BudgetsTreemap } from "app/components/Charts/Budgets/Treemap"; -import { BudgetsTreemapDataItem } from "app/components/Charts/Budgets/Treemap/data"; import ReRouteDialogBox from "app/components/Charts/common/dialogBox"; -import { useRecoilState } from "recoil"; -import { breadCrumbItems } from "app/state/recoil/atoms"; -import { Grid } from "@material-ui/core"; import { formatFinancialValue } from "app/utils/formatFinancialValue"; -import { appColors } from "app/theme"; +import { BudgetsTreemap } from "app/components/Charts/Budgets/Treemap"; +import { BudgetsTreemapDataItem } from "app/components/Charts/Budgets/Treemap/data"; interface BudgetsFlowModuleProps { nodes: { @@ -62,12 +57,11 @@ interface BudgetsFlowModuleProps { export function BudgetsFlowModule(props: BudgetsFlowModuleProps) { const history = useHistory(); - const [breadCrumbList, setBreadCrumbList] = useRecoilState(breadCrumbItems); const [xsTooltipData, setXsTooltipData] = React.useState(null); - const [reRouteDialog, setReRouteDialog] = useState({ + const [reRouteDialog, setReRouteDialog] = React.useState({ display: false, code: "", }); @@ -77,31 +71,19 @@ export function BudgetsFlowModule(props: BudgetsFlowModuleProps) { (actions) => actions.DataPathSteps.addSteps ); - const totalBudget = sumBy( - filter(props.links, { source: "Budgets" }), - "value" - ); + const totalBudget: number = React.useMemo(() => { + return sumBy(filter(props.links, { source: "Budgets" }), "value"); + }, [props.links]); React.useEffect(() => { if (props.vizLevel === 0) { if ( - dataPathSteps.length === 0 || - !find(dataPathSteps, { name: "Budget-budget flow" }) - ) { - addDataPathSteps([ - { - id: uniqueId(), - name: "Budget-budget flow", - path: `${history.location.pathname}${history.location.search}`, - }, - ]); - } else if ( props.isGrantDetail && !find(dataPathSteps, (step) => step.path.indexOf("/grant/") > -1) ) { addDataPathSteps([ { - id: uniqueId(), + id: "grant", name: props.codeParam || "Grant", path: `${history.location.pathname}${history.location.search}`, }, @@ -112,7 +94,7 @@ export function BudgetsFlowModule(props: BudgetsFlowModuleProps) { ) { addDataPathSteps([ { - id: uniqueId(), + id: "location", name: props.codeParam ? getNameFromIso3(props.codeParam) : "Location", @@ -125,45 +107,24 @@ export function BudgetsFlowModule(props: BudgetsFlowModuleProps) { ) { addDataPathSteps([ { - id: uniqueId(), + id: "partner", name: props.codeParam || "Partner", path: `${history.location.pathname}${history.location.search}`, }, ]); } - } - if (props.vizLevel > 0 && props.vizSelected && props.vizSelected.id) { - const newDrilldowns: DrilldownModelUpdated[] = []; - if (props.vizLevel === 1) { - newDrilldowns.push({ - id: uniqueId(), - name: props.vizSelected.id, - path: `${history.location.pathname}${history.location.search}`, - vizSelected: { - id: props.vizSelected.id || "", - filterStr: props.vizSelected.filterStr || "", - }, - }); - } else if (props.vizLevel === 2 && props.drilldownVizSelected.id) { - const idSplits = props.drilldownVizSelected.id.split("-"); - const firstDrillDown = idSplits.length > 2 ? idSplits[2] : idSplits[1]; - const secondDrillDown = - idSplits.length > 2 ? `${idSplits[0]}-${idSplits[1]}` : idSplits[0]; - newDrilldowns.push({ - id: uniqueId(), - name: `${firstDrillDown} - ${secondDrillDown}`, - path: `${history.location.pathname}${history.location.search}`, - vizSelected: { - id: props.vizSelected.id || "", - filterStr: props.vizSelected.filterStr || "", - }, - drilldownVizSelected: { - id: props.drilldownVizSelected.id || "", - filterStr: props.drilldownVizSelected.filterStr || "", + if ( + dataPathSteps.length === 0 || + !find(dataPathSteps, { name: "Grant Implementation: Budgets" }) + ) { + addDataPathSteps([ + { + id: uniqueId(), + name: "Grant Implementation: Budgets", + path: `${history.location.pathname}${history.location.search}`, }, - }); + ]); } - addDataPathSteps(newDrilldowns); } }, [props.vizLevel, props.vizSelected, props.drilldownVizSelected]); @@ -180,18 +141,16 @@ export function BudgetsFlowModule(props: BudgetsFlowModuleProps) { links: props.links, }} onNodeClick={(node: { id: string; filterStr: string }) => { - setBreadCrumbList([ - ...breadCrumbList, + props.setVizLevel(1); + props.setVizSelected(node); + addDataPathSteps([ { + id: uniqueId(), name: node.id, - path: location.pathname, - id: v4(), - vizLevel: 1, + path: `${history.location.pathname}${history.location.search}`, vizSelected: node, }, ]); - props.setVizLevel(1); - props.setVizSelected(node); }} /> ); @@ -204,24 +163,24 @@ export function BudgetsFlowModule(props: BudgetsFlowModuleProps) { data={props.dataDrilldownLevel1} setXsTooltipData={setXsTooltipData} onNodeClick={(node: string) => { - setBreadCrumbList([ - ...breadCrumbList, - { - name: node, - path: location.pathname, - id: v4(), - vizLevel: 2, - vizSelected: { - id: node, - filterStr: undefined, + if (props.setDrilldownVizSelected) { + props.setVizLevel(2); + props.setDrilldownVizSelected({ + id: node, + filterStr: undefined, + }); + addDataPathSteps([ + { + id: uniqueId(), + name: node, + path: `${history.location.pathname}${history.location.search}`, + drilldownVizSelected: { + id: node, + filterStr: node, + }, }, - }, - ]); - props.setVizLevel(2); - props.setDrilldownVizSelected({ - id: node, - filterStr: undefined, - }); + ]); + } }} /> ); @@ -234,19 +193,12 @@ export function BudgetsFlowModule(props: BudgetsFlowModuleProps) { data={props.dataDrilldownLevel2} selectedNodeId={props.vizSelected.id} onNodeClick={(node: string) => { - if (props.drilldownVizSelected.id) { + if (props.drilldownVizSelected.id && !props.isGrantDetail) { const idSplits = props.drilldownVizSelected.id.split("-"); let code = node .replace(idSplits[0], "") .replace(`-${idSplits[1]}`, ""); code = code.slice(0, code.length - 1); - addDataPathSteps([ - { - id: uniqueId(), - name: code, - path: `/grant/${code}/period/budgets/flow`, - }, - ]); setReRouteDialog({ display: true, code, diff --git a/src/app/modules/viz-module/sub-modules/budgets/geomap/index.tsx b/src/app/modules/viz-module/sub-modules/budgets/geomap/index.tsx index 1af28111c..768e316cc 100644 --- a/src/app/modules/viz-module/sub-modules/budgets/geomap/index.tsx +++ b/src/app/modules/viz-module/sub-modules/budgets/geomap/index.tsx @@ -68,12 +68,12 @@ export function BudgetsGeoMap(props: Props) { React.useEffect(() => { if ( dataPathSteps.length === 0 || - !find(dataPathSteps, { name: "Budget-map" }) + !find(dataPathSteps, { name: "Grant Implementation: Budgets" }) ) { addDataPathSteps([ { id: uniqueId(), - name: "Budget-map", + name: "Grant Implementation: Budgets", path: `${history.location.pathname}${history.location.search}`, }, ]); diff --git a/src/app/modules/viz-module/sub-modules/budgets/time-cycle/data-wrappers/generic.tsx b/src/app/modules/viz-module/sub-modules/budgets/time-cycle/data-wrappers/generic.tsx index bc81d2d09..c5dc41d0f 100644 --- a/src/app/modules/viz-module/sub-modules/budgets/time-cycle/data-wrappers/generic.tsx +++ b/src/app/modules/viz-module/sub-modules/budgets/time-cycle/data-wrappers/generic.tsx @@ -1,6 +1,7 @@ /* third-party */ import React from "react"; import get from "lodash/get"; +import isEqual from "lodash/isEqual"; import { useHistory } from "react-router-dom"; import { useTitle, useUpdateEffect } from "react-use"; import { useStoreActions, useStoreState } from "app/state/store/hooks"; @@ -8,8 +9,6 @@ import { useStoreActions, useStoreState } from "app/state/store/hooks"; import { getAPIFormattedFilters } from "app/utils/getAPIFormattedFilters"; import { BudgetsTreemapDataItem } from "app/components/Charts/Budgets/Treemap/data"; import { BudgetsTimeCycleModule } from "app/modules/viz-module/sub-modules/budgets/time-cycle"; -import { breadCrumbItems } from "app/state/recoil/atoms"; -import { useRecoilValue } from "recoil"; interface Props { toolboxOpen?: boolean; @@ -21,20 +20,32 @@ export function GenericBudgetsTimeCycleWrapper(props: Props) { const history = useHistory(); - const breadcrumbList = useRecoilValue(breadCrumbItems); + const dataPathSteps = useStoreState((state) => state.DataPathSteps.steps); - const [vizLevel, setVizLevel] = React.useState( - breadcrumbList[breadcrumbList.length - 1]?.vizLevel || 0 - ); + const [vizLevel, setVizLevel] = React.useState(0); const [drilldownVizSelected, setDrilldownVizSelected] = React.useState< string | undefined - >(breadcrumbList[breadcrumbList.length - 1]?.vizSelected as string); + >(dataPathSteps[dataPathSteps.length - 1]?.drilldownVizSelected?.filterStr); + React.useEffect(() => { - setDrilldownVizSelected( - breadcrumbList[breadcrumbList.length - 1]?.vizSelected as string - ); - setVizLevel(breadcrumbList[breadcrumbList.length - 1]?.vizLevel || 0); - }, [breadcrumbList]); + const newVizSelected = + dataPathSteps[dataPathSteps.length - 1]?.vizSelected?.filterStr; + const newDrilldownVizSelected = + dataPathSteps[dataPathSteps.length - 1]?.drilldownVizSelected?.filterStr; + if (!isEqual(newVizSelected, vizSelected)) { + setVizSelected(newVizSelected); + } + if (!isEqual(newDrilldownVizSelected, drilldownVizSelected)) { + setDrilldownVizSelected(newDrilldownVizSelected); + } + if (newDrilldownVizSelected) { + setVizLevel(2); + } else if (newVizSelected) { + setVizLevel(1); + } else { + setVizLevel(0); + } + }, [dataPathSteps]); // api call & data const fetchData = useStoreActions((store) => store.BudgetsTimeCycle.fetch); diff --git a/src/app/modules/viz-module/sub-modules/budgets/time-cycle/data-wrappers/grantDetail.tsx b/src/app/modules/viz-module/sub-modules/budgets/time-cycle/data-wrappers/grantDetail.tsx index 8b92aab4d..ba6540558 100644 --- a/src/app/modules/viz-module/sub-modules/budgets/time-cycle/data-wrappers/grantDetail.tsx +++ b/src/app/modules/viz-module/sub-modules/budgets/time-cycle/data-wrappers/grantDetail.tsx @@ -1,14 +1,13 @@ /* third-party */ import React from "react"; import get from "lodash/get"; +import isEqual from "lodash/isEqual"; import { useTitle, useUpdateEffect } from "react-use"; import { useStoreActions, useStoreState } from "app/state/store/hooks"; /* project */ import { getAPIFormattedFilters } from "app/utils/getAPIFormattedFilters"; import { BudgetsTreemapDataItem } from "app/components/Charts/Budgets/Treemap/data"; import { BudgetsTimeCycleModule } from "app/modules/viz-module/sub-modules/budgets/time-cycle"; -import { breadCrumbItems } from "app/state/recoil/atoms"; -import { useRecoilValue } from "recoil"; interface Props { code: string; @@ -19,22 +18,32 @@ interface Props { export function GrantDetailGenericBudgetsTimeCycleWrapper(props: Props) { useTitle("The Data Explorer - Grant Budgets Time cycle"); - const breadcrumbList = useRecoilValue(breadCrumbItems); - - const [vizLevel, setVizLevel] = React.useState( - breadcrumbList[breadcrumbList.length - 1]?.vizLevel || 0 - ); + const dataPathSteps = useStoreState((state) => state.DataPathSteps.steps); + const [vizLevel, setVizLevel] = React.useState(0); const [drilldownVizSelected, setDrilldownVizSelected] = React.useState< string | undefined - >(breadcrumbList[breadcrumbList.length - 1]?.vizSelected as string); + >(dataPathSteps[dataPathSteps.length - 1]?.drilldownVizSelected?.filterStr); React.useEffect(() => { - setDrilldownVizSelected( - breadcrumbList[breadcrumbList.length - 1]?.vizSelected as string - ); - setVizLevel(breadcrumbList[breadcrumbList.length - 1]?.vizLevel || 0); - }, [breadcrumbList]); + const newVizSelected = + dataPathSteps[dataPathSteps.length - 1]?.vizSelected?.filterStr; + const newDrilldownVizSelected = + dataPathSteps[dataPathSteps.length - 1]?.drilldownVizSelected?.filterStr; + if (!isEqual(newVizSelected, vizSelected)) { + setVizSelected(newVizSelected); + } + if (!isEqual(newDrilldownVizSelected, drilldownVizSelected)) { + setDrilldownVizSelected(newDrilldownVizSelected); + } + if (newDrilldownVizSelected) { + setVizLevel(2); + } else if (newVizSelected) { + setVizLevel(1); + } else { + setVizLevel(0); + } + }, [dataPathSteps]); // api call & data const fetchData = useStoreActions( diff --git a/src/app/modules/viz-module/sub-modules/budgets/time-cycle/data-wrappers/locationDetail.tsx b/src/app/modules/viz-module/sub-modules/budgets/time-cycle/data-wrappers/locationDetail.tsx index e48d84fc5..97a6cdf70 100644 --- a/src/app/modules/viz-module/sub-modules/budgets/time-cycle/data-wrappers/locationDetail.tsx +++ b/src/app/modules/viz-module/sub-modules/budgets/time-cycle/data-wrappers/locationDetail.tsx @@ -1,6 +1,7 @@ /* third-party */ import React from "react"; import get from "lodash/get"; +import isEqual from "lodash/isEqual"; import { useTitle, useUpdateEffect } from "react-use"; import { useStoreActions, useStoreState } from "app/state/store/hooks"; /* project */ @@ -15,10 +16,33 @@ interface Props { export function LocationDetailGenericBudgetsTimeCycleWrapper(props: Props) { useTitle("The Data Explorer - Location Budgets Time cycle"); + + const dataPathSteps = useStoreState((state) => state.DataPathSteps.steps); + const [vizLevel, setVizLevel] = React.useState(0); const [drilldownVizSelected, setDrilldownVizSelected] = React.useState< string | undefined - >(undefined); + >(dataPathSteps[dataPathSteps.length - 1]?.drilldownVizSelected?.filterStr); + + React.useEffect(() => { + const newVizSelected = + dataPathSteps[dataPathSteps.length - 1]?.vizSelected?.filterStr; + const newDrilldownVizSelected = + dataPathSteps[dataPathSteps.length - 1]?.drilldownVizSelected?.filterStr; + if (!isEqual(newVizSelected, vizSelected)) { + setVizSelected(newVizSelected); + } + if (!isEqual(newDrilldownVizSelected, drilldownVizSelected)) { + setDrilldownVizSelected(newDrilldownVizSelected); + } + if (newDrilldownVizSelected) { + setVizLevel(2); + } else if (newVizSelected) { + setVizLevel(1); + } else { + setVizLevel(0); + } + }, [dataPathSteps]); // api call & data const fetchData = useStoreActions( diff --git a/src/app/modules/viz-module/sub-modules/budgets/time-cycle/data-wrappers/partnerDetail.tsx b/src/app/modules/viz-module/sub-modules/budgets/time-cycle/data-wrappers/partnerDetail.tsx index 4c843dd2a..8f55e26b3 100644 --- a/src/app/modules/viz-module/sub-modules/budgets/time-cycle/data-wrappers/partnerDetail.tsx +++ b/src/app/modules/viz-module/sub-modules/budgets/time-cycle/data-wrappers/partnerDetail.tsx @@ -1,6 +1,7 @@ /* third-party */ import React from "react"; import get from "lodash/get"; +import isEqual from "lodash/isEqual"; import { useTitle, useUpdateEffect } from "react-use"; import { useStoreActions, useStoreState } from "app/state/store/hooks"; /* project */ @@ -15,10 +16,33 @@ interface Props { export function PartnerDetailGenericBudgetsTimeCycleWrapper(props: Props) { useTitle("The Data Explorer - Partner Budgets Time cycle"); + + const dataPathSteps = useStoreState((state) => state.DataPathSteps.steps); + const [vizLevel, setVizLevel] = React.useState(0); const [drilldownVizSelected, setDrilldownVizSelected] = React.useState< string | undefined - >(undefined); + >(dataPathSteps[dataPathSteps.length - 1]?.drilldownVizSelected?.filterStr); + + React.useEffect(() => { + const newVizSelected = + dataPathSteps[dataPathSteps.length - 1]?.vizSelected?.filterStr; + const newDrilldownVizSelected = + dataPathSteps[dataPathSteps.length - 1]?.drilldownVizSelected?.filterStr; + if (!isEqual(newVizSelected, vizSelected)) { + setVizSelected(newVizSelected); + } + if (!isEqual(newDrilldownVizSelected, drilldownVizSelected)) { + setDrilldownVizSelected(newDrilldownVizSelected); + } + if (newDrilldownVizSelected) { + setVizLevel(2); + } else if (newVizSelected) { + setVizLevel(1); + } else { + setVizLevel(0); + } + }, [dataPathSteps]); // api call & data const fetchData = useStoreActions( diff --git a/src/app/modules/viz-module/sub-modules/budgets/time-cycle/index.tsx b/src/app/modules/viz-module/sub-modules/budgets/time-cycle/index.tsx index eb49c0640..b9d7b4579 100644 --- a/src/app/modules/viz-module/sub-modules/budgets/time-cycle/index.tsx +++ b/src/app/modules/viz-module/sub-modules/budgets/time-cycle/index.tsx @@ -1,11 +1,9 @@ /* third-party */ import React from "react"; -import { v4 } from "uuid"; import get from "lodash/get"; import find from "lodash/find"; import sumBy from "lodash/sumBy"; import uniqueId from "lodash/uniqueId"; -import { useRecoilState } from "recoil"; import Grid from "@material-ui/core/Grid"; import { useHistory } from "react-router-dom"; import { TreeMapNodeDatum } from "@nivo/treemap"; @@ -14,7 +12,6 @@ import useMediaQuery from "@material-ui/core/useMediaQuery"; import { useStoreActions, useStoreState } from "app/state/store/hooks"; /* project */ import { DrilldownModelUpdated } from "app/interfaces"; -import { breadCrumbItems } from "app/state/recoil/atoms"; import { PageLoader } from "app/modules/common/page-loader"; import { getNameFromIso3 } from "app/utils/getIso3FromName"; import { formatFinancialValue } from "app/utils/formatFinancialValue"; @@ -45,14 +42,15 @@ interface BudgetsTimeCycleModuleProps { export function BudgetsTimeCycleModule(props: BudgetsTimeCycleModuleProps) { const history = useHistory(); - const totalBudget = sumBy(props.data, "amount"); const cmsData = useCMSData({ returnData: true }); const isMobile = useMediaQuery("(max-width: 767px)"); const [xsTooltipData, setXsTooltipData] = React.useState(null); - const [breadCrumbList, setBreadCrumbList] = useRecoilState(breadCrumbItems); + const totalBudget = React.useMemo(() => { + return sumBy(props.data, "amount"); + }, [props.data]); const dataPathSteps = useStoreState((state) => state.DataPathSteps.steps); const addDataPathSteps = useStoreActions( @@ -67,23 +65,12 @@ export function BudgetsTimeCycleModule(props: BudgetsTimeCycleModuleProps) { React.useEffect(() => { if (props.vizLevel === 0) { if ( - dataPathSteps.length === 0 || - !find(dataPathSteps, { name: "Budget-time cycle" }) - ) { - addDataPathSteps([ - { - id: uniqueId(), - name: "Budget-time cycle", - path: `${history.location.pathname}${history.location.search}`, - }, - ]); - } else if ( props.isGrantDetail && !find(dataPathSteps, (step) => step.path.indexOf("/grant/") > -1) ) { addDataPathSteps([ { - id: uniqueId(), + id: "grant", name: props.codeParam || "Grant", path: `${history.location.pathname}${history.location.search}`, }, @@ -94,7 +81,7 @@ export function BudgetsTimeCycleModule(props: BudgetsTimeCycleModuleProps) { ) { addDataPathSteps([ { - id: uniqueId(), + id: "location", name: props.codeParam ? getNameFromIso3(props.codeParam) : "Location", @@ -107,45 +94,24 @@ export function BudgetsTimeCycleModule(props: BudgetsTimeCycleModuleProps) { ) { addDataPathSteps([ { - id: uniqueId(), + id: "partner", name: props.codeParam || "Partner", path: `${history.location.pathname}${history.location.search}`, }, ]); } - } - if (props.vizLevel > 0 && props.vizSelected && props.vizSelected) { - const newDrilldowns: DrilldownModelUpdated[] = []; - if (props.vizLevel === 1) { - newDrilldowns.push({ - id: uniqueId(), - name: props.vizSelected, - path: `${history.location.pathname}${history.location.search}`, - vizSelected: { - id: props.vizSelected || "", - filterStr: props.vizSelected || "", - }, - }); - } else if (props.vizLevel === 2 && props.drilldownVizSelected) { - const idSplits = props.drilldownVizSelected.split("-"); - const firstDrillDown = idSplits.length > 2 ? idSplits[2] : idSplits[1]; - const secondDrillDown = - idSplits.length > 2 ? `${idSplits[0]}-${idSplits[1]}` : idSplits[0]; - newDrilldowns.push({ - id: uniqueId(), - name: `${firstDrillDown} - ${secondDrillDown}`, - path: `${history.location.pathname}${history.location.search}`, - vizSelected: { - id: props.vizSelected || "", - filterStr: props.vizSelected || "", - }, - drilldownVizSelected: { - id: props.drilldownVizSelected || "", - filterStr: props.drilldownVizSelected || "", + if ( + dataPathSteps.length === 0 || + !find(dataPathSteps, { name: "Grant Implementation: Budgets" }) + ) { + addDataPathSteps([ + { + id: uniqueId(), + name: "Grant Implementation: Budgets", + path: `${history.location.pathname}${history.location.search}`, }, - }); + ]); } - addDataPathSteps(newDrilldowns); } }, [props.vizLevel, props.vizSelected, props.drilldownVizSelected]); @@ -159,46 +125,49 @@ export function BudgetsTimeCycleModule(props: BudgetsTimeCycleModuleProps) { { - setBreadCrumbList([ - ...breadCrumbList, + props.setVizLevel(1); + props.setVizSelected(node); + addDataPathSteps([ { + // TODO: implement changes applied here to the other viz modules + id: uniqueId(), name: node, - path: location.pathname, - id: v4(), - vizLevel: 1, - vizSelected: node, + path: `${history.location.pathname}${history.location.search}`, + vizSelected: { + id: node, + filterStr: node, + }, }, ]); - props.setVizLevel(1); - props.setVizSelected(node); }} /> ); } else if (props.vizLevel === 1) { vizComponent = ( - - { - setBreadCrumbList([ - ...breadCrumbList, + { + if (props.setDrilldownVizSelected) { + props.setVizLevel(2); + props.setDrilldownVizSelected(node); + addDataPathSteps([ { + id: uniqueId(), name: node, - path: location.pathname, - id: v4(), - vizLevel: 2, - vizSelected: node, + path: `${history.location.pathname}${history.location.search}`, + drilldownVizSelected: { + id: node, + filterStr: node, + }, }, ]); - props.setVizLevel(2); - props.setDrilldownVizSelected(node); - }} - /> - + } + }} + /> ); } else if (props.vizLevel === 2) { vizComponent = ( @@ -208,19 +177,12 @@ export function BudgetsTimeCycleModule(props: BudgetsTimeCycleModuleProps) { data={props.dataDrilldownLevel2} selectedNodeId={props.vizSelected} onNodeClick={(node: string) => { - if (props.drilldownVizSelected) { + if (props.drilldownVizSelected && !props.isGrantDetail) { const idSplits = props.drilldownVizSelected.split("-"); let code = node .replace(idSplits[0], "") .replace(`-${idSplits[1]}`, ""); code = code.slice(0, code.length - 1); - addDataPathSteps([ - { - id: uniqueId(), - name: code, - path: `/grant/${code}/period/budgets/time-cycle`, - }, - ]); setReRouteDialog({ display: true, code, diff --git a/src/app/modules/viz-module/sub-modules/eligibility/index.tsx b/src/app/modules/viz-module/sub-modules/eligibility/index.tsx index 642ef868c..c10560d47 100644 --- a/src/app/modules/viz-module/sub-modules/eligibility/index.tsx +++ b/src/app/modules/viz-module/sub-modules/eligibility/index.tsx @@ -1,6 +1,9 @@ /* third-party */ import React from "react"; import get from "lodash/get"; +import find from "lodash/find"; +import uniqueId from "lodash/uniqueId"; +import { useHistory } from "react-router-dom"; import useTitle from "react-use/lib/useTitle"; import { useStoreActions, useStoreState } from "app/state/store/hooks"; /* project */ @@ -12,6 +15,9 @@ import { DotChartModel } from "app/components/Charts/Eligibility/DotChart/data"; export function EligibilityModule() { useTitle("The Data Explorer - Eligibility"); + const history = useHistory(); + + const dataPathSteps = useStoreState((state) => state.DataPathSteps.steps); const selectedYear = useStoreState( (state) => state.ToolBoxPanelEligibilityYearState.value ); @@ -29,8 +35,24 @@ export function EligibilityModule() { const appliedFilters = useStoreState((state) => state.AppliedFiltersState); + const addDataPathSteps = useStoreActions( + (actions) => actions.DataPathSteps.addSteps + ); + React.useEffect(() => { fetchYearOptionsData({}); + if ( + dataPathSteps.length === 0 || + !find(dataPathSteps, { name: "Access to Funding: Eligibility" }) + ) { + addDataPathSteps([ + { + id: uniqueId(), + name: "Access to Funding: Eligibility", + path: `${history.location.pathname}${history.location.search}`, + }, + ]); + } }, []); React.useEffect(() => { diff --git a/src/app/modules/viz-module/sub-modules/eligibility/table/data-wrappers/generic.tsx b/src/app/modules/viz-module/sub-modules/eligibility/table/data-wrappers/generic.tsx index 54b510255..1c0d422a0 100644 --- a/src/app/modules/viz-module/sub-modules/eligibility/table/data-wrappers/generic.tsx +++ b/src/app/modules/viz-module/sub-modules/eligibility/table/data-wrappers/generic.tsx @@ -1,7 +1,10 @@ /* third-party */ import React from "react"; import get from "lodash/get"; +import find from "lodash/find"; import filter from "lodash/filter"; +import uniqueId from "lodash/uniqueId"; +import { useHistory } from "react-router-dom"; import TablePagination from "@material-ui/core/TablePagination"; import { useDebounce, useTitle, useUpdateEffect } from "react-use"; import { useStoreActions, useStoreState } from "app/state/store/hooks"; @@ -32,6 +35,8 @@ function getTableData(data: DotChartModel[]): SimpleTableRow[] { export function GenericEligibilityWrapper() { useTitle("The Data Explorer - Eligibility"); + const history = useHistory(); + const [search, setSearch] = React.useState(""); const [sortBy, setSortBy] = React.useState("name ASC"); @@ -43,6 +48,7 @@ export function GenericEligibilityWrapper() { (store) => store.EligibilityYears.fetch ); + const dataPathSteps = useStoreState((state) => state.DataPathSteps.steps); const data = useStoreState( (state) => get(state.EligibilityTable.data, "data", []) as DotChartModel[] ); @@ -59,8 +65,24 @@ export function GenericEligibilityWrapper() { const appliedFilters = useStoreState((state) => state.AppliedFiltersState); + const addDataPathSteps = useStoreActions( + (actions) => actions.DataPathSteps.addSteps + ); + React.useEffect(() => { fetchYearOptionsData({}); + if ( + dataPathSteps.length === 0 || + !find(dataPathSteps, { name: "Access to Funding: Eligibility" }) + ) { + addDataPathSteps([ + { + id: uniqueId(), + name: "Access to Funding: Eligibility", + path: `${history.location.pathname}${history.location.search}`, + }, + ]); + } }, []); function reloadData() { diff --git a/src/app/modules/viz-module/sub-modules/investments/disbursed/data-wrappers/generic.tsx b/src/app/modules/viz-module/sub-modules/investments/disbursed/data-wrappers/generic.tsx index ca2a4db8f..06024f351 100644 --- a/src/app/modules/viz-module/sub-modules/investments/disbursed/data-wrappers/generic.tsx +++ b/src/app/modules/viz-module/sub-modules/investments/disbursed/data-wrappers/generic.tsx @@ -1,6 +1,7 @@ /* third-party */ import React from "react"; import get from "lodash/get"; +import isEqual from "lodash/isEqual"; import { useUpdateEffect } from "react-use"; import useMediaQuery from "@material-ui/core/useMediaQuery"; import { useStoreActions, useStoreState } from "app/state/store/hooks"; @@ -8,8 +9,6 @@ import { useStoreActions, useStoreState } from "app/state/store/hooks"; import { getAPIFormattedFilters } from "app/utils/getAPIFormattedFilters"; import { DisbursementsTreemapDataItem } from "app/components/Charts/Investments/Disbursements/data"; import { InvestmentsDisbursedModule } from "app/modules/viz-module/sub-modules/investments/disbursed"; -import { useRecoilValue } from "recoil"; -import { breadCrumbItems } from "app/state/recoil/atoms"; interface Props { code?: string; @@ -19,23 +18,36 @@ interface Props { } export function GenericInvestmentsDisbursedWrapper(props: Props) { - const breadcrumbList = useRecoilValue(breadCrumbItems); - - const [vizLevel, setVizLevel] = React.useState( - breadcrumbList[breadcrumbList.length - 1]?.vizLevel || 0 - ); const isMobile = useMediaQuery("(max-width: 767px)"); + const dataPathSteps = useStoreState((state) => state.DataPathSteps.steps); const [vizSelected, setVizSelected] = React.useState( - breadcrumbList[breadcrumbList.length - 1]?.vizSelected as string + dataPathSteps[dataPathSteps.length - 1]?.vizSelected?.filterStr ); + const [drilldownVizSelected, setDrilldownVizSelected] = React.useState< + string | undefined + >(dataPathSteps[dataPathSteps.length - 1]?.drilldownVizSelected?.filterStr); + const [vizLevel, setVizLevel] = React.useState(0); React.useEffect(() => { - setVizSelected( - breadcrumbList[breadcrumbList.length - 1]?.vizSelected as string - ); - setVizLevel(breadcrumbList[breadcrumbList.length - 1]?.vizLevel || 0); - }, [breadcrumbList]); + const newVizSelected = + dataPathSteps[dataPathSteps.length - 1]?.vizSelected?.filterStr; + const newDrilldownVizSelected = + dataPathSteps[dataPathSteps.length - 1]?.drilldownVizSelected?.filterStr; + if (!isEqual(newVizSelected, vizSelected)) { + setVizSelected(newVizSelected); + } + if (!isEqual(newDrilldownVizSelected, drilldownVizSelected)) { + setDrilldownVizSelected(newDrilldownVizSelected); + } + if (newDrilldownVizSelected) { + setVizLevel(2); + } else if (newVizSelected) { + setVizLevel(1); + } else { + setVizLevel(0); + } + }, [dataPathSteps]); // api call & data const fetchData = useStoreActions((store) => { @@ -139,16 +151,19 @@ export function GenericInvestmentsDisbursedWrapper(props: Props) { if (vizSelected) { const splits = vizSelected.split("-"); let filterString = ""; - if (splits.length > 0) { + if (splits.length > 1) { if (!isMobile) { const locations = [...appliedFilters.locations]; + const components = [...appliedFilters.components]; if (props.code) { locations.push(props.code); } locations.push(splits[0]); + components.push(splits[1]); filterString = getAPIFormattedFilters({ ...appliedFilters, locations, + components, }); } else { const locations = [...appliedFilters.locations]; @@ -187,7 +202,7 @@ export function GenericInvestmentsDisbursedWrapper(props: Props) { data={data} allowDrilldown type={props.type} - vizLevel={vizLevel as number} + vizLevel={vizLevel} isLoading={isLoading} setVizLevel={setVizLevel} vizSelected={vizSelected} diff --git a/src/app/modules/viz-module/sub-modules/investments/disbursed/data-wrappers/locationDetail.tsx b/src/app/modules/viz-module/sub-modules/investments/disbursed/data-wrappers/locationDetail.tsx index 2f17aedfd..766e6e42e 100644 --- a/src/app/modules/viz-module/sub-modules/investments/disbursed/data-wrappers/locationDetail.tsx +++ b/src/app/modules/viz-module/sub-modules/investments/disbursed/data-wrappers/locationDetail.tsx @@ -1,13 +1,12 @@ /* third-party */ -import React, { useState } from "react"; +import React from "react"; import get from "lodash/get"; -import { useHistory } from "react-router-dom"; +import isEqual from "lodash/isEqual"; import { useStoreActions, useStoreState } from "app/state/store/hooks"; /* project */ import { getAPIFormattedFilters } from "app/utils/getAPIFormattedFilters"; import { DisbursementsTreemapDataItem } from "app/components/Charts/Investments/Disbursements/data"; import { InvestmentsDisbursedModule } from "app/modules/viz-module/sub-modules/investments/disbursed"; -import ReRouteDialogBox from "app/components/Charts/common/dialogBox"; interface Props { code: string; @@ -16,21 +15,34 @@ interface Props { } export function LocationDetailInvestmentsDisbursedWrapper(props: Props) { - const history = useHistory(); - const [vizLevel, setVizLevel] = React.useState(0); + const dataPathSteps = useStoreState((state) => state.DataPathSteps.steps); const [vizSelected, setVizSelected] = React.useState( - undefined + dataPathSteps[dataPathSteps.length - 1]?.vizSelected?.filterStr ); + const [drilldownVizSelected, setDrilldownVizSelected] = React.useState< + string | undefined + >(dataPathSteps[dataPathSteps.length - 1]?.drilldownVizSelected?.filterStr); + const [vizLevel, setVizLevel] = React.useState(0); - const [reRouteDialog, setReRouteDialog] = useState<{ - display: boolean; - code: string; - clickthroughPath?: string; - }>({ - display: false, - code: "", - clickthroughPath: "", - }); + React.useEffect(() => { + const newVizSelected = + dataPathSteps[dataPathSteps.length - 1]?.vizSelected?.filterStr; + const newDrilldownVizSelected = + dataPathSteps[dataPathSteps.length - 1]?.drilldownVizSelected?.filterStr; + if (!isEqual(newVizSelected, vizSelected)) { + setVizSelected(newVizSelected); + } + if (!isEqual(newDrilldownVizSelected, drilldownVizSelected)) { + setDrilldownVizSelected(newDrilldownVizSelected); + } + if (newDrilldownVizSelected) { + setVizLevel(2); + } else if (newVizSelected) { + setVizLevel(1); + } else { + setVizLevel(0); + } + }, [dataPathSteps]); // api call & data const fetchData = useStoreActions((store) => { @@ -75,20 +87,6 @@ export function LocationDetailInvestmentsDisbursedWrapper(props: Props) { const appliedFilters = useStoreState((state) => state.AppliedFiltersState); - function goToGrantDetail(code: string) { - let clickthroughPath = "signed/treemap"; - if (props.type === "Commitment") { - clickthroughPath = "commitment/treemap"; - } else if (props.type === "Disbursed") { - clickthroughPath = "disbursements/treemap"; - } - setReRouteDialog({ - display: true, - code, - clickthroughPath, - }); - } - React.useEffect(() => { const filterString = getAPIFormattedFilters( props.code @@ -102,34 +100,20 @@ export function LocationDetailInvestmentsDisbursedWrapper(props: Props) { }, [props.code, appliedFilters, props.type]); return ( - <> - {reRouteDialog.display && ( - - history.push( - `/grant/${reRouteDialog.code}/period/${reRouteDialog.clickthroughPath}` - ) - } - /> - )} - - + ); } diff --git a/src/app/modules/viz-module/sub-modules/investments/disbursed/data-wrappers/partnerDetail.tsx b/src/app/modules/viz-module/sub-modules/investments/disbursed/data-wrappers/partnerDetail.tsx index a3708de5f..1264354b2 100644 --- a/src/app/modules/viz-module/sub-modules/investments/disbursed/data-wrappers/partnerDetail.tsx +++ b/src/app/modules/viz-module/sub-modules/investments/disbursed/data-wrappers/partnerDetail.tsx @@ -1,13 +1,13 @@ /* third-party */ -import React, { useState } from "react"; +import React from "react"; import get from "lodash/get"; +import isEqual from "lodash/isEqual"; import { useHistory } from "react-router-dom"; import { useStoreActions, useStoreState } from "app/state/store/hooks"; /* project */ import { getAPIFormattedFilters } from "app/utils/getAPIFormattedFilters"; import { DisbursementsTreemapDataItem } from "app/components/Charts/Investments/Disbursements/data"; import { InvestmentsDisbursedModule } from "app/modules/viz-module/sub-modules/investments/disbursed"; -import ReRouteDialogBox from "app/components/Charts/common/dialogBox"; interface Props { code: string; @@ -16,21 +16,34 @@ interface Props { } export function PartnerDetailInvestmentsDisbursedWrapper(props: Props) { - const history = useHistory(); - const [vizLevel, setVizLevel] = React.useState(0); + const dataPathSteps = useStoreState((state) => state.DataPathSteps.steps); const [vizSelected, setVizSelected] = React.useState( - undefined + dataPathSteps[dataPathSteps.length - 1]?.vizSelected?.filterStr ); + const [drilldownVizSelected, setDrilldownVizSelected] = React.useState< + string | undefined + >(dataPathSteps[dataPathSteps.length - 1]?.drilldownVizSelected?.filterStr); + const [vizLevel, setVizLevel] = React.useState(0); - const [reRouteDialog, setReRouteDialog] = useState<{ - display: boolean; - code: string; - clickthroughPath?: string; - }>({ - display: false, - code: "", - clickthroughPath: "", - }); + React.useEffect(() => { + const newVizSelected = + dataPathSteps[dataPathSteps.length - 1]?.vizSelected?.filterStr; + const newDrilldownVizSelected = + dataPathSteps[dataPathSteps.length - 1]?.drilldownVizSelected?.filterStr; + if (!isEqual(newVizSelected, vizSelected)) { + setVizSelected(newVizSelected); + } + if (!isEqual(newDrilldownVizSelected, drilldownVizSelected)) { + setDrilldownVizSelected(newDrilldownVizSelected); + } + if (newDrilldownVizSelected) { + setVizLevel(2); + } else if (newVizSelected) { + setVizLevel(1); + } else { + setVizLevel(0); + } + }, [dataPathSteps]); // api call & data const fetchData = useStoreActions((store) => { @@ -74,21 +87,6 @@ export function PartnerDetailInvestmentsDisbursedWrapper(props: Props) { const appliedFilters = useStoreState((state) => state.AppliedFiltersState); - function goToGrantDetail(code: string) { - let clickthroughPath = "signed/treemap"; - if (props.type === "Commitment") { - clickthroughPath = "commitment/treemap"; - } else if (props.type === "Disbursed") { - clickthroughPath = "disbursements/treemap"; - } - setReRouteDialog({ - display: true, - code, - clickthroughPath, - }); - history.push(`/grant/${code}/period/${clickthroughPath}`); - } - React.useEffect(() => { const filterString = getAPIFormattedFilters( props.code @@ -102,34 +100,20 @@ export function PartnerDetailInvestmentsDisbursedWrapper(props: Props) { }, [props.code, appliedFilters, props.type]); return ( - <> - {reRouteDialog.display && ( - - history.push( - `/grant/${reRouteDialog.code}/period/${reRouteDialog.clickthroughPath}` - ) - } - /> - )} - - + ); } diff --git a/src/app/modules/viz-module/sub-modules/investments/disbursed/index.tsx b/src/app/modules/viz-module/sub-modules/investments/disbursed/index.tsx index 242f398fb..414fa02ac 100644 --- a/src/app/modules/viz-module/sub-modules/investments/disbursed/index.tsx +++ b/src/app/modules/viz-module/sub-modules/investments/disbursed/index.tsx @@ -1,6 +1,5 @@ /* third-party */ import React from "react"; -import { v4 } from "uuid"; import find from "lodash/find"; import maxBy from "lodash/maxBy"; import sumBy from "lodash/sumBy"; @@ -8,16 +7,15 @@ import filter from "lodash/filter"; import { useTitle } from "react-use"; import { appColors } from "app/theme"; import uniqueId from "lodash/uniqueId"; -import { useRecoilState } from "recoil"; import Grid from "@material-ui/core/Grid"; import { useHistory } from "react-router-dom"; -import { breadCrumbItems } from "app/state/recoil/atoms"; import useMediaQuery from "@material-ui/core/useMediaQuery"; import { useStoreActions, useStoreState } from "app/state/store/hooks"; /* project */ import { PageLoader } from "app/modules/common/page-loader"; +import { getNameFromIso3 } from "app/utils/getIso3FromName"; +import ReRouteDialogBox from "app/components/Charts/common/dialogBox"; import { formatFinancialValue } from "app/utils/formatFinancialValue"; -import { getIso3FromName, getNameFromIso3 } from "app/utils/getIso3FromName"; import { DisbursementsTreemap } from "app/components/Charts/Investments/Disbursements"; import { DisbursementsTreemapDataItem } from "app/components/Charts/Investments/Disbursements/data"; @@ -72,7 +70,6 @@ export function InvestmentsDisbursedModule( ) { useTitle("The Data Explorer - Investments/Disbursed"); const isMobile = useMediaQuery("(max-width: 767px)"); - const totalValue = sumBy(props.data, "value"); const history = useHistory(); @@ -80,34 +77,30 @@ export function InvestmentsDisbursedModule( DisbursementsTreemapDataItem[] >(props.data); - const [breadCrumbList, setBreadCrumbList] = useRecoilState(breadCrumbItems); - const breadcrumbID = v4(); + const [reRouteDialog, setReRouteDialog] = React.useState({ + display: false, + code: "", + }); const dataPathSteps = useStoreState((state) => state.DataPathSteps.steps); const addDataPathSteps = useStoreActions( (actions) => actions.DataPathSteps.addSteps ); + const totalValue = React.useMemo( + () => sumBy(props.data, "value"), + [props.data] + ); + React.useEffect(() => { if (props.vizLevel === 0) { if ( - dataPathSteps.length === 0 || - !find(dataPathSteps, { name: `${props.type}-treemap` }) - ) { - addDataPathSteps([ - { - id: uniqueId(), - name: `${props.type}-treemap`, - path: `${history.location.pathname}${history.location.search}`, - }, - ]); - } else if ( props.isGrantDetail && !find(dataPathSteps, (step) => step.path.indexOf("/grant/") > -1) ) { addDataPathSteps([ { - id: uniqueId(), + id: "grant", name: props.codeParam || "Grant", path: `${history.location.pathname}${history.location.search}`, }, @@ -118,7 +111,7 @@ export function InvestmentsDisbursedModule( ) { addDataPathSteps([ { - id: uniqueId(), + id: "location", name: props.codeParam ? getNameFromIso3(props.codeParam) : "Location", @@ -131,32 +124,34 @@ export function InvestmentsDisbursedModule( ) { addDataPathSteps([ { - id: uniqueId(), + id: "partner", name: props.codeParam || "Partner", path: `${history.location.pathname}${history.location.search}`, }, ]); } + if ( + dataPathSteps.length === 0 || + !find(dataPathSteps, { name: `Grant Implementation: ${props.type}` }) + ) { + addDataPathSteps([ + { + id: uniqueId(), + name: `Grant Implementation: ${props.type}`, + path: `${history.location.pathname}${history.location.search}`, + }, + ]); + } } if (props.vizLevel > 0 && props.vizSelected) { - const code = props.vizSelected.split("-")[0]; - let name = ""; - props.data.forEach((item: DisbursementsTreemapDataItem) => { - if (name.length === 0) { - const fItem = find(item._children, { code }); - if (fItem) { - name = fItem.name; - } - } - }); addDataPathSteps([ { id: uniqueId(), - name: name || code, + name: props.vizSelected, path: `${history.location.pathname}${history.location.search}`, vizSelected: { - id: props.vizSelected || "", - filterStr: props.vizSelected || "", + id: props.vizSelected, + filterStr: props.vizSelected, }, }, ]); @@ -223,16 +218,6 @@ export function InvestmentsDisbursedModule( if (props.allowDrilldown) { props.setVizLevel(1); props.setVizSelected(node); - setBreadCrumbList([ - ...breadCrumbList, - { - name: name as string, - path: location.pathname, - id: breadcrumbID, - vizLevel: 1, - vizSelected: node, - }, - ]); } else if (props.onNodeClick && code) { props.onNodeClick(code); } @@ -245,18 +230,14 @@ export function InvestmentsDisbursedModule( isDrilldownTreemap data={props.drilldownData} onNodeClick={(node: string) => { - const idSplits = node.split("-"); - const code = getIso3FromName(idSplits[1]); - addDataPathSteps([ - { - id: uniqueId(), - name: `${idSplits[1]} - ${idSplits[0]}`, - path: `/location/${code}/${clickthroughPath}?components=${idSplits[0]}`, - }, - ]); - history.push( - `/location/${code}/${clickthroughPath}?components=${idSplits[0]}` - ); + const code = node + .split("-") + .slice(0, node.split("-").length - 1) + .join("-"); + setReRouteDialog({ + display: true, + code, + }); }} /> ); @@ -265,6 +246,17 @@ export function InvestmentsDisbursedModule( return ( + {reRouteDialog.display && ( + + history.push( + `/grant/${reRouteDialog.code}/period/${clickthroughPath}` + ) + } + /> + )} { if ( dataPathSteps.length === 0 || - !find(dataPathSteps, { name: `${props.type}-map` }) + !find(dataPathSteps, { name: `Grant Implementation: ${props.type}` }) ) { addDataPathSteps([ { id: uniqueId(), - name: `${props.type}-map`, + name: `Grant Implementation: ${props.type}`, path: `${history.location.pathname}${history.location.search}`, }, ]); diff --git a/src/app/modules/viz-module/sub-modules/investments/time-cycle/data-wrappers/generic.tsx b/src/app/modules/viz-module/sub-modules/investments/time-cycle/data-wrappers/generic.tsx index 0aca243d3..e19c4f695 100644 --- a/src/app/modules/viz-module/sub-modules/investments/time-cycle/data-wrappers/generic.tsx +++ b/src/app/modules/viz-module/sub-modules/investments/time-cycle/data-wrappers/generic.tsx @@ -1,15 +1,15 @@ /* third-party */ import React from "react"; import get from "lodash/get"; +import isEqual from "lodash/isEqual"; import { useUpdateEffect } from "react-use"; import useTitle from "react-use/lib/useTitle"; import { useStoreActions, useStoreState } from "app/state/store/hooks"; /* project */ +import { getIso3FromName } from "app/utils/getIso3FromName"; import { getAPIFormattedFilters } from "app/utils/getAPIFormattedFilters"; import { BudgetsTreemapDataItem } from "app/components/Charts/Budgets/Treemap/data"; import { InvestmentsTimeCycleModule } from "app/modules/viz-module/sub-modules/investments/time-cycle"; -import { useRecoilValue } from "recoil"; -import { breadCrumbItems } from "app/state/recoil/atoms"; interface Props { code?: string; @@ -20,20 +20,35 @@ interface Props { export function GenericInvestmentsTimeCycleWrapper(props: Props) { useTitle("The Data Explorer - Investments/Time cycle"); - const breadcrumbList = useRecoilValue(breadCrumbItems); - const [vizLevel, setVizLevel] = React.useState( - breadcrumbList[breadcrumbList.length - 1]?.vizLevel || 0 - ); + const dataPathSteps = useStoreState((state) => state.DataPathSteps.steps); const [vizSelected, setVizSelected] = React.useState( - breadcrumbList[breadcrumbList.length - 1]?.vizSelected as string + dataPathSteps[dataPathSteps.length - 1]?.vizSelected?.filterStr ); + const [drilldownVizSelected, setDrilldownVizSelected] = React.useState< + string | undefined + >(dataPathSteps[dataPathSteps.length - 1]?.drilldownVizSelected?.filterStr); + const [vizLevel, setVizLevel] = React.useState(0); + React.useEffect(() => { - setVizSelected( - breadcrumbList[breadcrumbList.length - 1]?.vizSelected as string - ); - setVizLevel(breadcrumbList[breadcrumbList.length - 1]?.vizLevel || 0); - }, [breadcrumbList]); + const newVizSelected = + dataPathSteps[dataPathSteps.length - 1]?.vizSelected?.filterStr; + const newDrilldownVizSelected = + dataPathSteps[dataPathSteps.length - 1]?.drilldownVizSelected?.filterStr; + if (!isEqual(newVizSelected, vizSelected)) { + setVizSelected(newVizSelected); + } + if (!isEqual(newDrilldownVizSelected, drilldownVizSelected)) { + setDrilldownVizSelected(newDrilldownVizSelected); + } + if (newDrilldownVizSelected) { + setVizLevel(2); + } else if (newVizSelected) { + setVizLevel(1); + } else { + setVizLevel(0); + } + }, [dataPathSteps]); // api call & data const fetchData = useStoreActions((store) => { @@ -112,6 +127,44 @@ export function GenericInvestmentsTimeCycleWrapper(props: Props) { return state.DisbursementsTimeCycleDrilldown.loading; } }); + const fetchDrilldown2Data = useStoreActions((store) => { + switch (props.type) { + case "Disbursed": + return store.DisbursementsTimeCycleDrilldown2.fetch; + case "Signed": + return store.SignedTimeCycleDrilldown2.fetch; + case "Commitment": + return store.CommitmentTimeCycleDrilldown2.fetch; + default: + return store.DisbursementsTimeCycleDrilldown2.fetch; + } + }); + const drilldown2Data = useStoreState((state) => { + let compData = state.DisbursementsTimeCycleDrilldown2.data; + switch (props.type) { + case "Signed": + compData = state.SignedTimeCycleDrilldown2.data; + break; + case "Commitment": + compData = state.CommitmentTimeCycleDrilldown2.data; + break; + default: + compData = state.DisbursementsTimeCycleDrilldown2.data; + } + return get(compData, "data", []) as BudgetsTreemapDataItem[]; + }); + const isDrilldown2Loading = useStoreState((state) => { + switch (props.type) { + case "Disbursed": + return state.DisbursementsTimeCycleDrilldown2.loading; + case "Signed": + return state.SignedTimeCycleDrilldown2.loading; + case "Commitment": + return state.CommitmentTimeCycleDrilldown2.loading; + default: + return state.DisbursementsTimeCycleDrilldown2.loading; + } + }); const dataPathActiveStep = useStoreState( (state) => state.DataPathActiveStep.step ); @@ -134,7 +187,7 @@ export function GenericInvestmentsTimeCycleWrapper(props: Props) { }, [props.code, appliedFilters, props.type]); useUpdateEffect(() => { - if (vizSelected) { + if (vizSelected && vizLevel === 1) { let filterString = getAPIFormattedFilters( props.code ? { @@ -149,8 +202,31 @@ export function GenericInvestmentsTimeCycleWrapper(props: Props) { filterString = `barPeriod=${vizSelected}`; } fetchDrilldownData({ filterString }); + } else if (drilldownVizSelected && vizLevel === 2) { + const splits = drilldownVizSelected.split("-"); + const location = getIso3FromName(splits[0]); + const component = splits[1]; + let filterString = getAPIFormattedFilters( + props.code + ? { + ...appliedFilters, + locations: [...appliedFilters.locations, props.code, location], + components: [...appliedFilters.components, component], + } + : { + ...appliedFilters, + locations: [...appliedFilters.locations, location], + components: [...appliedFilters.components, component], + } + ); + if (filterString) { + filterString += `&barPeriod=${vizSelected}`; + } else { + filterString = `barPeriod=${vizSelected}`; + } + fetchDrilldown2Data({ filterString }); } - }, [vizSelected]); + }, [vizSelected, drilldownVizSelected]); React.useEffect(() => { if (dataPathActiveStep) { @@ -181,6 +257,10 @@ export function GenericInvestmentsTimeCycleWrapper(props: Props) { setVizSelected={setVizSelected} toolboxOpen={props.toolboxOpen} setOpenToolboxPanel={props.setOpenToolboxPanel} + isDrilldown2Loading={isDrilldown2Loading} + drilldown2Data={drilldown2Data} + drilldownVizSelected={drilldownVizSelected} + setDrilldownVizSelected={setDrilldownVizSelected} /> ); } diff --git a/src/app/modules/viz-module/sub-modules/investments/time-cycle/data-wrappers/grantDetail.tsx b/src/app/modules/viz-module/sub-modules/investments/time-cycle/data-wrappers/grantDetail.tsx index 04a3bb0fd..9108e130f 100644 --- a/src/app/modules/viz-module/sub-modules/investments/time-cycle/data-wrappers/grantDetail.tsx +++ b/src/app/modules/viz-module/sub-modules/investments/time-cycle/data-wrappers/grantDetail.tsx @@ -70,15 +70,15 @@ export function GrantDetailInvestmentsTimeCycleWrapper(props: Props) { return ( diff --git a/src/app/modules/viz-module/sub-modules/investments/time-cycle/data-wrappers/partnerDetail.tsx b/src/app/modules/viz-module/sub-modules/investments/time-cycle/data-wrappers/partnerDetail.tsx index 5a7918a52..5cb545c4c 100644 --- a/src/app/modules/viz-module/sub-modules/investments/time-cycle/data-wrappers/partnerDetail.tsx +++ b/src/app/modules/viz-module/sub-modules/investments/time-cycle/data-wrappers/partnerDetail.tsx @@ -1,6 +1,7 @@ /* third-party */ import React from "react"; import get from "lodash/get"; +import isEqual from "lodash/isEqual"; import { useUpdateEffect } from "react-use"; import useTitle from "react-use/lib/useTitle"; import { useStoreActions, useStoreState } from "app/state/store/hooks"; @@ -17,10 +18,35 @@ interface Props { export function PartnerDetailInvestmentsTimeCycleWrapper(props: Props) { useTitle("The Data Explorer - Investments/Time cycle"); - const [vizLevel, setVizLevel] = React.useState(0); + + const dataPathSteps = useStoreState((state) => state.DataPathSteps.steps); const [vizSelected, setVizSelected] = React.useState( - undefined + dataPathSteps[dataPathSteps.length - 1]?.vizSelected?.filterStr ); + const [drilldownVizSelected, setDrilldownVizSelected] = React.useState< + string | undefined + >(dataPathSteps[dataPathSteps.length - 1]?.drilldownVizSelected?.filterStr); + const [vizLevel, setVizLevel] = React.useState(0); + + React.useEffect(() => { + const newVizSelected = + dataPathSteps[dataPathSteps.length - 1]?.vizSelected?.filterStr; + const newDrilldownVizSelected = + dataPathSteps[dataPathSteps.length - 1]?.drilldownVizSelected?.filterStr; + if (!isEqual(newVizSelected, vizSelected)) { + setVizSelected(newVizSelected); + } + if (!isEqual(newDrilldownVizSelected, drilldownVizSelected)) { + setDrilldownVizSelected(newDrilldownVizSelected); + } + if (newDrilldownVizSelected) { + setVizLevel(2); + } else if (newVizSelected) { + setVizLevel(1); + } else { + setVizLevel(0); + } + }, [dataPathSteps]); // api call & data const fetchData = useStoreActions((store) => { diff --git a/src/app/modules/viz-module/sub-modules/investments/time-cycle/index.tsx b/src/app/modules/viz-module/sub-modules/investments/time-cycle/index.tsx index 5755752e9..79148112a 100644 --- a/src/app/modules/viz-module/sub-modules/investments/time-cycle/index.tsx +++ b/src/app/modules/viz-module/sub-modules/investments/time-cycle/index.tsx @@ -1,12 +1,9 @@ /* third-party */ import React from "react"; -import { v4 } from "uuid"; import find from "lodash/find"; import uniqueId from "lodash/uniqueId"; -import { useRecoilState } from "recoil"; import { useHistory } from "react-router-dom"; import { TreeMapNodeDatum } from "@nivo/treemap"; -import { breadCrumbItems } from "app/state/recoil/atoms"; import { useStoreActions, useStoreState } from "app/state/store/hooks"; /* project */ import { PageLoader } from "app/modules/common/page-loader"; @@ -14,6 +11,9 @@ import { BudgetsTreemap } from "app/components/Charts/Budgets/Treemap"; import { getIso3FromName, getNameFromIso3 } from "app/utils/getIso3FromName"; import { InvestmentsTimeCycle } from "app/components/Charts/Investments/TimeCycle"; import { BudgetsTreemapDataItem } from "app/components/Charts/Budgets/Treemap/data"; +import { DisbursementsTreemap } from "app/components/Charts/Investments/Disbursements"; +import { DisbursementsTreemapDataItem } from "app/components/Charts/Investments/Disbursements/data"; +import ReRouteDialogBox from "app/components/Charts/common/dialogBox"; interface InvestmentsTimeCycleModuleProps { data: Record[]; @@ -23,6 +23,8 @@ interface InvestmentsTimeCycleModuleProps { vizLevel: number; setVizLevel: (vizLevel: number) => void; vizSelected: string | undefined; + setDrilldownVizSelected?: (vizSelected: string | undefined) => void; + drilldownVizSelected?: string | undefined; setVizSelected: (vizSelected: string | undefined) => void; type?: string; toolboxOpen?: boolean; @@ -31,6 +33,8 @@ interface InvestmentsTimeCycleModuleProps { isGrantDetail?: boolean; isPartnerDetail?: boolean; isLocationDetail?: boolean; + isDrilldown2Loading?: boolean; + drilldown2Data?: BudgetsTreemapDataItem[]; } export function InvestmentsTimeCycleModule( @@ -41,33 +45,25 @@ export function InvestmentsTimeCycleModule( const [xsTooltipData, setXsTooltipData] = React.useState(null); + const [reRouteDialog, setReRouteDialog] = React.useState({ + display: false, + code: "", + }); + const dataPathSteps = useStoreState((state) => state.DataPathSteps.steps); const addDataPathSteps = useStoreActions( (actions) => actions.DataPathSteps.addSteps ); - const [breadCrumbList, setBreadCrumbList] = useRecoilState(breadCrumbItems); - React.useEffect(() => { if (props.vizLevel === 0) { if ( - dataPathSteps.length === 0 || - !find(dataPathSteps, { name: "Budget-time cycle" }) - ) { - addDataPathSteps([ - { - id: uniqueId(), - name: "Budget-time cycle", - path: `${history.location.pathname}${history.location.search}`, - }, - ]); - } else if ( props.isGrantDetail && !find(dataPathSteps, (step) => step.path.indexOf("/grant/") > -1) ) { addDataPathSteps([ { - id: uniqueId(), + id: "grant", name: props.codeParam || "Grant", path: `${history.location.pathname}${history.location.search}`, }, @@ -78,7 +74,7 @@ export function InvestmentsTimeCycleModule( ) { addDataPathSteps([ { - id: uniqueId(), + id: "location", name: props.codeParam ? getNameFromIso3(props.codeParam) : "Location", @@ -91,27 +87,26 @@ export function InvestmentsTimeCycleModule( ) { addDataPathSteps([ { - id: uniqueId(), + id: "partner", name: props.codeParam || "Partner", path: `${history.location.pathname}${history.location.search}`, }, ]); } - } - if (props.vizLevel > 0 && props.vizSelected) { - addDataPathSteps([ - { - id: uniqueId(), - name: props.vizSelected, - path: `${history.location.pathname}${history.location.search}`, - vizSelected: { - id: props.vizSelected, - filterStr: props.vizSelected, + if ( + dataPathSteps.length === 0 || + !find(dataPathSteps, { name: `Grant Implementation: ${props.type}` }) + ) { + addDataPathSteps([ + { + id: uniqueId(), + name: `Grant Implementation: ${props.type}`, + path: `${history.location.pathname}${history.location.search}`, }, - }, - ]); + ]); + } } - }, [props.vizLevel, props.vizSelected]); + }, [props.vizLevel, props.vizSelected, props.drilldownVizSelected]); let clickthroughPath = "signed/treemap"; if (props.type === "Commitment") { @@ -122,7 +117,11 @@ export function InvestmentsTimeCycleModule( let vizComponent = ; - if (props.isLoading || props.isDrilldownLoading) { + if ( + props.isLoading || + props.isDrilldownLoading || + props.isDrilldown2Loading + ) { vizComponent = ; } else { if (props.vizLevel === 0) { @@ -130,20 +129,21 @@ export function InvestmentsTimeCycleModule( { - setBreadCrumbList([ - ...breadCrumbList, + onNodeClick={(node: string, _x: number, _y: number) => { + props.setVizLevel(1); + props.setVizSelected(node); + addDataPathSteps([ { + // TODO: implement changes applied here to the other viz modules + id: uniqueId(), name: node, - path: location.pathname, - id: v4(), - vizLevel: 1, - vizSelected: node, + path: `${history.location.pathname}${history.location.search}`, + vizSelected: { + id: node, + filterStr: node, + }, }, ]); - props.setVizLevel(1); - props.setVizSelected(node); }} /> ); @@ -154,47 +154,70 @@ export function InvestmentsTimeCycleModule( xsTooltipData={xsTooltipData} tooltipValueLabel="Disbursements" setXsTooltipData={setXsTooltipData} - onNodeClick={(node: string, x: number, y: number) => { - // props.setVizLevel(2); - // props.setVizSelected(node); + onNodeClick={(node: string, _x: number, _y: number) => { + if (props.setDrilldownVizSelected) { + props.setVizLevel(2); + props.setDrilldownVizSelected(node); + addDataPathSteps([ + { + id: uniqueId(), + name: node, + path: `${history.location.pathname}${history.location.search}`, + drilldownVizSelected: { + id: node, + filterStr: node, + }, + }, + ]); + } + }} + /> + ); + } else if (props.vizLevel === 2 && props.drilldown2Data) { + vizComponent = ( + { const idSplits = node.split("-"); const code = getIso3FromName(idSplits[0]); - addDataPathSteps([ - { - id: uniqueId(), - name: `${idSplits[0]} - ${idSplits[1]}`, - path: `/location/${code}/${clickthroughPath}?components=${idSplits[1]}`, - }, - ]); - history.push( - `/location/${code}/${clickthroughPath}?components=${idSplits[1]}` - ); + setReRouteDialog({ + display: true, + code, + }); }} /> ); - } else if (props.vizLevel === 2) { - // vizComponent = ( - // {}} - // /> - // ); } } return ( -
+ {reRouteDialog.display && ( + + history.push( + `/grant/${reRouteDialog.code}/period/${clickthroughPath}` + ) + } + /> + )} +
- {vizComponent} -
+ * { + overflow: visible !important; + } + `} + > + {vizComponent} +
+
); } diff --git a/src/app/modules/viz-module/sub-modules/pledgescontributions/geomap/index.tsx b/src/app/modules/viz-module/sub-modules/pledgescontributions/geomap/index.tsx index 0ebd6010f..25ac7dd19 100644 --- a/src/app/modules/viz-module/sub-modules/pledgescontributions/geomap/index.tsx +++ b/src/app/modules/viz-module/sub-modules/pledgescontributions/geomap/index.tsx @@ -1,9 +1,12 @@ /* third-party */ import React from "react"; import get from "lodash/get"; +import find from "lodash/find"; import filter from "lodash/filter"; import { appColors } from "app/theme"; +import uniqueId from "lodash/uniqueId"; import { FeatureCollection } from "geojson"; +import { useHistory } from "react-router-dom"; import useTitle from "react-use/lib/useTitle"; import { useStoreActions, useStoreState } from "app/state/store/hooks"; /* project */ @@ -15,6 +18,9 @@ import { getAPIFormattedFilters } from "app/utils/getAPIFormattedFilters"; export function PledgesContributionsGeoMap() { useTitle("The Data Explorer - Pledges & Contributions GeoMap"); + + const history = useHistory(); + const valueType = useStoreState( (state) => state.ToolBoxPanelDonorMapTypeState.value ); @@ -50,6 +56,28 @@ export function PledgesContributionsGeoMap() { const appliedFilters = useStoreState((state) => state.AppliedFiltersState); + const dataPathSteps = useStoreState((state) => state.DataPathSteps.steps); + const addDataPathSteps = useStoreActions( + (actions) => actions.DataPathSteps.addSteps + ); + + React.useEffect(() => { + if ( + dataPathSteps.length === 0 || + !find(dataPathSteps, { + name: "Resource Mobilization: Pledges & Contributions", + }) + ) { + addDataPathSteps([ + { + id: uniqueId(), + name: "Resource Mobilization: Pledges & Contributions", + path: `${history.location.pathname}${history.location.search}`, + }, + ]); + } + }, []); + React.useEffect(() => { const filterString = getAPIFormattedFilters(appliedFilters); fetchData({ diff --git a/src/app/modules/viz-module/sub-modules/pledgescontributions/table/index.tsx b/src/app/modules/viz-module/sub-modules/pledgescontributions/table/index.tsx index 6d1c92c23..d1b4be068 100644 --- a/src/app/modules/viz-module/sub-modules/pledgescontributions/table/index.tsx +++ b/src/app/modules/viz-module/sub-modules/pledgescontributions/table/index.tsx @@ -1,9 +1,12 @@ /* third-party */ import React from "react"; import get from "lodash/get"; +import find from "lodash/find"; import filter from "lodash/filter"; -import { useDebounce, useTitle, useUpdateEffect } from "react-use"; +import uniqueId from "lodash/uniqueId"; +import { useHistory } from "react-router-dom"; import TablePagination from "@material-ui/core/TablePagination"; +import { useDebounce, useTitle, useUpdateEffect } from "react-use"; import { useStoreActions, useStoreState } from "app/state/store/hooks"; /* project */ import { SimpleTable } from "app/components/Table/Simple"; @@ -14,6 +17,8 @@ import { getAPIFormattedFilters } from "app/utils/getAPIFormattedFilters"; export function PledgesContributionsTable() { useTitle("The Data Explorer - Pledges & Contributions Table"); + const history = useHistory(); + const [page, setPage] = React.useState(0); const [search, setSearch] = React.useState(""); const [sortBy, setSortBy] = React.useState("name ASC"); @@ -46,6 +51,28 @@ export function PledgesContributionsTable() { }); } + const dataPathSteps = useStoreState((state) => state.DataPathSteps.steps); + const addDataPathSteps = useStoreActions( + (actions) => actions.DataPathSteps.addSteps + ); + + React.useEffect(() => { + if ( + dataPathSteps.length === 0 || + !find(dataPathSteps, { + name: "Resource Mobilization: Pledges & Contributions", + }) + ) { + addDataPathSteps([ + { + id: uniqueId(), + name: "Resource Mobilization: Pledges & Contributions", + path: `${history.location.pathname}${history.location.search}`, + }, + ]); + } + }, []); + React.useEffect( () => reloadData(), [selectedAggregation, appliedFilters, sortBy] diff --git a/src/app/modules/viz-module/sub-modules/pledgescontributions/time-cycle/index.tsx b/src/app/modules/viz-module/sub-modules/pledgescontributions/time-cycle/index.tsx index 84e033a54..bf62170f9 100644 --- a/src/app/modules/viz-module/sub-modules/pledgescontributions/time-cycle/index.tsx +++ b/src/app/modules/viz-module/sub-modules/pledgescontributions/time-cycle/index.tsx @@ -1,6 +1,9 @@ /* third-party */ import React from "react"; import get from "lodash/get"; +import find from "lodash/find"; +import uniqueId from "lodash/uniqueId"; +import { useHistory } from "react-router-dom"; import { useTitle, useUnmount, useUpdateEffect } from "react-use"; import { useStoreActions, useStoreState } from "app/state/store/hooks"; /* project */ @@ -15,6 +18,8 @@ import { PledgesContributionsTreemapDataItem } from "app/components/Charts/Pledg export function PledgesContributionsTimeCycleModule() { useTitle("The Data Explorer - Pledges & Contributions/Time cycle"); + const history = useHistory(); + const [vizLevel, setVizLevel] = React.useState(0); const [vizSelected, setVizSelected] = React.useState( undefined @@ -58,6 +63,28 @@ export function PledgesContributionsTimeCycleModule() { (actions) => actions.PageHeaderVizDrilldownsState.setValue ); + const dataPathSteps = useStoreState((state) => state.DataPathSteps.steps); + const addDataPathSteps = useStoreActions( + (actions) => actions.DataPathSteps.addSteps + ); + + React.useEffect(() => { + if ( + dataPathSteps.length === 0 || + !find(dataPathSteps, { + name: "Resource Mobilization: Pledges & Contributions", + }) + ) { + addDataPathSteps([ + { + id: uniqueId(), + name: "Resource Mobilization: Pledges & Contributions", + path: `${history.location.pathname}${history.location.search}`, + }, + ]); + } + }, []); + React.useEffect(() => { if (vizLevel === 0) { setVizDrilldowns([]); diff --git a/src/app/modules/viz-module/sub-modules/pledgescontributions/treemap/index.tsx b/src/app/modules/viz-module/sub-modules/pledgescontributions/treemap/index.tsx index c821809d8..8d0d04547 100644 --- a/src/app/modules/viz-module/sub-modules/pledgescontributions/treemap/index.tsx +++ b/src/app/modules/viz-module/sub-modules/pledgescontributions/treemap/index.tsx @@ -1,10 +1,13 @@ /* third-party */ import React from "react"; import get from "lodash/get"; +import find from "lodash/find"; import sumBy from "lodash/sumBy"; import maxBy from "lodash/maxBy"; import filter from "lodash/filter"; +import uniqueId from "lodash/uniqueId"; import Grid from "@material-ui/core/Grid"; +import { useHistory } from "react-router-dom"; import { TreeMapNodeDatum } from "@nivo/treemap"; import { useTitle, useUpdateEffect } from "react-use"; import { useStoreActions, useStoreState } from "app/state/store/hooks"; @@ -18,6 +21,9 @@ import { appColors } from "app/theme"; export function PledgesContributionsTreemap() { useTitle("The Data Explorer - Pledges & Contributions/Treemap"); + + const history = useHistory(); + const [vizSelected, setVizSelected] = React.useState( undefined ); @@ -62,6 +68,28 @@ export function PledgesContributionsTreemap() { (state) => state.ToolBoxPanelDonorMapTypeState.value ); + const dataPathSteps = useStoreState((state) => state.DataPathSteps.steps); + const addDataPathSteps = useStoreActions( + (actions) => actions.DataPathSteps.addSteps + ); + + React.useEffect(() => { + if ( + dataPathSteps.length === 0 || + !find(dataPathSteps, { + name: "Resource Mobilization: Pledges & Contributions", + }) + ) { + addDataPathSteps([ + { + id: uniqueId(), + name: "Resource Mobilization: Pledges & Contributions", + path: `${history.location.pathname}${history.location.search}`, + }, + ]); + } + }, []); + React.useEffect(() => { const filterString = getAPIFormattedFilters(appliedFilters); fetchData({ diff --git a/src/app/state/api/action-reducers/sync/dataPath/index.ts b/src/app/state/api/action-reducers/sync/dataPath/index.ts index bb1f68d78..fcb1c082e 100644 --- a/src/app/state/api/action-reducers/sync/dataPath/index.ts +++ b/src/app/state/api/action-reducers/sync/dataPath/index.ts @@ -2,18 +2,6 @@ import { action, Action } from "easy-peasy"; import { DrilldownModelUpdated } from "app/interfaces"; -export interface DataPathPanelVisibilityStateModel { - value: boolean; - setValue: Action; -} - -export const DataPathPanelVisibilityState: DataPathPanelVisibilityStateModel = { - value: false, - setValue: action((state, payload: boolean) => { - state.value = payload; - }), -}; - export interface DataPathStepsStateModel { steps: DrilldownModelUpdated[]; addSteps: Action; diff --git a/src/app/state/api/action-reducers/viz/commitment.ts b/src/app/state/api/action-reducers/viz/commitment.ts index 8d49b6043..c0f65ace8 100644 --- a/src/app/state/api/action-reducers/viz/commitment.ts +++ b/src/app/state/api/action-reducers/viz/commitment.ts @@ -18,3 +18,7 @@ export const CommitmentTimeCycle: ApiCallModel = { export const CommitmentTimeCycleDrilldown: ApiCallModel = { ...APIModel(`${process.env.REACT_APP_API}/commitment/time-cycle/drilldown`), }; + +export const CommitmentTimeCycleDrilldown2: ApiCallModel = { + ...APIModel(`${process.env.REACT_APP_API}/commitment/treemap/drilldown`), +}; diff --git a/src/app/state/api/action-reducers/viz/disbursementsTimeCycle.ts b/src/app/state/api/action-reducers/viz/disbursementsTimeCycle.ts index c45d31374..032e9ef51 100644 --- a/src/app/state/api/action-reducers/viz/disbursementsTimeCycle.ts +++ b/src/app/state/api/action-reducers/viz/disbursementsTimeCycle.ts @@ -12,3 +12,9 @@ export const DisbursementsTimeCycleDrilldown: ApiCallModel = { `${process.env.REACT_APP_API}/disbursements/time-cycle/drilldown` ), }; + +export const DisbursementsTimeCycleDrilldown2: ApiCallModel = { + ...APIModel( + `${process.env.REACT_APP_API}/disbursements/time-cycle/drilldown/2` + ), +}; diff --git a/src/app/state/api/action-reducers/viz/signed.ts b/src/app/state/api/action-reducers/viz/signed.ts index 4122be7ff..66e1d6f50 100644 --- a/src/app/state/api/action-reducers/viz/signed.ts +++ b/src/app/state/api/action-reducers/viz/signed.ts @@ -18,3 +18,7 @@ export const SignedTimeCycle: ApiCallModel = { export const SignedTimeCycleDrilldown: ApiCallModel = { ...APIModel(`${process.env.REACT_APP_API}/signed/time-cycle/drilldown`), }; + +export const SignedTimeCycleDrilldown2: ApiCallModel = { + ...APIModel(`${process.env.REACT_APP_API}/signed/treemap/drilldown`), +}; diff --git a/src/app/state/api/interfaces/index.ts b/src/app/state/api/interfaces/index.ts index ad6d7f362..dcf49b921 100644 --- a/src/app/state/api/interfaces/index.ts +++ b/src/app/state/api/interfaces/index.ts @@ -46,7 +46,6 @@ import { } from "app/state/api/interfaces/cms"; import { DataPathActiveStepStateModel, - DataPathPanelVisibilityStateModel, DataPathStepsStateModel, } from "../action-reducers/sync/dataPath"; @@ -163,14 +162,17 @@ export interface StoreModel { DisbursementsTreemapDrilldown: ApiCallModel; DisbursementsTimeCycle: ApiCallModel; DisbursementsTimeCycleDrilldown: ApiCallModel; + DisbursementsTimeCycleDrilldown2: ApiCallModel; SignedTreemap: ApiCallModel; SignedTreemapDrilldown: ApiCallModel; SignedTimeCycle: ApiCallModel; SignedTimeCycleDrilldown: ApiCallModel; + SignedTimeCycleDrilldown2: ApiCallModel; CommitmentTreemap: ApiCallModel; CommitmentTreemapDrilldown: ApiCallModel; CommitmentTimeCycle: ApiCallModel; CommitmentTimeCycleDrilldown: ApiCallModel; + CommitmentTimeCycleDrilldown2: ApiCallModel; PledgesContributionsGeomap: ApiCallModel; PledgesContributionsTimeCycle: ApiCallModel; PledgesContributionsTimeCycleDrilldown: ApiCallModel; @@ -252,7 +254,6 @@ export interface StoreModel { ToolBoxPanelEligibilityAdvancedCheckboxState: ToolBoxPanelEligibilityAdvancedCheckboxStateModel; ToolBoxPanelBudgetTimeCycleDrilldownYearSelector: ToolBoxPanelBudgetTimeCycleDrilldownYearSelectorModel; // sync data path vars - DataPathPanelVisibilityState: DataPathPanelVisibilityStateModel; DataPathSteps: DataPathStepsStateModel; DataPathActiveStep: DataPathActiveStepStateModel; // CMS diff --git a/src/app/state/store/index.ts b/src/app/state/store/index.ts index 4c298d690..1df7443f3 100644 --- a/src/app/state/store/index.ts +++ b/src/app/state/store/index.ts @@ -46,6 +46,7 @@ import DisbursementsTreemap, { } from "app/state/api/action-reducers/viz/disbursementsTreemap"; import DisbursementsTimeCycle, { DisbursementsTimeCycleDrilldown, + DisbursementsTimeCycleDrilldown2, } from "app/state/api/action-reducers/viz/disbursementsTimeCycle"; import PledgesContributionsGeomap from "app/state/api/action-reducers/viz/pledgesContributionsGeomap"; import PledgesContributionsTimeCycle, { @@ -115,11 +116,13 @@ import PartnerDetailBudgetsTimeCycle, { import SignedTreemap, { SignedTimeCycle, SignedTimeCycleDrilldown, + SignedTimeCycleDrilldown2, SignedTreemapDrilldown, } from "../api/action-reducers/viz/signed"; import CommitmentTreemap, { CommitmentTimeCycle, CommitmentTimeCycleDrilldown, + CommitmentTimeCycleDrilldown2, CommitmentTreemapDrilldown, } from "../api/action-reducers/viz/commitment"; import LocationDetailSignedTreemap from "../api/action-reducers/locationDetail/signedTreemap"; @@ -164,7 +167,6 @@ import countrySummary from "../api/action-reducers/cms/countrySummary"; import notesAndDisclaimers from "../api/action-reducers/cms/notesAndDisclaimers"; import { DataPathActiveStep, - DataPathPanelVisibilityState, DataPathStepsState, } from "../api/action-reducers/sync/dataPath"; import PledgesContributionsTable from "../api/action-reducers/viz/pledgesContributionsTable"; @@ -196,14 +198,17 @@ const storeContent: StoreModel = { DisbursementsTreemapDrilldown: persist(DisbursementsTreemapDrilldown), DisbursementsTimeCycle: persist(DisbursementsTimeCycle), DisbursementsTimeCycleDrilldown: persist(DisbursementsTimeCycleDrilldown), + DisbursementsTimeCycleDrilldown2: persist(DisbursementsTimeCycleDrilldown2), SignedTreemap: persist(SignedTreemap), SignedTreemapDrilldown: persist(SignedTreemapDrilldown), SignedTimeCycle: persist(SignedTimeCycle), SignedTimeCycleDrilldown: persist(SignedTimeCycleDrilldown), + SignedTimeCycleDrilldown2: persist(SignedTimeCycleDrilldown2), CommitmentTreemap: persist(CommitmentTreemap), CommitmentTreemapDrilldown: persist(CommitmentTreemapDrilldown), CommitmentTimeCycle: persist(CommitmentTimeCycle), CommitmentTimeCycleDrilldown: persist(CommitmentTimeCycleDrilldown), + CommitmentTimeCycleDrilldown2: persist(CommitmentTimeCycleDrilldown2), PledgesContributionsGeomap: persist(PledgesContributionsGeomap), PledgesContributionsTimeCycle: persist(PledgesContributionsTimeCycle), PledgesContributionsTimeCycleDrilldown: persist( @@ -327,7 +332,6 @@ const storeContent: StoreModel = { ToolBoxPanelBudgetFlowDrilldownSelectors, ToolBoxPanelBudgetTimeCycleDrilldownYearSelector, // sync data path vars - DataPathPanelVisibilityState, DataPathSteps: persist(DataPathStepsState), DataPathActiveStep: DataPathActiveStep, // CMS API