diff --git a/src/client/src/components/Search/Search.js b/src/client/src/components/Search/Search.js
index e952b9097..6656b0c44 100644
--- a/src/client/src/components/Search/Search.js
+++ b/src/client/src/components/Search/Search.js
@@ -35,6 +35,7 @@ const Search = ({
searchTerm,
setSearchTerm,
resetSearch,
+ resetSort,
handlePageChange,
addFilter,
removeFilter,
@@ -94,7 +95,10 @@ const Search = ({
setSearchTerm={setSearchTerm}
resetSearch={resetSearch}
isLoading={isLoading}
- onEnterPress={() => sendRequest(searchTerm, options)}
+ onEnterPress={() => {
+ resetSort();
+ sendRequest(searchTerm, options);
+ }}
/>
@@ -105,6 +109,7 @@ const Search = ({
})}
onClick={(e) => {
e.preventDefault();
+ resetSort();
sendRequest(searchTerm, options);
}}
>
@@ -257,6 +262,7 @@ Search.propTypes = {
removeFilter: PropTypes.func.isRequired,
removeFilters: PropTypes.func.isRequired,
resetSearch: PropTypes.func.isRequired,
+ resetSort: PropTypes.func.isRequired,
results: PropTypes.array,
searchTerm: PropTypes.string.isRequired,
sendRequest: PropTypes.func.isRequired,
diff --git a/src/client/src/components/SearchAwesomeTable/SearchAwesomeTable.js b/src/client/src/components/SearchAwesomeTable/SearchAwesomeTable.js
index 1d632d182..8080527c9 100644
--- a/src/client/src/components/SearchAwesomeTable/SearchAwesomeTable.js
+++ b/src/client/src/components/SearchAwesomeTable/SearchAwesomeTable.js
@@ -1,5 +1,11 @@
import "./awesomeTable.scss";
+import {
+ faSort,
+ faSortDown,
+ faSortUp,
+} from "@fortawesome/free-solid-svg-icons";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import classNames from "classnames";
import PropTypes from "prop-types";
import React from "react";
@@ -9,6 +15,12 @@ import LeftArrow from "../shared/Icons/LeftArrow.jsx";
import RightArrow from "../shared/Icons/RightArrow.jsx";
import LoadSpinner from "../shared/LoadSpinner";
+const getSortIcon = (field, sortField, sortDirection) => {
+ if (field === sortField) {
+ return sortDirection === "asc" ? faSortDown : faSortUp;
+ }
+ return faSort;
+};
const SearchAwesomeTable = ({
showPagination = false,
pagination,
@@ -17,6 +29,10 @@ const SearchAwesomeTable = ({
isLoading = false,
data,
fields,
+ // isSortable = true,
+ sortColumn,
+ sortField,
+ sortDirection,
}) => {
const handleRowClick = (event, element) => {
if (event.target.tagName === "A") {
@@ -29,9 +45,23 @@ const SearchAwesomeTable = ({
{fields.map((field) => {
+ const isSortableField = field.sortKey;
return (
-
+ | isSortableField && sortColumn(field.sortKey)}
+ >
{field.headName}
+ {isSortableField && (
+
+ )}
|
);
})}
diff --git a/src/client/src/components/SearchResults/SearchResults.js b/src/client/src/components/SearchResults/SearchResults.js
index 93365e388..cab6825e5 100644
--- a/src/client/src/components/SearchResults/SearchResults.js
+++ b/src/client/src/components/SearchResults/SearchResults.js
@@ -118,9 +118,7 @@ const SearchResults = ({
});
},
headName: "SIRET",
- importantHead: true,
link: ({ siret }) => `/establishment/${siret}`,
- sortKey: "siret",
},
{
accessor: ({ etatAdministratifEtablissement, siret }) => {
@@ -130,7 +128,7 @@ const SearchResults = ({
});
},
headName: "État",
- sortKey: "etatadministratifetablissement",
+ sortKey: "etatAdministratifEtablissement",
},
{
accessor: (etablissement) => {
@@ -140,7 +138,6 @@ const SearchResults = ({
},
headName: "Raison sociale / Nom",
html: true,
- sortKey: "enterprise_name",
},
{
accessor: (fields) => {
@@ -172,10 +169,8 @@ const SearchResults = ({
},
headName: "Dirigeants",
- importantHead: true,
link: ({ fields }) =>
`/enterprise/${fields?.siren}#mandataires`,
- sortKey: "dirigeants",
},
{
accessor: ({ etablissementSiege }) => {
@@ -186,7 +181,6 @@ const SearchResults = ({
});
},
headName: "Catégorie établissement",
- sortKey: "etablissementsiege",
},
{
accessor: ({
@@ -201,8 +195,8 @@ const SearchResults = ({
});
},
headName: "Code postal",
- sortKey: "codepostaletablissement",
},
+
{
accessor: ({
trancheEffectifsEtablissement,
@@ -222,7 +216,7 @@ const SearchResults = ({
});
},
headName: "Effectif (DSN)",
- sortKey: "lastdsntrancheeffectifsetablissement",
+ sortKey: "trancheEffectifsEtablissement",
},
{
accessor: ({
@@ -239,7 +233,7 @@ const SearchResults = ({
);
},
headName: "Activité",
- sortKey: "activiteprincipaleetablissement",
+ sortKey: "codeActivitePrincipale",
},
]}
/>
diff --git a/src/client/src/containers/Search/Search.js b/src/client/src/containers/Search/Search.js
index 9d5f1438f..2b17de616 100644
--- a/src/client/src/containers/Search/Search.js
+++ b/src/client/src/containers/Search/Search.js
@@ -2,11 +2,13 @@ import { groupBy, omit } from "lodash";
import { prop } from "lodash/fp";
import moment from "moment";
import React, { useEffect } from "react";
+import { useSelector } from "react-redux";
import SearchView from "../../components/Search";
import Http from "../../services/Http";
import {
useResetSearch,
+ useResetSort,
useSearchFilters,
useSearchPage,
useSearchQuery,
@@ -23,7 +25,7 @@ const PAGE_SIZE = 10;
const XLSX_DOC_TYPE =
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
-const formatLocationFilter = (filters) => {
+const formatLocationFilter = (filters, sortDirection, sortField) => {
const locationFilters = groupBy(filters.location, "type");
const codesCommunes = locationFilters?.commune?.map(prop("value")) || [];
const departements = [
@@ -32,17 +34,26 @@ const formatLocationFilter = (filters) => {
regionItem.regions.map((region) => region.value)
) || []),
];
-
return {
...omit(filters, "location"),
codesCommunes: normalizeCodeCommunes(codesCommunes),
departements,
+
+ sortField: sortField,
+ sortOrder: sortDirection,
};
};
const Search = () => {
const [searchQuery, setSearchQuery] = useSearchTerms();
const [searchPage, setSearchPage] = useSearchPage();
+ const sortFieldFromStore = useSelector(
+ (state) => state?.search?.sort?.sortField
+ );
+
+ const sortOrderFromStore = useSelector(
+ (state) => state?.search?.sort?.sortOrder
+ );
const { filters, addFilter, removeFilter, removeFilters } =
useSearchFilters();
@@ -50,13 +61,13 @@ const Search = () => {
const { data, loading, error, makeQuery, query } = useSearchQuery();
const resetSearch = useResetSearch();
-
+ const resetSort = useResetSort();
useEffect(() => {
if (searchQuery) {
onSearch();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
- }, []);
+ }, [sortField, sortDirection]);
const downloadQuery = async () => {
const trimmedQuery = searchQuery?.trim();
@@ -64,7 +75,11 @@ const Search = () => {
const response = await Http.get("/downloadXlsx", {
params: {
q: trimmedQuery,
- ...formatLocationFilter(filters),
+ ...formatLocationFilter(
+ filters,
+ sortOrderFromStore != null ? sortDirection : null,
+ sortFieldFromStore != null ? sortField : null
+ ),
},
responseType: "blob",
});
@@ -89,7 +104,11 @@ const Search = () => {
setSearchPage(nextCurrentPage);
makeQuery(searchQuery, {
page: { current: nextCurrentPage - 1, size: PAGE_SIZE },
- params: formatLocationFilter(filters),
+ params: formatLocationFilter(
+ filters,
+ sortOrderFromStore != null ? sortDirection : null,
+ sortFieldFromStore != null ? sortField : null
+ ),
});
};
@@ -97,7 +116,11 @@ const Search = () => {
setSearchPage(1);
makeQuery(searchQuery, {
page: { current: 0, size: PAGE_SIZE },
- params: formatLocationFilter(filters),
+ params: formatLocationFilter(
+ filters,
+ sortOrderFromStore != null ? sortDirection : null,
+ sortFieldFromStore != null ? sortField : null
+ ),
});
};
@@ -114,6 +137,7 @@ const Search = () => {
searchTerm={searchQuery || ""}
setSearchTerm={setSearchQuery}
resetSearch={resetSearch}
+ resetSort={resetSort}
handlePageChange={handlePageChange}
addFilter={addFilter}
removeFilter={removeFilter}
diff --git a/src/client/src/services/Store/actions/search.js b/src/client/src/services/Store/actions/search.js
index fc9f5e190..37c3b0739 100644
--- a/src/client/src/services/Store/actions/search.js
+++ b/src/client/src/services/Store/actions/search.js
@@ -6,6 +6,18 @@ export const setSearchTerm = (term) => (dispatch) => {
type: types.SET_SEARCH_TERM,
});
};
+export const setSearchSortOrder = (sortOrder) => (dispatch) => {
+ dispatch({
+ sortOrder,
+ type: types.SET_SEARCH_SORT_ORDER,
+ });
+};
+export const setSearchSortField = (sortField) => (dispatch) => {
+ dispatch({
+ sortField,
+ type: types.SET_SEARCH_SORT_FIELD,
+ });
+};
export const setSearchPage = (page) => (dispatch) => {
dispatch({
@@ -33,3 +45,9 @@ export const resetSearch = () => (dispatch) => {
type: types.RESET_SEARCH,
});
};
+
+export const resetSort = () => (dispatch) => {
+ dispatch({
+ type: types.RESET_SORT,
+ });
+};
diff --git a/src/client/src/services/Store/constants/ActionTypes.js b/src/client/src/services/Store/constants/ActionTypes.js
index 2a8fd2fd4..1709f2c8b 100644
--- a/src/client/src/services/Store/constants/ActionTypes.js
+++ b/src/client/src/services/Store/constants/ActionTypes.js
@@ -1,6 +1,9 @@
export const SET_SEARCH_TERM = "SET_SEARCH_TERM";
+export const SET_SEARCH_SORT_ORDER = "SET_SEARCH_SORT_ORDER";
+export const SET_SEARCH_SORT_FIELD = "SET_SEARCH_SORT_FIELD";
export const SET_SEARCH_PAGE = "SET_SEARCH_PAGE";
export const SET_SEARCH_FILTERS = "SET_SEARCH_FILTERS";
export const SET_SEARCH_SORT = "SET_SEARCH_SORT";
export const SET_SEARCH_RESULTS = "SET_SEARCH_RESULTS";
export const RESET_SEARCH = "RESET_SEARCH";
+export const RESET_SORT = "RESET_SORT";
diff --git a/src/client/src/services/Store/hooks/search.js b/src/client/src/services/Store/hooks/search.js
index 5464c8679..9a363f8a3 100644
--- a/src/client/src/services/Store/hooks/search.js
+++ b/src/client/src/services/Store/hooks/search.js
@@ -5,9 +5,12 @@ import { useDispatch, useSelector } from "react-redux";
import { useElasticQuery } from "../../Elastic/elastic";
import {
resetSearch,
+ resetSort,
setSearchFilters,
setSearchPage as setSearchPageAction,
setSearchResults,
+ setSearchSortField as setSearchSortFieldAction,
+ setSearchSortOrder as setSearchSortOrderAction,
setSearchTerm as setSearchTermAction,
} from "../actions";
@@ -23,6 +26,17 @@ export const useSearchTerms = () => {
return [searchTerm, setSearchTerm];
};
+export const useSearchSortTerms = () => {
+ const dispatch = useDispatch();
+ const setSearchSortOrder = (term) => {
+ dispatch(setSearchSortOrderAction(term));
+ };
+ const setSearchSortField = (term) => {
+ dispatch(setSearchSortFieldAction(term));
+ };
+
+ return [setSearchSortOrder, setSearchSortField];
+};
export const useSearchPage = () => {
const dispatch = useDispatch();
@@ -39,12 +53,12 @@ export const useSearchFilters = () => {
const dispatch = useDispatch();
const savedFilters = useSelector((state) => getSearchState(state).filters);
// cache busting mechanism
+
const validFilters = savedFilters.etats
? savedFilters
: {
etats: ["A"],
};
-
const addFilter = (key, value) => {
dispatch(setSearchFilters({ ...savedFilters, [key]: value }));
};
@@ -104,3 +118,8 @@ export const useResetSearch = () => {
return () => dispatch(resetSearch());
};
+export const useResetSort = () => {
+ const dispatch = useDispatch();
+
+ return () => dispatch(resetSort());
+};
diff --git a/src/client/src/services/Store/reducers/search.js b/src/client/src/services/Store/reducers/search.js
index b93a13852..10d97204b 100644
--- a/src/client/src/services/Store/reducers/search.js
+++ b/src/client/src/services/Store/reducers/search.js
@@ -1,15 +1,20 @@
import {
RESET_SEARCH,
+ RESET_SORT,
SET_SEARCH_FILTERS,
SET_SEARCH_PAGE,
SET_SEARCH_RESULTS,
SET_SEARCH_SORT,
+ SET_SEARCH_SORT_FIELD,
+ SET_SEARCH_SORT_ORDER,
SET_SEARCH_TERM,
} from "../constants/ActionTypes";
const initialState = {
filters: {
etats: ["A"],
+ sortField: null,
+ sortOrder: null,
},
page: 1,
results: {
@@ -19,6 +24,8 @@ const initialState = {
sort: {
ascDirection: false,
field: null,
+ sortField: null,
+ sortOrder: null,
},
term: null,
};
@@ -30,6 +37,31 @@ const search = (state = initialState, action) => {
...state,
term: action.term,
};
+ case SET_SEARCH_SORT_ORDER:
+ return {
+ ...state,
+ sort: {
+ ...state.sort,
+ sortOrder: action.sortOrder,
+ },
+ };
+ case SET_SEARCH_SORT_FIELD:
+ return {
+ ...state,
+ sort: {
+ ...state.sort,
+ sortField: action.sortField,
+ },
+ };
+ case RESET_SORT:
+ return {
+ ...state,
+ sort: {
+ ...state.sort,
+ sortField: null,
+ sortOrder: null,
+ },
+ };
case SET_SEARCH_PAGE:
return {
diff --git a/src/client/src/utils/search-table/hooks.js b/src/client/src/utils/search-table/hooks.js
index 668b5e883..c8c202365 100644
--- a/src/client/src/utils/search-table/hooks.js
+++ b/src/client/src/utils/search-table/hooks.js
@@ -1,5 +1,8 @@
import { omit } from "lodash";
-import { useState } from "react";
+import { useEffect, useState } from "react";
+import { useSelector } from "react-redux";
+
+import { useSearchSortTerms } from "../../services/Store/hooks/search";
export const useFilters = (defaultValue) => {
const [filters, setFilters] = useState(defaultValue);
@@ -20,22 +23,49 @@ export const useFilters = (defaultValue) => {
};
export const useSort = () => {
- const [sortDirection, setSortDirection] = useState("desc");
- const [sortField, setSortField] = useState(null);
+ const [setSearchSortOrder, setSearchSortField] = useSearchSortTerms();
+ const sortFieldFromStore = useSelector(
+ (state) => state?.search?.sort?.sortField
+ );
+ const sortOrderFromStore = useSelector(
+ (state) => state?.search?.sort?.sortOrder
+ );
+ const [sortDirection, setSortDirection] = useState(sortOrderFromStore);
+ const [sortField, setSortField] = useState(sortFieldFromStore);
+ const { addFilter } = useFilters();
+
+ useEffect(() => {
+ if (sortFieldFromStore == null && sortOrderFromStore == null) {
+ setSortField(sortFieldFromStore);
+ setSortDirection(sortOrderFromStore);
+ }
+ }, [sortFieldFromStore, sortOrderFromStore]);
const toggleSortField = (field) => {
if (field !== sortField) {
- setSortDirection("desc");
+ // Set a new sort field and default direction to "desc"
setSortField(field);
- return;
- }
-
- if (sortDirection === "desc") {
- setSortDirection("asc");
- return;
+ setSearchSortField(field);
+ setSortDirection("desc");
+ setSearchSortOrder("desc");
+ addFilter("sortField", field);
+ addFilter("sortDirection", "desc");
+ } else {
+ // Toggling sort direction if field is the same
+ if (sortDirection === "desc") {
+ setSortDirection("asc");
+ setSearchSortOrder("asc");
+ addFilter("sortDirection", "asc");
+ } else if (sortDirection === "asc") {
+ // Reset on the third click
+ setSortField(null);
+ setSortDirection(null);
+ setSearchSortField(null);
+ setSearchSortOrder(null);
+ addFilter("sortField", null);
+ addFilter("sortDirection", null);
+ }
}
-
- setSortField(null);
};
return {
diff --git a/src/server/src/utils/elastic.js b/src/server/src/utils/elastic.js
index 15a00b15c..07cac3e06 100644
--- a/src/server/src/utils/elastic.js
+++ b/src/server/src/utils/elastic.js
@@ -1,5 +1,4 @@
import config from "config";
-
import { Client } from "@elastic/elasticsearch";
import codesNaf from "@socialgouv/codes-naf";
@@ -17,7 +16,7 @@ const filtersFieldMap = {
departement: "departement",
codesPostaux: "codesPostalEtablissement",
tranchesEffectifs: "trancheEffectifsEtablissement",
- naf:"codeActivitePrincipale"
+ naf: "codeActivitePrincipale",
};
const getCodeNafLibelle = (code) =>
@@ -45,27 +44,14 @@ const makeQuery = ({ query, siege, dirigeant, ...filters }) => {
query: {
bool: {
must: [
- {
- match: {
- "dirigeants.nom": {
- query: dirigeant.nom,
- },
- },
- },
- {
- match: {
- "dirigeants.prenom": {
- query: dirigeant.prenom,
- },
- },
- },
+ { match: { "dirigeants.nom": dirigeant.nom } },
+ { match: { "dirigeants.prenom": dirigeant.prenom } },
],
},
},
},
});
- }
- if (dirigeant.nom && !dirigeant.prenom) {
+ } else if (dirigeant.nom) {
dirigeantConditions.push({
nested: {
path: "dirigeants",
@@ -74,8 +60,7 @@ const makeQuery = ({ query, siege, dirigeant, ...filters }) => {
},
},
});
- }
- if (dirigeant.prenom && !dirigeant.nom) {
+ } else if (dirigeant.prenom) {
dirigeantConditions.push({
nested: {
path: "dirigeants",
@@ -86,6 +71,7 @@ const makeQuery = ({ query, siege, dirigeant, ...filters }) => {
});
}
}
+
return {
...(query ? { min_score: 20 } : {}),
query: {
@@ -110,7 +96,6 @@ const makeQuery = ({ query, siege, dirigeant, ...filters }) => {
]
: []
),
-
...(query
? [
{
@@ -129,7 +114,6 @@ const makeQuery = ({ query, siege, dirigeant, ...filters }) => {
boost: 100,
},
},
-
{
multi_match: {
query,
@@ -141,7 +125,6 @@ const makeQuery = ({ query, siege, dirigeant, ...filters }) => {
minimum_should_match: "100%",
},
},
-
...(siretOrSirenQuery
? [
{
@@ -213,7 +196,7 @@ const formatElasticResult = (hit) => {
export const getElasticQueryParams = (req) => {
const query = (req.query["q"] || "").trim();
const activites = req.query["activites"] || [];
- const naf=req.query["naf"] || [];
+ const naf = req.query["naf"] || [];
const codesCommunes = req.query["codesCommunes"] || [];
const codesPostaux = req.query["codesPostaux"] || [];
const departement = req.query["departements"] || [];
@@ -221,6 +204,8 @@ export const getElasticQueryParams = (req) => {
const dirigeant = req.query["dirigeant"]
? JSON.parse(req.query["dirigeant"])
: null;
+ const sortField = req.query["sortField"];
+ const sortOrder = req.query["sortOrder"];
let etats = req.query["etats"] || [];
const siege = (req.query["siege"] || "").trim();
@@ -239,13 +224,26 @@ export const getElasticQueryParams = (req) => {
codesPostaux,
tranchesEffectifs,
dirigeant,
- naf
+ naf,
+ sortField,
+ sortOrder,
};
};
export const requestElastic = async (params, { from, size }) => {
- console.log(params);
const body = makeQuery(params);
+
+ // Ajouter le tri après avoir construit la requête de filtrage
+ if (params.sortField && params.sortOrder) {
+ body.sort = [
+ {
+ [params.sortField]: {
+ order: params.sortOrder,
+ },
+ },
+ ];
+ }
+
const {
body: {
hits: { total, hits },