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

Maps Update: Cards and Modals for Maps Components #1314

Open
wants to merge 53 commits into
base: development
Choose a base branch
from
Open
Changes from 1 commit
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
6f3ee9f
Changing maps page to cards with modals
Jul 18, 2024
17d26a0
Adding messagesfor map modal
Jul 18, 2024
823b161
Adding licence header
Jul 18, 2024
09b333a
Removing unused line
Jul 18, 2024
8ff0ebe
Fix to allow saves in map modal
Nireves333 Jul 24, 2024
dc87b56
Create and utilize new maps selector
hazeltonbw Jul 28, 2024
c799c95
Class Component to Redux function component update
hazeltonbw Jul 28, 2024
0a941da
Adding ": " to map card descriptors for formatting
Jul 24, 2024
af01ec7
Removing unnecessary button
Jul 24, 2024
b55b672
Adding missing : on a map descriptor
Jul 30, 2024
2b3885d
Removing item name from descriptor (so no map)
Jul 30, 2024
a7e6ddf
Limiting map note to 30 characters.
Jul 31, 2024
6397225
Treating circle size as a positive number
Jul 31, 2024
0fb3b45
Changing the order of descriptors on card to match the modal.
Jul 31, 2024
76ddde9
Removing unused variable
Jul 31, 2024
815f998
Changing the name of the button to upload a new map image file.
Jul 31, 2024
b427cbf
Resolving syntax and jsdoc declaration requirements
Jul 31, 2024
b687569
Resolving syntax and jsdoc declaration requirements
Jul 31, 2024
d57da8c
use useAppDispatch for type safety
hazeltonbw Jul 31, 2024
bc49a65
Remove container props, utilize dispatch for redux actions
hazeltonbw Jul 31, 2024
105395d
Use the new component instead of old container
hazeltonbw Jul 31, 2024
0d0a421
Remove props used for container, removed isEdited and isSubmitting st…
hazeltonbw Jul 31, 2024
75c5609
Delete unused types and only import necessary functions from moment p…
hazeltonbw Jul 31, 2024
8430932
Delete unused props & functions, add new memoized selector
hazeltonbw Jul 31, 2024
6db61de
Treating map file name as an uneditable item
Aug 1, 2024
e775c62
Handling displayable with an enum instead of a boolean and fixing a t…
Aug 1, 2024
4c5f66c
Bug squashing
Aug 1, 2024
09e0738
Aligning modal call function names with other components and limiting…
Aug 2, 2024
811dc66
Update code to use simple selector
hazeltonbw Aug 3, 2024
9641220
Put modal state where it belongs
hazeltonbw Aug 3, 2024
30d7bf7
Merge branch 'development' into Maps-Update
juanjoseguva Aug 3, 2024
1e0b61b
Reverting to treating map displayability variable as a boolean instea…
Aug 3, 2024
51a220f
Add coloring to map Display and add TODO for date internationalization
Nireves333 Aug 3, 2024
d270242
Use memoized createAppSelector instead of directly handling state in …
hazeltonbw Aug 4, 2024
18fa275
Fix linting errors
hazeltonbw Aug 4, 2024
ca08606
Leave TODO notes for future updates
hazeltonbw Aug 5, 2024
9c4513d
Merge remote-tracking branch 'origin/development' into pr/juanjoseguv…
huss Aug 5, 2024
8a276b4
fix linting
huss Aug 5, 2024
21de916
Initial RTKQ Maps
ChrisMart21 Aug 5, 2024
45d0db6
Remove Maps from RootState
ChrisMart21 Aug 11, 2024
17c851e
Comments
ChrisMart21 Aug 11, 2024
5c8dbc1
More Maps Changes
ChrisMart21 Aug 13, 2024
b7a3e56
ReEnable Immutable check
ChrisMart21 Aug 13, 2024
ae28cda
Bug Fix Revert Change.
ChrisMart21 Aug 13, 2024
67d578c
Hot Fix Changes.
ChrisMart21 Aug 13, 2024
66ea48d
Fix Imports
ChrisMart21 Aug 13, 2024
095def8
MapChartSelect Crash Fix
ChrisMart21 Aug 13, 2024
8a53505
Login Invalidation updated.
ChrisMart21 Aug 13, 2024
b71527a
Cleanup Legacy Redux where applicable
ChrisMart21 Aug 13, 2024
90db2c6
Merge branch 'development' into Maps-Update
ChrisMart21 Aug 13, 2024
18b8b6e
Fix Merge Issues / Missing MPL headers
ChrisMart21 Aug 13, 2024
a8237ba
Update Root Reducer, Sanitize Devtools
ChrisMart21 Aug 14, 2024
04ba6a5
Address Unserializable Values In State/Actions.
ChrisMart21 Aug 14, 2024
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
Prev Previous commit
Next Next commit
Remove Maps from RootState
- maps no longer a state property,
- old State type deleted, only use RootState now
- admin pages untested, graph seems okay.
- needs testing.
  • Loading branch information
ChrisMart21 committed Aug 11, 2024
commit 45d0db6253b4e1b2e974f1ba1833392927f361cf
17 changes: 4 additions & 13 deletions src/client/app/components/ChartSelectComponent.tsx
Original file line number Diff line number Diff line change
@@ -2,17 +2,13 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

import { sortBy, values } from 'lodash';
import * as React from 'react';
import { useState } from 'react';
import { FormattedMessage } from 'react-intl';
import { useSelector } from 'react-redux';
import { Dropdown, DropdownItem, DropdownMenu, DropdownToggle } from 'reactstrap';
import { useAppDispatch, useAppSelector } from '../redux/reduxHooks';
import { graphSlice, selectChartToRender } from '../redux/slices/graphSlice';
import { SelectOption } from '../types/items';
import { ChartTypes } from '../types/redux/graph';
import { State } from '../types/redux/state';
import translate from '../utils/translate';
import TooltipMarkerComponent from './TooltipMarkerComponent';

@@ -24,10 +20,10 @@ export default function ChartSelectComponent() {
const currentChartToRender = useAppSelector(selectChartToRender);
const dispatch = useAppDispatch();
const [expand, setExpand] = useState(false);
const mapsById = useSelector((state: State) => state.maps.byMapID);
const sortedMaps = sortBy(values(mapsById).map(map => (
{ value: map.id, label: map.name, isDisabled: !(map.origin && map.opposite) } as SelectOption
)), 'label');
// const mapsById = useAppSelector(selectMapDataById);
// const sortedMaps = sortBy(values(mapsById).map(map => (
// { value: map.id, label: map.name, isDisabled: !(map.origin && map.opposite) } as SelectOption
// )), 'label');

return (
<>
@@ -52,11 +48,6 @@ export default function ChartSelectComponent() {
key={chartType}
onClick={() => {
dispatch(graphSlice.actions.changeChartToRender(chartType));
if (chartType === ChartTypes.map && Object.keys(sortedMaps).length === 1) {
// If there is only one map, selectedMap is the id of the only map. ie; display map automatically if only 1 map
dispatch({ type: 'UPDATE_SELECTED_MAPS', mapID: sortedMaps[0].value });

}
}}
>
{translate(`${chartType}`)}
3 changes: 2 additions & 1 deletion src/client/app/components/MapChartComponent.tsx
Original file line number Diff line number Diff line change
@@ -8,6 +8,7 @@ import * as React from 'react';
import {
selectAreaUnit, selectBarWidthDays,
selectGraphAreaNormalization, selectSelectedGroups,
selectSelectedMap,
selectSelectedMeters, selectSelectedUnit
} from '../redux/slices/graphSlice';
import { selectGroupDataById } from '../redux/api/groupsApi';
@@ -56,7 +57,7 @@ export default function MapChartComponent() {

// RTK Types Disagree with maps ts types so, use old until migration completer for maps.
// This is also an issue when trying to refactor maps reducer into slice.
const selectedMap = useAppSelector(state => state.maps.selectedMap);
const selectedMap = useAppSelector(selectSelectedMap);
const map = useAppSelector(state => selectMapById(state, selectedMap));

if (meterIsFetching || groupIsFetching) {
28 changes: 13 additions & 15 deletions src/client/app/components/MapChartSelectComponent.tsx
Original file line number Diff line number Diff line change
@@ -3,11 +3,10 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

import * as React from 'react';
import { sortBy, values } from 'lodash';
import { useDispatch, useSelector } from 'react-redux';
import { State } from '../types/redux/state';
import { SelectOption } from '../types/items';
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
import { selectMapById, selectMapSelectOptions } from '../redux/api/mapsApi';
import { useAppDispatch, useAppSelector } from '../redux/reduxHooks';
import { selectSelectedMap, updateSelectedMaps } from '../redux/slices/graphSlice';
import SingleSelectComponent from './SingleSelectComponent';
import TooltipMarkerComponent from './TooltipMarkerComponent';

@@ -24,19 +23,20 @@ export default function MapChartSelectComponent() {
margin: 0
};
const messages = defineMessages({
selectMap: {id: 'select.map'}
selectMap: { id: 'select.map' }
});

// TODO When this is converted to RTK then should use useAppDispatch().
//Utilizes useDispatch and useSelector hooks
const dispatch = useDispatch();
const sortedMaps = sortBy(values(useSelector((state: State) => state.maps.byMapID)).map(map => (
{ value: map.id, label: map.name, isDisabled: !(map.origin && map.opposite) } as SelectOption
)), 'label');
const dispatch = useAppDispatch();

const sortedMaps = useAppSelector(selectMapSelectOptions);
const selectedMapData = useAppSelector(state => selectMapById(state, selectSelectedMap(state)));


const selectedMap = {
label: useSelector((state: State) => state.maps.byMapID[state.maps.selectedMap] ? state.maps.byMapID[state.maps.selectedMap].name : ''),
value: useSelector((state: State) => state.maps.selectedMap)
label: selectedMapData.name,
value: selectedMapData.id
};

//useIntl instead of injectIntl and WrappedComponentProps
@@ -46,16 +46,14 @@ export default function MapChartSelectComponent() {
<div>
<p style={labelStyle}>
<FormattedMessage id='maps' />:
<TooltipMarkerComponent page='home' helpTextId='help.home.select.maps'/>
<TooltipMarkerComponent page='home' helpTextId='help.home.select.maps' />
</p>
<div style={divBottomPadding}>
<SingleSelectComponent
options={sortedMaps}
selectedOption={(selectedMap.value === 0) ? undefined : selectedMap}
placeholder={intl.formatMessage(messages.selectMap)}
onValueChange={selected => dispatch({type: 'UPDATE_SELECTED_MAPS', mapID: selected.value})}
//When we specify stuff in actions files, we also specify other variables, in this case mapID.
//This is where we specify values instead of triggering the action by itself.
onValueChange={selected => dispatch(updateSelectedMaps(selected.value))}
/>
</div>
</div>
10 changes: 5 additions & 5 deletions src/client/app/components/RouteComponent.tsx
Original file line number Diff line number Diff line change
@@ -5,9 +5,8 @@ import * as React from 'react';
import { IntlProvider } from 'react-intl';
import { RouterProvider, createBrowserRouter } from 'react-router-dom';
import UploadCSVContainer from '../containers/csv/UploadCSVContainer';
import MapCalibrationContainer from '../containers/maps/MapCalibrationContainer';
import MapsDetailComponent from './maps/MapsDetailComponent';
import { useAppSelector } from '../redux/reduxHooks';
import { selectSelectedLanguage } from '../redux/slices/appStateSlice';
import LocaleTranslationData from '../translations/data';
import { UserRole } from '../types/items';
import AppLayout from './AppLayout';
@@ -17,14 +16,15 @@ import AdminComponent from './admin/AdminComponent';
import UsersDetailComponent from './admin/users/UsersDetailComponent';
import ConversionsDetailComponent from './conversion/ConversionsDetailComponent';
import GroupsDetailComponent from './groups/GroupsDetailComponent';
import { MapCalibrationComponent2 } from './maps/MapCalibrationComponent';
import MapsDetailComponent from './maps/MapsDetailComponent';
import MetersDetailComponent from './meters/MetersDetailComponent';
import AdminOutlet from './router/AdminOutlet';
import ErrorComponent from './router/ErrorComponent';
import { GraphLink } from './router/GraphLinkComponent';
import NotFound from './router/NotFoundOutlet';
import RoleOutlet from './router/RoleOutlet';
import UnitsDetailComponent from './unit/UnitsDetailComponent';
import ErrorComponent from './router/ErrorComponent';
import { selectSelectedLanguage } from '../redux/slices/appStateSlice';

/**
* @returns the router component Responsible for client side routing.
@@ -54,7 +54,7 @@ const router = createBrowserRouter([
element: <AdminOutlet />,
children: [
{ path: 'admin', element: <AdminComponent /> },
{ path: 'calibration', element: <MapCalibrationContainer /> },
{ path: 'calibration', element: <MapCalibrationComponent2 /> },
{ path: 'maps', element: <MapsDetailComponent /> },
{ path: 'units', element: <UnitsDetailComponent /> },
{ path: 'conversions', element: <ConversionsDetailComponent /> },
142 changes: 142 additions & 0 deletions src/client/app/components/maps/MapCalibrationChartDisplayComponent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

import { PlotData, PlotMouseEvent } from 'plotly.js';
import * as React from 'react';
import Plot from 'react-plotly.js';
import { useAppDispatch, useAppSelector } from '../../redux/reduxHooks';
import { selectSelectedLanguage } from '../../redux/slices/appStateSlice';
import { localEditsSlice } from '../../redux/slices/localEditsSlice';
import Locales from '../../types/locales';
import { CalibrationSettings } from '../../types/redux/map';
import { Dimensions, normalizeImageDimensions } from '../../utils/calibration';
import { mapsAdapter } from '../../redux/api/mapsApi';

/**
* @returns TODO DO ME
*/
export default function MapCalibrationChartDisplayContainer() {
const dispatch = useAppDispatch();
const x: number[] = [];
const y: number[] = [];
const texts: string[] = [];
const currentLanguange = useAppSelector(selectSelectedLanguage);
const map = useAppSelector(state => mapsAdapter.getSelectors().selectById(state.localEdits.mapEdits, state.localEdits.calibratingMap));

const settings = useAppSelector(state => state.localEdits.calibrationSettings);
const points = map.calibrationSet;
if (points) {
for (const point of points) {
x.push(point.cartesian.x);
y.push(point.cartesian.y);
texts.push(`latitude: ${point.gps.latitude}, longitude: ${point.gps.longitude}`);
}
}
const imageDimensions: Dimensions = normalizeImageDimensions({
width: map.imgWidth,
height: map.imgHeight
});
const backgroundTrace = createBackgroundTrace(imageDimensions, settings);
const dataPointTrace = {
x,
y,
type: 'scatter',
mode: 'markers',
marker: {
color: 'rgb(7,110,180)',
opacity: 0.5,
size: 6
},
text: texts,
opacity: 1,
showlegend: false
};
const data = [backgroundTrace, dataPointTrace];

const imageSource = map.mapSource;

// for a detailed description of layout attributes: https://plotly.com/javascript/reference/#layout
const layout: any = {
width: 1000,
height: 1000,
xaxis: {
visible: false, // changes all visibility settings including showgrid, zeroline, showticklabels and hiding ticks
range: [0, 500] // range of displayed graph
},
yaxis: {
visible: false,
range: [0, 500],
scaleanchor: 'x'
},
images: [{
layer: 'below',
source: imageSource,
xref: 'x',
yref: 'y',
x: 0,
y: 0,
sizex: 500,
sizey: 500,
xanchor: 'left',
yanchor: 'bottom',
sizing: 'contain',
opacity: 1
}]
};

return <Plot
data={data as PlotData[]}
layout={layout}
config={{
// makes locales available for use
locales: Locales,
locale: currentLanguange
}}
onClick={(event: PlotMouseEvent) => {
event.event.preventDefault();
dispatch(localEditsSlice.actions.updateCurrentCartesian(event));
}}
/>;
}

/**
* use a transparent heatmap to capture which point the user clicked on the map
* @param imageDimensions Normalized dimensions of the image
* @param settings Settings for calibration displays
* @returns point and data
*/
function createBackgroundTrace(imageDimensions: Dimensions, settings: CalibrationSettings) {
// define the grid of heatmap
const x: number[] = [];
const y: number[] = [];
// bound the grid to image dimensions to avoid clicking outside of the map
for (let i = 0; i <= Math.ceil(imageDimensions.width); i = i + 1) {
x.push(i);
}
for (let j = 0; j <= Math.ceil(imageDimensions.height); j = j + 1) {
y.push(j);
}
// define the actual points of the graph, numbers in the array are used to designate different colors;
const z: number[][] = [];
for (let ind1 = 0; ind1 < y.length; ++ind1) {
const temp = [];
for (let ind2 = 0; ind2 < x.length; ++ind2) {
temp.push(0);
}
z.push(temp);
}
const trace = {
x,
y,
z,
type: 'heatmap',
colorscale: [['0.5', 'rgba(6,86,157,0)']], // set colors to be fully transparent
xgap: 1,
ygap: 1,
hoverinfo: 'x+y',
opacity: (settings.showGrid) ? '0.5' : '0', // controls whether the grids will be displayed
showscale: false
};
return trace;
}
36 changes: 36 additions & 0 deletions src/client/app/components/maps/MapCalibrationComponent.tsx
Original file line number Diff line number Diff line change
@@ -9,6 +9,13 @@ import MapCalibrationInitiateContainer from '../../containers/maps/MapCalibratio
//import MapsDetailContainer from '../../containers/maps/MapsDetailContainer';
ChrisMart21 marked this conversation as resolved.
Show resolved Hide resolved
import { CalibrationModeTypes } from '../../types/redux/map';
import MapsDetailComponent from './MapsDetailComponent';
import { Navigate } from 'react-router-dom';
import { useAppSelector } from '../../redux/reduxHooks';
import MapCalibrationInfoDisplayComponent from './MapCalibrationInfoDisplayComponent';
import MapCalibrationInitiateComponent from './MapCalibrationInitiateComponent';
import { localEditsSlice } from '../../redux/slices/localEditsSlice';
import { mapsAdapter } from '../../redux/api/mapsApi';
import MapCalibrationChartDisplayComponent from './MapCalibrationChartDisplayComponent';

interface MapCalibrationProps {
mode: CalibrationModeTypes;
@@ -54,3 +61,32 @@ export default class MapCalibrationComponent extends React.Component<MapCalibrat
}
}
}
/**
* @returns Calibration Component corresponding to current step invloved
*/
export const MapCalibrationComponent2 = () => {
const mapToCalibrate = useAppSelector(localEditsSlice.selectors.selectCalibrationMapId);
const calibrationMode = useAppSelector(state => {
const data = mapsAdapter.getSelectors().selectById(state.localEdits.mapEdits, mapToCalibrate);
return data?.calibrationMode ?? CalibrationModeTypes.unavailable;
});
if (calibrationMode === CalibrationModeTypes.initiate) {
return (
<div className='container-fluid'>
{/* <MapCalibrationInitiateContainer /> */}
<MapCalibrationInitiateComponent />
</div >
);
} else if (calibrationMode === CalibrationModeTypes.calibrate) {
return (
<div className='container-fluid'>
<div id={'MapCalibrationContainer'}>
<MapCalibrationChartDisplayComponent />
<MapCalibrationInfoDisplayComponent />
</div>
</div>
);
} else {
return <Navigate to='/maps' replace />;
}
};
Loading