From 884ae7ee39d29d27d7479c1465d6978e69c068dd Mon Sep 17 00:00:00 2001 From: rbisson <remi.bisson@inrae.fr> Date: Tue, 17 Dec 2024 16:03:27 +0100 Subject: [PATCH 1/5] [ResultsTableMUI] Corrected bug causing rows to deselect on tab change --- src/pages/results/ResultsTableMUI.js | 139 ++++++++++++--------------- 1 file changed, 61 insertions(+), 78 deletions(-) diff --git a/src/pages/results/ResultsTableMUI.js b/src/pages/results/ResultsTableMUI.js index e3e8887..a918a9d 100644 --- a/src/pages/results/ResultsTableMUI.js +++ b/src/pages/results/ResultsTableMUI.js @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useMemo, useState } from 'react'; import { Trans, useTranslation } from 'react-i18next'; import MUIDataTable from 'mui-datatables'; import { createTheme, ThemeProvider } from '@mui/material'; @@ -30,113 +30,94 @@ const ResultsTableMUI = ({ setResourceFlyoutData, }) => { const { t } = useTranslation('results'); - const [selectedRows, setSelectedRows] = useState([]); const [publicFields, setPublicFields] = useState([]); - const [rows, setRows] = useState([]); - const [columns, setColumns] = useState([]); + const [userFieldsIds, setUserFieldsIds] = useState([]); const [isLoading, setIsLoading] = useState(true); const [rowsPerPage, setRowsPerPage] = useState(15); - // On page load, check table rows from selected resources from map - useEffect(() => { - setSelectedRows(selectedRowsIds.map((id) => getRowIdFromResourceData(id))); - }, [selectedRowsIds]); - - // On search results change, fetch all public fields + // Fetch public fields and user display settings useEffect(() => { - fetchPublicFields().then((publicFieldsResults) => { + const fetchData = async () => { + setIsLoading(true); + const publicFieldsResults = await fetchPublicFields(); setPublicFields(publicFieldsResults); - }); - }, [searchResults]); - - // Build table data (columns and rows) - useEffect(() => { - if (!searchResults && searchResults.length > 0) { - return; - } - getStdFieldsIds().then((stdFieldsIds) => { - const columns = buildColumns(stdFieldsIds); - setColumns(columns); - setRows(buildRows(searchResults, columns)); - setIsLoading(false); - }); - }, [publicFields]); - - // Get fields ids settings from user - const getStdFieldsIds = async () => { - const stdFieldsIds = await fetchUserFieldsDisplaySettings( - sessionStorage.getItem('userId') - ); - if (!stdFieldsIds) { - const defaultFieldsIds = [1]; + const userStdFieldsIds = await fetchUserFieldsDisplaySettings( + sessionStorage.getItem('userId') + ); // TODO replace hard-coded array by gatekeeper fetch on default settings - return defaultFieldsIds; - } - return stdFieldsIds; - }; - - // Returns value from JSON obj associated to key string. - const getValueByPath = (obj, path) => { - return path.split('.').reduce((acc, key) => acc && acc[key], obj); - }; + // If no userStdFields, use system default ones. + setUserFieldsIds(userStdFieldsIds || [1]); + }; + fetchData(); + }, [searchResults]); - // Build each row in table from search results data - const buildRows = (results, columns) => { - let dataRows = []; - if (results.length === 0) { - return dataRows; + // Memoize columns + const columns = useMemo(() => { + if (publicFields.length === 0) { + return []; } - results.forEach((result) => { - let row = { - id: result.id, - }; - columns.forEach((columnField) => { - const fieldValue = getValueByPath(result, columnField.name); - if (typeof fieldValue === 'string') { - row[columnField.name] = fieldValue; - } else if (typeof fieldValue === 'number') { - row[columnField.name] = fieldValue.toString(); - } - }); - dataRows.push(row); - }); - return dataRows; - }; - - // Build table columns names (label and name) - const buildColumns = (stdFieldsIds) => { - let dataColumns = []; - dataColumns.push({ - name: 'id', - label: 'ID', - options: { - display: 'excluded', + let dataColumns = [ + { + name: 'id', + label: 'ID', + options: { display: 'excluded' }, }, - }); + ]; publicFields.forEach((publicField) => { dataColumns.push({ name: publicField.field_name, label: buildFieldName(publicField.field_name), - options: { - display: stdFieldsIds.includes(publicField.id), - }, + options: { display: userFieldsIds.includes(publicField.id) }, }); }); return dataColumns; + }, [publicFields, userFieldsIds]); + + // Returns value from JSON obj associated to key string. + const getValueByPath = (obj, path) => { + return path.split('.').reduce((acc, key) => acc && acc[key], obj); }; + const rows = useMemo(() => { + const buildRows = (results, columns) => { + if (results.length === 0) { + return []; + } + return results.map((result) => { + let row = { id: result.id }; + columns.forEach((column) => { + const value = getValueByPath(result, column.name); + row[column.name] = typeof value === 'string' ? value : value?.toString(); + }); + return row; + }); + }; + const rows = buildRows(searchResults, columns); + setIsLoading(false); + return searchResults && columns.length > 0 ? rows : []; + }, [searchResults, columns]); + const getResourceDataFromRowId = (id) => { return searchResults.find((resource) => resource.id === id); }; const getRowIdFromResourceData = (id) => { + if (!rows || rows.length === 0) { + return -1; + } for (let index = 0; index < rows.length; index++) { if (rows[index].id === id) { return index; } } + return -1; }; + // On page load, check table rows from selected resources from map + const selectedRows = useMemo(() => { + return selectedRowsIds.map((id) => getRowIdFromResourceData(id)); + }, [rows, selectedRowsIds]); + // Add row to list of selected on checkbox click const onRowSelectionCallback = (selectedRow, allSelectedRows) => { setSelectedRowsIds( @@ -218,9 +199,11 @@ const ResultsTableMUI = ({ textLabels, }; + const isTableReady = !isLoading && columns.length > 0 && rows.length > 0; + return ( <ThemeProvider theme={getMuiTheme()}> - {!isLoading && ( + {isTableReady && ( <MUIDataTable title={<Trans i18nKey={'results:table.title'} components={{ searchQuery }} />} data={rows} -- GitLab From 8489a403d67f603bf4f778d507cbba00f569f793 Mon Sep 17 00:00:00 2001 From: rbisson <remi.bisson@inrae.fr> Date: Tue, 17 Dec 2024 16:10:26 +0100 Subject: [PATCH 2/5] [Profile] Removed unused imports --- src/pages/profile/Profile.js | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/pages/profile/Profile.js b/src/pages/profile/Profile.js index 313212f..81c0d26 100644 --- a/src/pages/profile/Profile.js +++ b/src/pages/profile/Profile.js @@ -1,13 +1,6 @@ import React, { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { - EuiPanel, - EuiTabbedContent, - EuiFlexGroup, - EuiCallOut, - EuiSpacer, - EuiFlexItem, -} from '@elastic/eui'; +import { EuiPanel, EuiTabbedContent, EuiFlexGroup, EuiCallOut } from '@elastic/eui'; import UserFieldsDisplaySettings from './UserFieldsDisplaySettings'; import GroupSettings from './GroupSettings'; import RoleSettings from './RoleSettings'; -- GitLab From 2b832f36ba9aeb09d0814fd133cc0d4be89710e6 Mon Sep 17 00:00:00 2001 From: rbisson <remi.bisson@inrae.fr> Date: Wed, 18 Dec 2024 16:20:34 +0100 Subject: [PATCH 3/5] [SearchMap] Added actions to selected resources list ; improved styling --- public/locales/en/maps.json | 8 +- public/locales/fr/maps.json | 8 +- src/pages/maps/SearchMap.js | 339 ++++++++++++++++++++++++------------ src/pages/maps/styles.js | 26 +-- 4 files changed, 244 insertions(+), 137 deletions(-) diff --git a/public/locales/en/maps.json b/public/locales/en/maps.json index 555595d..c71540d 100644 --- a/public/locales/en/maps.json +++ b/public/locales/en/maps.json @@ -22,7 +22,11 @@ } }, "selectedPointsList": { - "title": "Selected resources list", - "empty": "Select resources to display them here." + "title": "Selected resources", + "empty": "Select resources to display them here.", + "actions": { + "openResourceFlyout": "Open resource flyout", + "unselectResource": "Unselect resource" + } } } diff --git a/public/locales/fr/maps.json b/public/locales/fr/maps.json index fdbcd9b..51f7f8d 100644 --- a/public/locales/fr/maps.json +++ b/public/locales/fr/maps.json @@ -22,7 +22,11 @@ } }, "selectedPointsList": { - "title": "Liste des ressources sélectionnées", - "empty": "Sélectionnez des ressources pour les afficher ici" + "title": "Ressources sélectionnées", + "empty": "Sélectionnez des ressources pour les afficher ici", + "actions": { + "openResourceFlyout": "Ouvrir la fiche de la resource", + "unselectResource": "Désélectionner la resource" + } } } diff --git a/src/pages/maps/SearchMap.js b/src/pages/maps/SearchMap.js index 6d47330..310e72e 100644 --- a/src/pages/maps/SearchMap.js +++ b/src/pages/maps/SearchMap.js @@ -28,8 +28,12 @@ import { EuiComboBox, EuiProgress, EuiSpacer, - EuiText, EuiTitle, + EuiPanel, + EuiFlexGroup, + EuiFlexItem, + EuiButtonIcon, + EuiToolTip, htmlIdGenerator, } from '@elastic/eui'; import { updateArrayElement } from '../../Utils.js'; @@ -390,6 +394,10 @@ const SearchMap = ({ searchResults, selectedPointsIds, setSelectedPointsIds }) = }); pointFeature.set('id', pointData.id); pointFeature.set('nom', pointData.name); + // If point is a 'selected point', link its id to it. + if (pointData.linkedPointId) { + pointFeature.set('linkedPointId', pointData.linkedPointId); + } pointFeature.set('isSelected', isSelected); pointFeature.setStyle(pointStyle); return pointFeature; @@ -448,6 +456,7 @@ const SearchMap = ({ searchResults, selectedPointsIds, setSelectedPointsIds }) = { id: 0, name: pointName, + linkedPointId: pointId, }, coords, 5000, @@ -463,16 +472,13 @@ const SearchMap = ({ searchResults, selectedPointsIds, setSelectedPointsIds }) = } else { const selectedPointsFeatures = selectedPointsSource.getFeatures(); if (selectedPointsFeatures.length > 0) { - for (let point = 0; point < selectedPointsFeatures.length; point++) { - const pointGeom = selectedPointsFeatures[point].getGeometry(); + for (let i = 0; i < selectedPointsFeatures.length; i++) { + const point = selectedPointsFeatures[i]; + const pointGeom = point.getGeometry(); const coords = pointGeom.getCenter(); if (polygonGeometry.intersectsCoordinate(coords)) { - // Remove previously selected features - selectedPointsSource.removeFeature(selectedPointsFeatures[point]); - newSelectedPointsIds = newSelectedPointsIds.splice( - newSelectedPointsIds.indexOf(selectedPointsFeatures[point].get('id')), - 1 - ); + const pointId = point.get('linkedPointId'); + unselectPoint(pointId, selectedPointsSource); } } } @@ -529,6 +535,7 @@ const SearchMap = ({ searchResults, selectedPointsIds, setSelectedPointsIds }) = { id: 0, name: pointName, + linkedPointId: id, }, proj.fromLonLat([geoPoint.longitude, geoPoint.latitude]), 5000, @@ -607,34 +614,127 @@ const SearchMap = ({ searchResults, selectedPointsIds, setSelectedPointsIds }) = setMapLayers(updatedLayers); }; - // Display selected points names - const SelectedPointsList = () => { + const getSelectedPoints = () => { const selectedPointsLayer = map .getLayers() .item(getLayerIndex('selectedPointsSource')); - let selectedPointsList = <></>; - let selectedPoints = 0; - if (selectedPointsLayer) { - selectedPoints = getSelectedPointsNames(selectedPointsLayer.getSource()); - if (selectedPoints.length === 0) { - selectedPointsList = <p>{t('maps:selectedPointsList.empty')}</p>; - } else { - selectedPointsList = selectedPoints.map((pointName, index) => { - return <p key={index}>{pointName}</p>; - }); - } + const selectedPointsSource = selectedPointsLayer.getSource(); + if (!selectedPointsLayer || !selectedPointsSource) { + return []; + } + const features = selectedPointsSource.getFeatures(); + if (!features) { + return []; + } + const points = []; + features.forEach((feature) => { + points.push(feature.getProperties()); + }); + return points; + }; + + // Unselect a single point. Remove it from map display and from selected points ids list. + const unselectPoint = (id, source) => { + let newSelectedPointsIds = selectedPointsIds; + if (!source) { + source = map.getLayers().item(getLayerIndex('selectedPointsSource')).getSource(); + } + const selectedPointsFeatures = source.getFeatures(); + if (!selectedPointsFeatures || selectedPointsFeatures.length === 0) { + return; + } + const featureToRemove = selectedPointsFeatures.find( + (feature) => feature.get('linkedPointId') === id + ); + if (!featureToRemove) { + return; + } + source.removeFeature(featureToRemove); + const indexOfPointToRemove = newSelectedPointsIds.indexOf(id); + if (indexOfPointToRemove >= 0) { + newSelectedPointsIds = newSelectedPointsIds.toSpliced(indexOfPointToRemove, 1); + } + // Remove previously selected features + setSelectedPointsIds(newSelectedPointsIds); + }; + + // Display selected points names + const SelectedPointsList = () => { + const SelectedPointItem = ({ id, name }) => { + const onUncheckButtonClick = () => { + unselectPoint(id); + }; + + const onOpenFlyoutClick = () => { + // TODO call a function to open resource flyout, filled with correct data + }; + + return ( + <EuiPanel paddingSize="s" hasShadow={false} hasBorder={true}> + <EuiFlexGroup alignItems={'center'}> + <EuiFlexGroup> + <p>{name}</p> + </EuiFlexGroup> + <EuiToolTip + position="top" + content={<p>{t('maps:selectedPointsList.actions.openResourceFlyout')}</p>} + > + <EuiButtonIcon + iconType="eye" + aria-label={t('maps:selectedPointsList.actions.openResourceFlyout')} + onClick={() => onOpenFlyoutClick()} + color={'primary'} + display={'base'} + size={'s'} + /> + </EuiToolTip> + <EuiToolTip + position="top" + content={<p>{t('maps:selectedPointsList.actions.unselectResource')}</p>} + > + <EuiButtonIcon + iconType="cross" + aria-label={t('maps:selectedPointsList.actions.unselectResource')} + onClick={() => onUncheckButtonClick()} + color={'danger'} + size={'s'} + /> + </EuiToolTip> + <EuiSpacer /> + </EuiFlexGroup> + </EuiPanel> + ); + }; + + const buildSelectedPointList = () => { + return selectedPoints.map((point, index) => { + return ( + <SelectedPointItem key={index} name={point.nom} id={point.linkedPointId} /> + ); + }); + }; + + let selectedPointsList; + let selectedPoints = getSelectedPoints(); + if (selectedPoints.length === 0) { + selectedPointsList = <p>{t('maps:selectedPointsList.empty')}</p>; + } else { + selectedPointsList = buildSelectedPointList(); } return ( - <div style={styles.selectedPointsListContainer}> - <EuiTitle size="s"> - <h3> - {t('maps:selectedPointsList.title')} ({selectedPoints.length}) - </h3> - </EuiTitle> - <EuiSpacer size="m" /> - <EuiText style={styles.selectedPointsList}>{selectedPointsList}</EuiText> - </div> + <EuiPanel hasShadow={false} hasBorder={true}> + <EuiFlexGroup direction={'column'}> + <EuiTitle size="s"> + <p> + {t('maps:selectedPointsList.title')} ({selectedPoints.length}) + </p> + </EuiTitle> + <EuiFlexGroup direction={'column'} style={styles.selectedPointsList}> + {selectedPointsList} + </EuiFlexGroup> + </EuiFlexGroup> + </EuiPanel> ); }; @@ -657,90 +757,101 @@ const SearchMap = ({ searchResults, selectedPointsIds, setSelectedPointsIds }) = setSelectedPointsIds([]); }; + const MapTools = () => { + return ( + <EuiPanel paddingSize="l" hasShadow={false} hasBorder={true}> + <EuiFlexGroup> + <table style={styles.layersTable}> + <thead> + <tr> + <th>{t('maps:layersTableHeaders.cartography')}</th> + <th>{t('maps:layersTableHeaders.filters')}</th> + <th>{t('maps:layersTableHeaders.tools')}</th> + </tr> + </thead> + <tbody> + <tr> + <td style={styles.layersTableCells}> + <EuiCheckbox + id={htmlIdGenerator()()} + label={t('maps:layersTable.openStreetMap')} + checked={mapLayersVisibility[getLayerIndex('osm-layer')]} + onChange={(e) => setLayerDisplay('osm-layer', e.target.checked)} + /> + <EuiCheckbox + id={htmlIdGenerator()()} + label={t('maps:layersTable.bingAerial')} + checked={mapLayersVisibility[getLayerIndex('Bing Aerial')]} + onChange={(e) => setLayerDisplay('Bing Aerial', e.target.checked)} + /> + <EuiCheckbox + id={htmlIdGenerator()()} + label={t('maps:layersTable.IGN')} + checked={mapLayersVisibility[getLayerIndex('IGN')]} + onChange={(e) => setLayerDisplay('IGN', e.target.checked)} + /> + </td> + <td style={styles.layersTableCells}> + <EuiCheckbox + id={htmlIdGenerator()()} + label={t('maps:layersTable.queryResults')} + checked={mapLayersVisibility[getLayerIndex('queryResults')]} + onChange={(e) => setLayerDisplay('queryResults', e.target.checked)} + /> + <br /> + <EuiComboBox + aria-label={t('maps:layersTable.selectFilterOption')} + placeholder={t('maps:layersTable.selectFilterOption')} + singleSelection={{ asPlainText: true }} + options={filterOptions} + selectedOptions={selectedFilterOptions} + onChange={onFilterSelectChange} + styles={styles.filtersSelect} + /> + </td> + <td style={styles.layersTableCells}> + <EuiTitle size="xxs"> + <h6>{t('maps:layersTable.selectionTool.title')}</h6> + </EuiTitle> + <EuiButtonGroup + legend={t('maps:layersTable.selectionTool.title')} + options={selectionToolOptions} + onChange={toggleSelectionToolMode} + idSelected={ + selectionToolMode + ? 'selectionToolButton__1' + : 'selectionToolButton__0' + } + color={'primary'} + isFullWidth + /> + <EuiSpacer size="s" /> + <EuiButton + onClick={() => unselectPoints()} + style={styles.unselectAllButton} + > + {t('maps:layersTable.selectionTool.unselectAll')} + </EuiButton> + </td> + </tr> + </tbody> + </table> + </EuiFlexGroup> + </EuiPanel> + ); + }; + return ( - <> - <div style={styles.container}> - <div id="map" style={styles.mapContainer}></div> - <SelectedPointsList /> - </div> - <div style={styles.loadingContainer}> - {isLoading && <EuiProgress size="l" color="accent" />} - </div> - <EuiSpacer size={'m'} /> - <table style={styles.layersTable}> - <thead> - <tr> - <th>{t('maps:layersTableHeaders.cartography')}</th> - <th>{t('maps:layersTableHeaders.filters')}</th> - <th>{t('maps:layersTableHeaders.tools')}</th> - </tr> - </thead> - <tbody> - <tr> - <td style={styles.layersTableCells}> - <EuiCheckbox - id={htmlIdGenerator()()} - label={t('maps:layersTable.openStreetMap')} - checked={mapLayersVisibility[getLayerIndex('osm-layer')]} - onChange={(e) => setLayerDisplay('osm-layer', e.target.checked)} - /> - <EuiCheckbox - id={htmlIdGenerator()()} - label={t('maps:layersTable.bingAerial')} - checked={mapLayersVisibility[getLayerIndex('Bing Aerial')]} - onChange={(e) => setLayerDisplay('Bing Aerial', e.target.checked)} - /> - <EuiCheckbox - id={htmlIdGenerator()()} - label={t('maps:layersTable.IGN')} - checked={mapLayersVisibility[getLayerIndex('IGN')]} - onChange={(e) => setLayerDisplay('IGN', e.target.checked)} - /> - </td> - <td style={styles.layersTableCells}> - <EuiCheckbox - id={htmlIdGenerator()()} - label={t('maps:layersTable.queryResults')} - checked={mapLayersVisibility[getLayerIndex('queryResults')]} - onChange={(e) => setLayerDisplay('queryResults', e.target.checked)} - /> - <br /> - <EuiComboBox - aria-label={t('maps:layersTable.selectFilterOption')} - placeholder={t('maps:layersTable.selectFilterOption')} - singleSelection={{ asPlainText: true }} - options={filterOptions} - selectedOptions={selectedFilterOptions} - onChange={onFilterSelectChange} - styles={styles.filtersSelect} - /> - </td> - <td style={styles.layersTableCells}> - <EuiTitle size="xxs"> - <h6>{t('maps:layersTable.selectionTool.title')}</h6> - </EuiTitle> - <EuiButtonGroup - legend={t('maps:layersTable.selectionTool.title')} - options={selectionToolOptions} - onChange={toggleSelectionToolMode} - idSelected={ - selectionToolMode ? 'selectionToolButton__1' : 'selectionToolButton__0' - } - color={'primary'} - isFullWidth - /> - <EuiSpacer size="s" /> - <EuiButton - onClick={() => unselectPoints()} - style={styles.unselectAllButton} - > - {t('maps:layersTable.selectionTool.unselectAll')} - </EuiButton> - </td> - </tr> - </tbody> - </table> - </> + <EuiFlexGroup> + <EuiFlexGroup direction={'column'} style={styles.container}> + <EuiFlexItem> + <div id="map" style={styles.mapContainer}></div> + {isLoading && <EuiProgress size="l" color="accent" />} + </EuiFlexItem> + <MapTools /> + </EuiFlexGroup> + <SelectedPointsList /> + </EuiFlexGroup> ); }; diff --git a/src/pages/maps/styles.js b/src/pages/maps/styles.js index f1a31a2..edf2829 100644 --- a/src/pages/maps/styles.js +++ b/src/pages/maps/styles.js @@ -21,32 +21,20 @@ const styles = { }), }), container: { - width: '100%', - display: 'flex', - justifyContent: 'start', - minHeight: '60vh', - maxHeight: '60vh', + minWidth: '70vw', + maxWidth: '70vw', }, mapContainer: { - minWidth: '60vw', - minHeight: '100%', - }, - loadingContainer: { - maxWidth: '60vw', - }, - selectedPointsListContainer: { - display: 'flex', - flexDirection: 'column', - width: '100%', - textAlign: 'center', - padding: '10px', + minWidth: '70vw', + minHeight: '58vh', + maxHeight: '58vh', }, selectedPointsList: { overflow: 'auto', - maxHeight: '100%', + maxHeight: '72vh', }, layersTable: { - width: '60vw', + width: '70vw', cursor: 'pointer', marginTop: '10px', }, -- GitLab From ee4a25ae3af8fea19eefc5bdbb94d704c9cd0c54 Mon Sep 17 00:00:00 2001 From: rbisson <remi.bisson@inrae.fr> Date: Wed, 18 Dec 2024 16:31:33 +0100 Subject: [PATCH 4/5] [Search] now emptying selected points array after a new search --- src/pages/search/Search.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/pages/search/Search.js b/src/pages/search/Search.js index 61d1be4..c892f8d 100644 --- a/src/pages/search/Search.js +++ b/src/pages/search/Search.js @@ -59,6 +59,11 @@ const Search = () => { }); }, []); + // On new search, reset selected rows + useEffect(() => { + setSelectedResultsRowsIds([]); + }, [searchResults]); + const tabsContent = [ { id: 'tab1', -- GitLab From bf13f9c639029738fe1aaf368d88e8f44c89be0d Mon Sep 17 00:00:00 2001 From: rbisson <remi.bisson@inrae.fr> Date: Wed, 18 Dec 2024 16:50:08 +0100 Subject: [PATCH 5/5] [Search] now flyout is in parent component and can be opened from id [SearchMap] eye button opens flyout now --- src/pages/maps/SearchMap.js | 11 ++++++-- src/pages/results/Results.js | 22 +++++++-------- src/pages/results/ResultsTableMUI.js | 10 ++----- src/pages/search/Search.js | 41 +++++++++++++++++++++++----- 4 files changed, 55 insertions(+), 29 deletions(-) diff --git a/src/pages/maps/SearchMap.js b/src/pages/maps/SearchMap.js index 310e72e..7a3a913 100644 --- a/src/pages/maps/SearchMap.js +++ b/src/pages/maps/SearchMap.js @@ -92,7 +92,13 @@ const pointBaseStyle = new Style({ }), }); -const SearchMap = ({ searchResults, selectedPointsIds, setSelectedPointsIds }) => { +const SearchMap = ({ + searchResults, + selectedPointsIds, + setSelectedPointsIds, + setResourceFlyoutDataFromId, + setIsResourceFlyoutOpen, +}) => { const { t } = useTranslation('maps'); // ref for handling zoomHelperText display const timerRef = useRef(null); @@ -666,7 +672,8 @@ const SearchMap = ({ searchResults, selectedPointsIds, setSelectedPointsIds }) = }; const onOpenFlyoutClick = () => { - // TODO call a function to open resource flyout, filled with correct data + setResourceFlyoutDataFromId(id); + setIsResourceFlyoutOpen(true); }; return ( diff --git a/src/pages/results/Results.js b/src/pages/results/Results.js index 6d274a1..5ebb6f4 100644 --- a/src/pages/results/Results.js +++ b/src/pages/results/Results.js @@ -1,23 +1,21 @@ -import React, { useState } from 'react'; +import React from 'react'; import { EuiCallOut, EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; import { useTranslation } from 'react-i18next'; import ResultsTableMUI from './ResultsTableMUI'; -import ResourceFlyout from './ResourceFlyout/ResourceFlyout'; import ResultsDownload from './ResultsDownload'; -const Results = ({ searchResults, searchQuery, selectedRowsIds, setSelectedRowsIds }) => { +const Results = ({ + searchResults, + searchQuery, + selectedRowsIds, + setSelectedRowsIds, + setResourceFlyoutDataFromId, + setIsResourceFlyoutOpen, +}) => { const { t } = useTranslation('results'); - const [isResourceFlyoutOpen, setIsResourceFlyoutOpen] = useState(false); - const [resourceFlyoutData, setResourceFlyoutData] = useState({}); return ( <> - <ResourceFlyout - resourceFlyoutData={resourceFlyoutData} - setResourceFlyoutData={setResourceFlyoutData} - isResourceFlyoutOpen={isResourceFlyoutOpen} - setIsResourceFlyoutOpen={setIsResourceFlyoutOpen} - /> <EuiFlexGroup> <EuiFlexItem> <EuiCallOut size="s" title={t('results:clickOnRowTip')} iconType="search" /> @@ -36,7 +34,7 @@ const Results = ({ searchResults, searchQuery, selectedRowsIds, setSelectedRowsI searchQuery={searchQuery} selectedRowsIds={selectedRowsIds} setSelectedRowsIds={setSelectedRowsIds} - setResourceFlyoutData={setResourceFlyoutData} + setResourceFlyoutDataFromId={setResourceFlyoutDataFromId} setIsResourceFlyoutOpen={setIsResourceFlyoutOpen} /> </> diff --git a/src/pages/results/ResultsTableMUI.js b/src/pages/results/ResultsTableMUI.js index a918a9d..f3eb2c7 100644 --- a/src/pages/results/ResultsTableMUI.js +++ b/src/pages/results/ResultsTableMUI.js @@ -27,7 +27,7 @@ const ResultsTableMUI = ({ selectedRowsIds, setSelectedRowsIds, setIsResourceFlyoutOpen, - setResourceFlyoutData, + setResourceFlyoutDataFromId, }) => { const { t } = useTranslation('results'); const [publicFields, setPublicFields] = useState([]); @@ -97,10 +97,6 @@ const ResultsTableMUI = ({ return searchResults && columns.length > 0 ? rows : []; }, [searchResults, columns]); - const getResourceDataFromRowId = (id) => { - return searchResults.find((resource) => resource.id === id); - }; - const getRowIdFromResourceData = (id) => { if (!rows || rows.length === 0) { return -1; @@ -132,9 +128,7 @@ const ResultsTableMUI = ({ if (!searchResults || searchResults.length === 0) { return; } - const resourceData = getResourceDataFromRowId(rows[cellState.dataIndex].id); - // Extract all values except for id to avoid displaying it to user - setResourceFlyoutData((({ id, ...rest }) => rest)(resourceData)); + setResourceFlyoutDataFromId(rows[cellState.dataIndex].id); setIsResourceFlyoutOpen(true); }; diff --git a/src/pages/search/Search.js b/src/pages/search/Search.js index c892f8d..6f31278 100644 --- a/src/pages/search/Search.js +++ b/src/pages/search/Search.js @@ -11,6 +11,7 @@ import { import { useTranslation } from 'react-i18next'; import AdvancedSearch from './AdvancedSearch/AdvancedSearch'; import BasicSearch from './BasicSearch/BasicSearch'; +import ResourceFlyout from '../results/ResourceFlyout/ResourceFlyout'; const Search = () => { const { t } = useTranslation('search'); @@ -24,6 +25,8 @@ const Search = () => { const [standardFields, setStandardFields] = useState([]); const [searchResults, setSearchResults] = useState([]); const [selectedResultsRowsIds, setSelectedResultsRowsIds] = useState([]); + const [isResourceFlyoutOpen, setIsResourceFlyoutOpen] = useState(false); + const [resourceFlyoutData, setResourceFlyoutData] = useState({}); useEffect(() => { fetchPublicFields().then((resultStdFields) => { @@ -64,6 +67,18 @@ const Search = () => { setSelectedResultsRowsIds([]); }, [searchResults]); + // Returns all data from a resource from its id. Used to display resource data in flyout + const getResourceDataFromRowId = (id) => { + return searchResults.find((resource) => resource.id === id); + }; + + // Add data to resource flyout, so it can be displayed. + const setResourceFlyoutDataFromId = (id) => { + const resourceData = getResourceDataFromRowId(id); + // Extract all values except for id to avoid displaying it to user + setResourceFlyoutData((({ id, ...rest }) => rest)(resourceData)); + }; + const tabsContent = [ { id: 'tab1', @@ -117,6 +132,8 @@ const Search = () => { searchQuery={isAdvancedSearch ? search : basicSearch} selectedRowsIds={selectedResultsRowsIds} setSelectedRowsIds={setSelectedResultsRowsIds} + setResourceFlyoutDataFromId={setResourceFlyoutDataFromId} + setIsResourceFlyoutOpen={setIsResourceFlyoutOpen} /> </EuiFlexItem> </EuiFlexGroup> @@ -133,6 +150,8 @@ const Search = () => { searchResults={searchResults} selectedPointsIds={selectedResultsRowsIds} setSelectedPointsIds={setSelectedResultsRowsIds} + setResourceFlyoutDataFromId={setResourceFlyoutDataFromId} + setIsResourceFlyoutOpen={setIsResourceFlyoutOpen} /> </EuiFlexItem> </EuiFlexGroup> @@ -141,13 +160,21 @@ const Search = () => { ]; return ( - <EuiTabbedContent - tabs={tabsContent} - selectedTab={tabsContent[selectedTabNumber]} - onTabClick={(tab) => { - setSelectedTabNumber(tabsContent.indexOf(tab)); - }} - /> + <> + <ResourceFlyout + resourceFlyoutData={resourceFlyoutData} + setResourceFlyoutData={setResourceFlyoutData} + isResourceFlyoutOpen={isResourceFlyoutOpen} + setIsResourceFlyoutOpen={setIsResourceFlyoutOpen} + /> + <EuiTabbedContent + tabs={tabsContent} + selectedTab={tabsContent[selectedTabNumber]} + onTabClick={(tab) => { + setSelectedTabNumber(tabsContent.indexOf(tab)); + }} + /> + </> ); }; -- GitLab