Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Waitp 1224 overlay selector desktop #4685

Merged
merged 7 commits into from
Jun 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/tupaia-web/src/api/queries/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,5 @@ export { useEntities, useEntitiesWithLocation } from './useEntities';
export { useEmailVerification } from './useEmailVerification';
export { useDashboards } from './useDashboards';
export { useReport } from './useReport';
export { useMapOverlays } from './useMapOverlays';
export { useMapOverlayData } from './useMapOverlayData';
31 changes: 31 additions & 0 deletions packages/tupaia-web/src/api/queries/useMapOverlayData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Tupaia
* Copyright (c) 2017 - 2023 Beyond Essential Systems Pty Ltd
*/

import { useQuery } from 'react-query';
import { get } from '../api';
import { EntityCode, ProjectCode, SingleMapOverlayItem } from '../../types';

export const useMapOverlayData = (
projectCode?: ProjectCode,
entityCode?: EntityCode,
mapOverlayCode?: SingleMapOverlayItem['code'],
) => {
return useQuery(
['mapOverlayData', projectCode, entityCode, mapOverlayCode],
async () => {
return get('measureData', {
params: {
mapOverlayCode,
organisationUnitCode: entityCode,
projectCode,
shouldShowAllParentCountryResults: projectCode !== entityCode, // TODO: figure out the logic here for shouldShowAllParentCountryResults
},
});
},
{
enabled: !!projectCode && !!entityCode && !!mapOverlayCode,
},
);
};
77 changes: 77 additions & 0 deletions packages/tupaia-web/src/api/queries/useMapOverlays.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* Tupaia
* Copyright (c) 2017 - 2023 Beyond Essential Systems Pty Ltd
*/
import { ChangeEvent } from 'react';
import { useParams, useSearchParams } from 'react-router-dom';
import { useQuery, UseQueryResult } from 'react-query';
import { get } from '../api';
import { MapOverlayGroup, SingleMapOverlayItem } from '../../types';
import { URL_SEARCH_PARAMS } from '../../constants';

const mapOverlayByCode = (
mapOverlayGroups: MapOverlayGroup[] = [],
): Record<SingleMapOverlayItem['code'], SingleMapOverlayItem> => {
return mapOverlayGroups.reduce(
(
result: Record<string, SingleMapOverlayItem>,
mapOverlay: MapOverlayGroup | SingleMapOverlayItem,
) => {
if (mapOverlay.children) {
return { ...result, ...mapOverlayByCode(mapOverlay.children) };
}
return {
...result,
[mapOverlay.code]: mapOverlay,
};
},
{},
);
};

interface UseMapOverlaysResult {
hasMapOverlays: boolean;
mapOverlayGroups: MapOverlayGroup[];
isLoadingMapOverlays: boolean;
errorLoadingMapOverlays: UseQueryResult['error'];
selectedOverlayCode: string | null;
selectedOverlay?: SingleMapOverlayItem;
updateSelectedMapOverlay: (e: ChangeEvent<HTMLInputElement>) => void;
}

/**
* Gets the map overlays and returns useful utils and values associated with these
*/
export const useMapOverlays = (): UseMapOverlaysResult => {
const [urlSearchParams, setUrlParams] = useSearchParams();
const { projectCode, entityCode } = useParams();
const { data, isLoading, error } = useQuery(
['mapOverlays', projectCode, entityCode],
async () => {
return get(`mapOverlays/${projectCode}/${entityCode}`);
},
{
enabled: !!projectCode && !!entityCode,
},
);

const selectedOverlayCode = urlSearchParams.get(URL_SEARCH_PARAMS.MAP_OVERLAY);
const codedOverlays = mapOverlayByCode(data?.mapOverlays);

const selectedOverlay = codedOverlays[selectedOverlayCode!];

const updateSelectedMapOverlay = (e: ChangeEvent<HTMLInputElement>) => {
urlSearchParams.set(URL_SEARCH_PARAMS.MAP_OVERLAY, e.target.value);
setUrlParams(urlSearchParams);
};

return {
hasMapOverlays: !!data?.mapOverlays?.length,
mapOverlayGroups: data?.mapOverlays,
isLoadingMapOverlays: isLoading,
errorLoadingMapOverlays: error,
selectedOverlayCode,
selectedOverlay,
updateSelectedMapOverlay,
};
};
1 change: 1 addition & 0 deletions packages/tupaia-web/src/constants/url.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export const URL_SEARCH_PARAMS = {
PROJECT: 'project',
TAB: 'tab',
PASSWORD_RESET_TOKEN: 'passwordResetToken',
MAP_OVERLAY: 'overlay',
};

export enum MODAL_ROUTES {
Expand Down
36 changes: 27 additions & 9 deletions packages/tupaia-web/src/features/Map/Map.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,15 +57,25 @@ const StyledMap = styled(LeafletMap)`
`;
// Position this absolutely so it can be placed over the map
const TilePickerWrapper = styled.div`
position: absolute;
right: 0;
bottom: 0;
height: 100%;
@media screen and (max-width: ${MOBILE_BREAKPOINT}) {
display: none;
}
`;

// This contains the map controls (legend, overlay selector, etc, so that they can fit within the map appropriately)
const MapControlWrapper = styled.div`
width: 100%;
height: 100%;
display: flex;
position: relative;
`;

const MapControlColumn = styled.div`
display: flex;
flex-direction: column;
flex: 1;
`;

export const Map = () => {
const { projectCode, entityCode } = useParams();
const [activeTileSet, setActiveTileSet] = useState(TILE_SETS[0]);
Expand All @@ -82,13 +92,21 @@ export const Map = () => {
<TileLayer tileSetUrl={activeTileSet.url} showAttribution={false} />
<MapOverlays />
<ZoomControl position="bottomright" />
<MapLegend />
<MapControlWrapper>
<MapControlColumn>
<MapOverlaySelector />
<MapLegend />
</MapControlColumn>
<TilePickerWrapper>
<TilePicker
tileSets={TILE_SETS}
activeTileSet={activeTileSet}
onChange={onTileSetChange}
/>
</TilePickerWrapper>
</MapControlWrapper>
<MapWatermark />
</StyledMap>
<MapOverlaySelector />
<TilePickerWrapper>
<TilePicker tileSets={TILE_SETS} activeTileSet={activeTileSet} onChange={onTileSetChange} />
</TilePickerWrapper>
</MapContainer>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,24 @@ const Wrapper = styled.div`
flex-direction: row;
align-items: flex-end;
justify-content: center;
position: absolute;
background-color: grey;
width: 300px;
height: 50px;
bottom: 1em;
left: 50%;
transform: translateX(-50%);
border-radius: 5px;
width: 100%;
padding: 1rem;
@media screen and (max-width: ${MOBILE_BREAKPOINT}) {
display: none;
}
`;

const Legend = styled.div`
height: 50px;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probably should be rems, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should, but this is just a placeholder so I don't think it's important at this stage

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh true. I just see px and I jump 😆

width: 300px;
background-color: grey;
border-radius: 5px;
`;

export const DesktopMapLegend = () => {
return <Wrapper />;
return (
<Wrapper>
<Legend />
</Wrapper>
);
};
Loading