From af3c7cb4c4e5a80e5a2e79280a3a61ac66ce7605 Mon Sep 17 00:00:00 2001
From: rbisson <remi.bisson@inrae.fr>
Date: Thu, 25 Apr 2024 16:22:12 +0200
Subject: [PATCH 01/17] added i18n packages and basic configuration, created
 assets folder in src

---
 package.json                                | 12 ++++++----
 src/{ => assets}/Map.svg                    |  0
 src/{ => assets}/favicon.svg                |  0
 src/assets/i18n/locales/en/translation.json |  7 ++++++
 src/assets/i18n/locales/fr/translation.json |  7 ++++++
 src/{ => assets}/logo.svg                   |  0
 src/components/Header/Header.js             |  2 +-
 src/i18n.js                                 | 26 +++++++++++++++++++++
 src/index.js                                |  1 +
 src/pages/home/Home.js                      |  4 ++++
 10 files changed, 53 insertions(+), 6 deletions(-)
 rename src/{ => assets}/Map.svg (100%)
 rename src/{ => assets}/favicon.svg (100%)
 create mode 100644 src/assets/i18n/locales/en/translation.json
 create mode 100644 src/assets/i18n/locales/fr/translation.json
 rename src/{ => assets}/logo.svg (100%)
 create mode 100644 src/i18n.js

diff --git a/package.json b/package.json
index c706491..536d40d 100644
--- a/package.json
+++ b/package.json
@@ -21,7 +21,9 @@
     "react-hanger": "^2.2.1",
     "react-html-parser": "^2.0.2",
     "react-router-dom": "^5.2.0",
-    "react-scripts": "^3.3.0"
+    "react-scripts": "^3.3.0",
+    "i18next": "^23.11.2",
+    "react-i18next": "^14.1.1"
   },
   "scripts": {
     "start": "react-scripts start",
@@ -45,12 +47,12 @@
     ]
   },
   "devDependencies": {
-    "jscs": "^3.0.7",
+    "eslint-config-prettier": "^9.1.0",
+    "eslint-plugin-prettier": "^5.1.3",
     "husky": "8.0.3",
+    "jscs": "^3.0.7",
     "lint-staged": "14.0.1",
-    "prettier": "^3.2.5",
-    "eslint-config-prettier": "^9.1.0",
-    "eslint-plugin-prettier": "^5.1.3"
+    "prettier": "^3.2.5"
   },
   "husky": {
     "hooks": {
diff --git a/src/Map.svg b/src/assets/Map.svg
similarity index 100%
rename from src/Map.svg
rename to src/assets/Map.svg
diff --git a/src/favicon.svg b/src/assets/favicon.svg
similarity index 100%
rename from src/favicon.svg
rename to src/assets/favicon.svg
diff --git a/src/assets/i18n/locales/en/translation.json b/src/assets/i18n/locales/en/translation.json
new file mode 100644
index 0000000..bded201
--- /dev/null
+++ b/src/assets/i18n/locales/en/translation.json
@@ -0,0 +1,7 @@
+{
+  "title": "Welcome to react using react-i18next",
+  "description": {
+    "part1": "To get started, edit <1>src/App.js</1> and save to reload.",
+    "part2": "Switch language between english and german using buttons above."
+  }
+}
diff --git a/src/assets/i18n/locales/fr/translation.json b/src/assets/i18n/locales/fr/translation.json
new file mode 100644
index 0000000..784b45d
--- /dev/null
+++ b/src/assets/i18n/locales/fr/translation.json
@@ -0,0 +1,7 @@
+{
+  "title": "Bienvenue react-i18next",
+  "description": {
+    "part1": "To get started, edit <1>src/App.js</1> and save to reload.",
+    "part2": "Switch language between english and german using buttons above."
+  }
+}
diff --git a/src/logo.svg b/src/assets/logo.svg
similarity index 100%
rename from src/logo.svg
rename to src/assets/logo.svg
diff --git a/src/components/Header/Header.js b/src/components/Header/Header.js
index f1e74d0..cb58973 100644
--- a/src/components/Header/Header.js
+++ b/src/components/Header/Header.js
@@ -9,7 +9,7 @@ import {
 } from '@elastic/eui';
 import HeaderUserMenu from './header_user_menu';
 import style from './styles';
-import logoInSylva from '../../favicon.svg';
+import logoInSylva from '../../assets/favicon.svg';
 
 const structure = [
   {
diff --git a/src/i18n.js b/src/i18n.js
new file mode 100644
index 0000000..9353fa6
--- /dev/null
+++ b/src/i18n.js
@@ -0,0 +1,26 @@
+import i18n from 'i18next';
+import { initReactI18next } from 'react-i18next';
+import en from './assets/i18n/locales/en/translation.json';
+import fr from './assets/i18n/locales/fr/translation.json';
+
+const resources = {
+  en: {
+    translation: en,
+  },
+  fr: {
+    translation: fr,
+  },
+};
+
+i18n.use(initReactI18next).init({
+  lng: 'fr',
+  fallbackLng: 'fr',
+  resources,
+  debug: true,
+  interpolation: {
+    // not needed for react as it escapes by default
+    escapeValue: false,
+  },
+});
+
+export default i18n;
diff --git a/src/index.js b/src/index.js
index ab31dbf..68ea85b 100644
--- a/src/index.js
+++ b/src/index.js
@@ -4,6 +4,7 @@ import '@elastic/eui/dist/eui_theme_light.css';
 import { UserProvider, checkUserLogin } from './context/UserContext';
 import App from './App';
 import { getLoginUrl, getUrlParam, redirect } from './Utils.js';
+import './i18n';
 
 const userId = getUrlParam('kcId', '');
 const accessToken = getUrlParam('accessToken', '');
diff --git a/src/pages/home/Home.js b/src/pages/home/Home.js
index c54f908..fe80cd6 100644
--- a/src/pages/home/Home.js
+++ b/src/pages/home/Home.js
@@ -6,8 +6,11 @@ import {
   EuiPageContentBody,
   EuiTitle,
 } from '@elastic/eui';
+import { useTranslation } from 'react-i18next';
 
 const Home = () => {
+  const { t } = useTranslation();
+
   return (
     <>
       <EuiPageContent>
@@ -16,6 +19,7 @@ const Home = () => {
             <EuiTitle>
               <h2>Welcome to the IN-SYLVA IS application search module</h2>
             </EuiTitle>
+            <p>{t('title')}</p>
             <br />
             <br />
             <p>
-- 
GitLab


From ffdd89de611a0cfd38f859c04c9cefb2d3ad5e19 Mon Sep 17 00:00:00 2001
From: rbisson <remi.bisson@inrae.fr>
Date: Thu, 25 Apr 2024 16:28:17 +0200
Subject: [PATCH 02/17] corrected index.html

---
 public/index.html | 22 ++--------------------
 1 file changed, 2 insertions(+), 20 deletions(-)

diff --git a/public/index.html b/public/index.html
index 38223fe..5e6c564 100644
--- a/public/index.html
+++ b/public/index.html
@@ -1,17 +1,11 @@
 <!DOCTYPE html>
 <html lang="en">
-
 <head>
   <meta charset="utf-8" />
-  <!-- <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" /> -->
+  <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.png" />
   <meta name="viewport" content="width=device-width, initial-scale=1" />
   <meta name="theme-color" content="#000000" />
-  <!--
-      manifest.json provides metadata used when your web app is installed on a
-      user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-    -->
   <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
-
   <!--
       Notice the use of %PUBLIC_URL% in the tags above.
       It will be replaced with the URL of the `public` folder during the build.
@@ -21,23 +15,11 @@
       work correctly both with client-side routing and a non-root public URL.
       Learn how to configure a non-root public URL by running `npm run build`.
     -->
-  <title>IN-SYLVA SEARCH</title>
+  <title>IN-SYLVA Search</title>
   <script src="%PUBLIC_URL%/env-config.js"></script>
 </head>
-
 <body>
   <noscript>You need to enable JavaScript to run this app.</noscript>
   <div id="root"></div>
-  <!--
-      This HTML file is a template.
-      If you open it directly in the browser, you will see an empty page.
-
-      You can add webfonts, meta tags, or analytics to this file.
-      The build step will place the bundled scripts into the <body> tag.
-
-      To begin the development, run `npm start` or `yarn start`.
-      To create a production bundle, use `npm run build` or `yarn build`.
-    -->
 </body>
-
 </html>
-- 
GitLab


From e41f56b0d1dcbb04df1f2dc507d395b37d801043 Mon Sep 17 00:00:00 2001
From: rbisson <remi.bisson@inrae.fr>
Date: Fri, 26 Apr 2024 16:55:24 +0200
Subject: [PATCH 03/17] multi translation files setup with lazy loading

---
 package.json                                |   7 +-
 public/locales/en/common.json               |   1 +
 public/locales/en/header.json               |   4 +
 public/locales/en/home.json                 |   3 +
 public/locales/en/maps.json                 |   7 +
 public/locales/en/profile.json              |   3 +
 public/locales/en/results.json              |   5 +
 public/locales/en/search.json               |   3 +
 public/locales/en/validation.json           |   1 +
 public/locales/fr/common.json               |   1 +
 public/locales/fr/header.json               |   4 +
 public/locales/fr/home.json                 |   7 +
 public/locales/fr/maps.json                 |   7 +
 public/locales/fr/profile.json              |   3 +
 public/locales/fr/results.json              |   5 +
 public/locales/fr/search.json               |   3 +
 public/locales/fr/validation.json           |   1 +
 src/assets/i18n/locales/en/translation.json |   7 -
 src/assets/i18n/locales/fr/translation.json |   7 -
 src/components/Header/Header.js             |   9 +-
 src/components/Loading/Loading.js           |  12 +
 src/components/Loading/package.json         |   6 +
 src/components/Loading/styles.js            |  11 +
 src/i18n.js                                 |  37 +-
 src/index.js                                |   7 +-
 src/pages/home/Home.js                      |   8 +-
 src/pages/maps/SearchMap.js                 | 406 ++++++--------------
 src/pages/maps/styles.js                    | 117 ++++++
 src/pages/profile/Profile.js                |  61 ++-
 src/pages/results/Results.js                | 113 +++---
 src/pages/search/Search.js                  |   8 +-
 31 files changed, 429 insertions(+), 445 deletions(-)
 create mode 100644 public/locales/en/common.json
 create mode 100644 public/locales/en/header.json
 create mode 100644 public/locales/en/home.json
 create mode 100644 public/locales/en/maps.json
 create mode 100644 public/locales/en/profile.json
 create mode 100644 public/locales/en/results.json
 create mode 100644 public/locales/en/search.json
 create mode 100644 public/locales/en/validation.json
 create mode 100644 public/locales/fr/common.json
 create mode 100644 public/locales/fr/header.json
 create mode 100644 public/locales/fr/home.json
 create mode 100644 public/locales/fr/maps.json
 create mode 100644 public/locales/fr/profile.json
 create mode 100644 public/locales/fr/results.json
 create mode 100644 public/locales/fr/search.json
 create mode 100644 public/locales/fr/validation.json
 delete mode 100644 src/assets/i18n/locales/en/translation.json
 delete mode 100644 src/assets/i18n/locales/fr/translation.json
 create mode 100644 src/components/Loading/Loading.js
 create mode 100644 src/components/Loading/package.json
 create mode 100644 src/components/Loading/styles.js
 create mode 100644 src/pages/maps/styles.js

diff --git a/package.json b/package.json
index 536d40d..4faa70a 100644
--- a/package.json
+++ b/package.json
@@ -13,6 +13,8 @@
     "@material-ui/lab": "^4.0.0-alpha.48",
     "@material-ui/styles": "^4.10.0",
     "downloadjs": "^1.4.7",
+    "i18next": "^23.11.2",
+    "i18next-http-backend": "^2.5.1",
     "moment": "^2.27.0",
     "mui-datatables": "^3.4.0",
     "ol": "^6.3.2-dev.1594217558556",
@@ -20,10 +22,9 @@
     "react-dom": "^16.13.1",
     "react-hanger": "^2.2.1",
     "react-html-parser": "^2.0.2",
+    "react-i18next": "^14.1.1",
     "react-router-dom": "^5.2.0",
-    "react-scripts": "^3.3.0",
-    "i18next": "^23.11.2",
-    "react-i18next": "^14.1.1"
+    "react-scripts": "^3.3.0"
   },
   "scripts": {
     "start": "react-scripts start",
diff --git a/public/locales/en/common.json b/public/locales/en/common.json
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/public/locales/en/common.json
@@ -0,0 +1 @@
+{}
diff --git a/public/locales/en/header.json b/public/locales/en/header.json
new file mode 100644
index 0000000..9342625
--- /dev/null
+++ b/public/locales/en/header.json
@@ -0,0 +1,4 @@
+{
+  "home": "Home",
+  "search": "Search"
+}
diff --git a/public/locales/en/home.json b/public/locales/en/home.json
new file mode 100644
index 0000000..0ae6026
--- /dev/null
+++ b/public/locales/en/home.json
@@ -0,0 +1,3 @@
+{
+  "pageTitle": "Welcome on In-Sylva search module's homepage."
+}
diff --git a/public/locales/en/maps.json b/public/locales/en/maps.json
new file mode 100644
index 0000000..2e100cb
--- /dev/null
+++ b/public/locales/en/maps.json
@@ -0,0 +1,7 @@
+{
+  "layersChoiceTitle": "Click on layers to toggle display.",
+  "layersTableHeaders": {
+    "cartography": "Cartography",
+    "data": "Data"
+  }
+}
diff --git a/public/locales/en/profile.json b/public/locales/en/profile.json
new file mode 100644
index 0000000..7c610a5
--- /dev/null
+++ b/public/locales/en/profile.json
@@ -0,0 +1,3 @@
+{
+  "pageTitle": "Profile management"
+}
diff --git a/public/locales/en/results.json b/public/locales/en/results.json
new file mode 100644
index 0000000..742539e
--- /dev/null
+++ b/public/locales/en/results.json
@@ -0,0 +1,5 @@
+{
+  "downloadResultsButton": {
+    "JSON": "Download as JSON"
+  }
+}
diff --git a/public/locales/en/search.json b/public/locales/en/search.json
new file mode 100644
index 0000000..26c286e
--- /dev/null
+++ b/public/locales/en/search.json
@@ -0,0 +1,3 @@
+{
+  "pageTitle": "In-Sylva Metadata Search Platform"
+}
diff --git a/public/locales/en/validation.json b/public/locales/en/validation.json
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/public/locales/en/validation.json
@@ -0,0 +1 @@
+{}
diff --git a/public/locales/fr/common.json b/public/locales/fr/common.json
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/public/locales/fr/common.json
@@ -0,0 +1 @@
+{}
diff --git a/public/locales/fr/header.json b/public/locales/fr/header.json
new file mode 100644
index 0000000..73052c5
--- /dev/null
+++ b/public/locales/fr/header.json
@@ -0,0 +1,4 @@
+{
+  "home": "Page d'accueil",
+  "search": "Recherche"
+}
diff --git a/public/locales/fr/home.json b/public/locales/fr/home.json
new file mode 100644
index 0000000..6f2a42e
--- /dev/null
+++ b/public/locales/fr/home.json
@@ -0,0 +1,7 @@
+{
+  "pageTitle": "Bienvenue sur la page d'accueil du module de recherche du SI In-Sylva",
+  "description": {
+    "part1": "rezer",
+    "part2": "rezer"
+  }
+}
diff --git a/public/locales/fr/maps.json b/public/locales/fr/maps.json
new file mode 100644
index 0000000..afe4298
--- /dev/null
+++ b/public/locales/fr/maps.json
@@ -0,0 +1,7 @@
+{
+  "layersChoiceTitle": "Cliquez sur les couches pour modifier l'affichage.",
+  "layersTableHeaders": {
+    "cartography": "Cartographie",
+    "data": "Données"
+  }
+}
diff --git a/public/locales/fr/profile.json b/public/locales/fr/profile.json
new file mode 100644
index 0000000..983d813
--- /dev/null
+++ b/public/locales/fr/profile.json
@@ -0,0 +1,3 @@
+{
+  "pageTitle": "Gestion du profil"
+}
diff --git a/public/locales/fr/results.json b/public/locales/fr/results.json
new file mode 100644
index 0000000..498fc27
--- /dev/null
+++ b/public/locales/fr/results.json
@@ -0,0 +1,5 @@
+{
+  "downloadResultsButton": {
+    "JSON": "Télécharger en JSON"
+  }
+}
diff --git a/public/locales/fr/search.json b/public/locales/fr/search.json
new file mode 100644
index 0000000..866c88a
--- /dev/null
+++ b/public/locales/fr/search.json
@@ -0,0 +1,3 @@
+{
+  "pageTitle": "Plateforme de recherche de métadonnées In-Sylva"
+}
diff --git a/public/locales/fr/validation.json b/public/locales/fr/validation.json
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/public/locales/fr/validation.json
@@ -0,0 +1 @@
+{}
diff --git a/src/assets/i18n/locales/en/translation.json b/src/assets/i18n/locales/en/translation.json
deleted file mode 100644
index bded201..0000000
--- a/src/assets/i18n/locales/en/translation.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
-  "title": "Welcome to react using react-i18next",
-  "description": {
-    "part1": "To get started, edit <1>src/App.js</1> and save to reload.",
-    "part2": "Switch language between english and german using buttons above."
-  }
-}
diff --git a/src/assets/i18n/locales/fr/translation.json b/src/assets/i18n/locales/fr/translation.json
deleted file mode 100644
index 784b45d..0000000
--- a/src/assets/i18n/locales/fr/translation.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
-  "title": "Bienvenue react-i18next",
-  "description": {
-    "part1": "To get started, edit <1>src/App.js</1> and save to reload.",
-    "part2": "Switch language between english and german using buttons above."
-  }
-}
diff --git a/src/components/Header/Header.js b/src/components/Header/Header.js
index cb58973..376a143 100644
--- a/src/components/Header/Header.js
+++ b/src/components/Header/Header.js
@@ -10,23 +10,26 @@ import {
 import HeaderUserMenu from './header_user_menu';
 import style from './styles';
 import logoInSylva from '../../assets/favicon.svg';
+import { useTranslation } from 'react-i18next';
 
 const structure = [
   {
     id: 0,
-    label: 'Home',
+    label: 'home',
     href: '/app/home',
     icon: '',
   },
   {
     id: 1,
-    label: 'Search',
+    label: 'search',
     href: '/app/search',
     icon: '',
   },
 ];
 
 const Header = () => {
+  const { t } = useTranslation('header');
+
   return (
     <>
       <EuiHeader>
@@ -43,7 +46,7 @@ const Header = () => {
           <EuiHeaderLinks border="right">
             {structure.map((link) => (
               <EuiHeaderLink iconType="empty" key={link.id}>
-                <Link to={link.href}>{link.label}</Link>
+                <Link to={link.href}>{t(link.label)}</Link>
               </EuiHeaderLink>
             ))}
           </EuiHeaderLinks>
diff --git a/src/components/Loading/Loading.js b/src/components/Loading/Loading.js
new file mode 100644
index 0000000..f5213a7
--- /dev/null
+++ b/src/components/Loading/Loading.js
@@ -0,0 +1,12 @@
+import React from 'react';
+import styles from './styles';
+
+const Loading = () => {
+  return (
+    <div style={styles.container}>
+      <h1>Loading...</h1>
+    </div>
+  );
+};
+
+export default Loading;
diff --git a/src/components/Loading/package.json b/src/components/Loading/package.json
new file mode 100644
index 0000000..b4c7045
--- /dev/null
+++ b/src/components/Loading/package.json
@@ -0,0 +1,6 @@
+{
+  "name": "Loading",
+  "version": "1.0.0",
+  "private": true,
+  "main": "Loading.js"
+}
diff --git a/src/components/Loading/styles.js b/src/components/Loading/styles.js
new file mode 100644
index 0000000..6c4d270
--- /dev/null
+++ b/src/components/Loading/styles.js
@@ -0,0 +1,11 @@
+const styles = {
+  container: {
+    width: '100vw',
+    height: '100vh',
+    display: 'flex',
+    justifyContent: 'center',
+    alignItems: 'center',
+  },
+};
+
+export default styles;
diff --git a/src/i18n.js b/src/i18n.js
index 9353fa6..65a0362 100644
--- a/src/i18n.js
+++ b/src/i18n.js
@@ -1,26 +1,21 @@
 import i18n from 'i18next';
 import { initReactI18next } from 'react-i18next';
-import en from './assets/i18n/locales/en/translation.json';
-import fr from './assets/i18n/locales/fr/translation.json';
+import Backend from 'i18next-http-backend';
 
-const resources = {
-  en: {
-    translation: en,
-  },
-  fr: {
-    translation: fr,
-  },
-};
-
-i18n.use(initReactI18next).init({
-  lng: 'fr',
-  fallbackLng: 'fr',
-  resources,
-  debug: true,
-  interpolation: {
-    // not needed for react as it escapes by default
-    escapeValue: false,
-  },
-});
+i18n
+  .use(Backend)
+  .use(initReactI18next)
+  .init({
+    lng: 'fr',
+    fallbackLng: 'fr',
+    defaultNS: 'home',
+    debug: true,
+    load: 'languageOnly',
+    loadPath: 'locales/{{lng}}/{{ns}}.json',
+    interpolation: {
+      // not needed for react as it escapes by default
+      escapeValue: false,
+    },
+  });
 
 export default i18n;
diff --git a/src/index.js b/src/index.js
index 68ea85b..285bd62 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,10 +1,11 @@
-import React from 'react';
+import React, { Suspense } from 'react';
 import ReactDOM from 'react-dom';
 import '@elastic/eui/dist/eui_theme_light.css';
 import { UserProvider, checkUserLogin } from './context/UserContext';
 import App from './App';
 import { getLoginUrl, getUrlParam, redirect } from './Utils.js';
 import './i18n';
+import Loading from './components/Loading';
 
 const userId = getUrlParam('kcId', '');
 const accessToken = getUrlParam('accessToken', '');
@@ -17,7 +18,9 @@ checkUserLogin(userId, accessToken, refreshToken);
 if (sessionStorage.getItem('access_token')) {
   ReactDOM.render(
     <UserProvider>
-      <App userId={userId} accessToken={accessToken} refreshToken={refreshToken} />
+      <Suspense fallback={<Loading />}>
+        <App userId={userId} accessToken={accessToken} refreshToken={refreshToken} />
+      </Suspense>
     </UserProvider>,
     document.getElementById('root')
   );
diff --git a/src/pages/home/Home.js b/src/pages/home/Home.js
index fe80cd6..9eb375b 100644
--- a/src/pages/home/Home.js
+++ b/src/pages/home/Home.js
@@ -9,7 +9,7 @@ import {
 import { useTranslation } from 'react-i18next';
 
 const Home = () => {
-  const { t } = useTranslation();
+  const { t } = useTranslation('home');
 
   return (
     <>
@@ -17,10 +17,8 @@ const Home = () => {
         <EuiPageContentHeader>
           <EuiPageContentHeaderSection>
             <EuiTitle>
-              <h2>Welcome to the IN-SYLVA IS application search module</h2>
+              <h2>{t('pageTitle')}</h2>
             </EuiTitle>
-            <p>{t('title')}</p>
-            <br />
             <br />
             <p>
               As a reminder, it should be remembered that the metadata stored in IN-SYLVA
@@ -32,7 +30,6 @@ const Home = () => {
               made up of a series of fields accompanied by their value.
             </p>
             <br />
-            <br />
             <p>
               With this part of the interface you will be able to search for metadata
               records (previously loaded via the portal), by defining a certain number of
@@ -50,7 +47,6 @@ const Home = () => {
               via which you can do more precise searches on one or more targeted fields.
             </p>
             <br />
-            <br />
             <p>Click on the "Search" tab to access the search interface.</p>
           </EuiPageContentHeaderSection>
         </EuiPageContentHeader>
diff --git a/src/pages/maps/SearchMap.js b/src/pages/maps/SearchMap.js
index 958af49..64a2beb 100644
--- a/src/pages/maps/SearchMap.js
+++ b/src/pages/maps/SearchMap.js
@@ -1,18 +1,19 @@
 import React, { useState, useEffect } from 'react';
 import { Map, View } from 'ol';
-// import TileLayer from "ol/layer/Tile";
 import { Tile as TileLayer, Vector as VectorLayer } from 'ol/layer';
+//import {fromLonLat, get as getProjection} from 'ol/proj.js';
+// import TileLayer from 'ol/layer/Tile';
 import ImageLayer from 'ol/layer/Image';
+import GeoJSON from 'ol/format/GeoJSON';
+import ImageWMS from 'ol/source/ImageWMS';
+//import { Circle as CircleStyle, Fill, Stroke, Style, Text, Icon } from 'ol/style';
+import { toStringXY } from 'ol/coordinate';
 import SourceOSM from 'ol/source/OSM';
 import BingMaps from 'ol/source/BingMaps';
 import { Vector as VectorSource } from 'ol/source';
 import WMTS from 'ol/source/WMTS';
 import WMTSTileGrid from 'ol/tilegrid/WMTS';
-//import {fromLonLat, get as getProjection} from 'ol/proj.js';
 import { getWidth } from 'ol/extent';
-import ImageWMS from 'ol/source/ImageWMS';
-import GeoJSON from 'ol/format/GeoJSON';
-//import { Circle as CircleStyle, Fill, Stroke, Style, Text, Icon } from 'ol/style';
 import { Fill, Stroke, Style, Text, Icon } from 'ol/style';
 import { Circle, Point, Polygon } from 'ol/geom';
 import Feature from 'ol/Feature';
@@ -23,192 +24,43 @@ import {
   OverviewMap,
   defaults as defaultControls,
 } from 'ol/control';
-import { toStringXY } from 'ol/coordinate';
 import 'ol/ol.css';
 import { EuiCheckbox } from '@elastic/eui';
 import { htmlIdGenerator } from '@elastic/eui/lib/services';
 import { updateArrayElement } from '../../Utils.js';
+import { useTranslation } from 'react-i18next';
+import styles from './styles';
 
-const SearchMap = (props) => {
-  /*var image = new CircleStyle({
-        radius: 5,
-        fill: null,
-        stroke: new Stroke({ color: 'red', width: 1 }),
-    });*/
-  const styles = {
-    Point: new Style({
-      image: new Icon({
-        anchor: [0.5, 46],
-        anchorXUnits: 'fraction',
-        anchorYUnits: 'pixels',
-        src: 'https://openlayers.org/en/v3.20.1/examples/data/icon.png',
-      }),
-    }),
-    /* 'Circle': new Style({
-            image: new Circle({
-                radius: 7,
-                fill: new Fill({
-                    color: 'green'
-                }),
-                stroke: new Stroke({
-                    color: 'blue',
-                    width: 2
-                })
-            })
-        }),*/
-    Circle: new Style({
-      stroke: new Stroke({
-        color: 'blue',
-        width: 2,
-      }),
-      //radius: 1000,
-      fill: new Fill({
-        color: 'rgba(0,0,255,0.3)',
-      }),
-    }),
-    /* 'LineString': new Style({
-            stroke: new Stroke({
-                color: 'green',
-                width: 1,
-            }),
-        }),
-        'MultiLineString': new Style({
-            stroke: new Stroke({
-                color: 'green',
-                width: 1,
-            }),
-        }),
-        'MultiPoint': new Style({
-            image: image,
-        }),
-        'MultiPolygon': new Style({
-            stroke: new Stroke({
-                color: 'yellow',
-                width: 1,
-            }),
-            fill: new Fill({
-                color: 'rgba(255, 255, 0, 0.1)',
-            }),
-        }),
-        'Polygon': new Style({
-            stroke: new Stroke({
-                color: 'blue',
-                lineDash: [4],
-                width: 3,
-            }),
-            fill: new Fill({
-                color: 'rgba(0, 0, 255, 0.1)',
-            }),
-        }),
-        'GeometryCollection': new Style({
-            stroke: new Stroke({
-                color: 'magenta',
-                width: 2,
-            }),
-            fill: new Fill({
-                color: 'magenta',
-            }),
-            image: new CircleStyle({
-                radius: 10,
-                fill: null,
-                stroke: new Stroke({
-                    color: 'magenta',
-                }),
-            }),
-        }),
-        'Circle': new Style({
-            stroke: new Stroke({
-                color: 'red',
-                width: 2,
-            }),
-            fill: new Fill({
-                color: 'rgba(255,0,0,0.2)',
-            }),
-        }),
-        bluecircle: {
-            width: 30,
-            height: 30,
-            border: "1px solid #088",
-            bordeRadius: "15",
-            backgroundColor: "#0ff",
-            opacity: 0.5,
-            zIndex: 9999
-        }, */
-    mapContainer: {
-      height: '80vh',
-      width: '60vw',
-    },
-    layerTree: {
-      cursor: 'pointer',
-    },
-  };
-  const source = new SourceOSM();
-  const overviewMapControl = new OverviewMap({
-    layers: [
-      new TileLayer({
-        source: source,
-      }),
-    ],
-  });
-  const [center, setCenter] = useState(proj.fromLonLat([2.5, 46.5]));
-  const [zoom, setZoom] = useState(6);
-  const styleFunction = function (feature) {
-    return styles[feature.getGeometry().getType()];
-  };
-
-  /* const newPoint = {
-        'type': 'Feature',
-        'geometry': {
-            'type': 'Point',
-            'coordinates': proj.fromLonLat([6.37777777778, 43.1938888889]),
-        },
-        'properties': {
-            'Site': 'TEST'
-        }
-    }
-
-    const geojsonObject = {
-        'type': 'FeatureCollection',
-        'crs': {
-            'type': 'name',
-            'properties': {
-                'name': 'EPSG:4326',
-            },
-        },
-        'features': [
-            // newPoint
-        ],
-    } */
-
-  const vectorSource = new VectorSource({
-    /*features: new GeoJSON().readFeatures(geojsonObject),*/
-    projection: 'EPSG:4326',
-  });
-
-  // vectorSource.addFeature(new Feature(new Polygon([[proj.fromLonLat([0, 45]), proj.fromLonLat([0, 50]), proj.fromLonLat([5, 50]), proj.fromLonLat([5, 45])]])));
-
-  const vectorLayer = new VectorLayer({
-    name: 'query_results',
-    source: vectorSource,
-    style: styleFunction,
-  });
-
+const initResolutions = () => {
   const resolutions = [];
-  const matrixIds = [];
   const proj3857 = proj.get('EPSG:3857');
-  const maxResolution = getWidth(proj3857.getExtent()) / 256;
 
+  const maxResolution = getWidth(proj3857.getExtent()) / 256;
   for (let i = 0; i < 20; i++) {
-    matrixIds[i] = i.toString();
     resolutions[i] = maxResolution / Math.pow(2, i);
   }
+  return resolutions;
+};
 
-  const tileGrid = new WMTSTileGrid({
-    origin: [-20037508, 20037508],
-    resolutions: resolutions,
-    matrixIds: matrixIds,
-  });
+const initMatrixIds = () => {
+  const matrixIds = [];
 
+  for (let i = 0; i < 20; i++) {
+    matrixIds[i] = i.toString();
+  }
+  return matrixIds;
+};
+
+const SearchMap = (props) => {
+  const { t } = useTranslation('maps');
+  const source = new SourceOSM();
+  const [center, setCenter] = useState(proj.fromLonLat([2.5, 46.5]));
+  const [zoom, setZoom] = useState(6);
+  const [vectorSource, setVectorSource] = useState(
+    new VectorSource({
+      projection: 'EPSG:4326',
+    })
+  );
   const [mapLayers, setMapLayers] = useState([
     new TileLayer({
       name: 'osm-layer',
@@ -232,7 +84,11 @@ const SearchMap = (props) => {
         matrixSet: 'PM',
         format: 'image/png',
         projection: 'EPSG:3857',
-        tileGrid: tileGrid,
+        tileGrid: new WMTSTileGrid({
+          origin: [-20037508, 20037508],
+          resolutions: initResolutions(),
+          matrixIds: initMatrixIds(),
+        }),
         style: 'normal',
         attributions:
           '<a href="https://www.ign.fr/" target="_blank">' +
@@ -240,29 +96,14 @@ const SearchMap = (props) => {
           'information géographique et forestière" alt="IGN"></a>',
       }),
     }),
-    vectorLayer,
-
-    // new ImageLayer({
-    //     name: 'donuts-insylva-layer',
-    //     source: new ImageWMS({
-    //         url: 'http: //w3.avignon.inra.fr/geoserver/wms',
-    //         params: { 'LAYERS': 'urfm:donut_view' },
-    //         ratio: 1
-    //     })
-    // })
+    new VectorLayer({
+      name: 'query_results',
+      source: vectorSource,
+      style: (feature) => {
+        return styles[feature.getGeometry().getType()];
+      },
+    }),
   ]);
-
-  const [mapLayersVisibility, setMapLayersVisibility] = useState(
-    new Array(mapLayers.length).fill(true)
-  );
-
-  // const posGreenwich = proj.fromLonLat([0, 51.47]);
-  // set initial map objects
-  const view = new View({
-    center: center,
-    zoom: zoom,
-  });
-
   const [map] = useState(
     new Map({
       target: null,
@@ -272,35 +113,24 @@ const SearchMap = (props) => {
           projection: 'EPSG:4326',
         }),
         new ScaleLine(),
-        overviewMapControl,
+        new OverviewMap({
+          layers: [
+            new TileLayer({
+              source: source,
+            }),
+          ],
+        }),
       ]),
-      view: view,
+      view: new View({
+        center: center,
+        zoom: zoom,
+      }),
     })
   );
+  const [mapLayersVisibility, setMapLayersVisibility] = useState(
+    new Array(mapLayers.length).fill(true)
+  );
 
-  const processData = (props) => {
-    if (props.searchResults) {
-      props.searchResults.forEach((result) => {
-        if (
-          result.experimental_site.geo_point &&
-          result.experimental_site.geo_point.longitude &&
-          result.experimental_site.geo_point.latitude
-        ) {
-          //vectorSource.addFeature(new Feature(new Point(proj.fromLonLat([result.experimental_site.geo_point.longitude, result.experimental_site.geo_point.latitude]))))
-          //vectorSource.addFeature(new Feature(new Circle(toStringXY([result.experimental_site.geo_point.longitude, result.experimental_site.geo_point.latitude],1),10)))
-          const coord = [
-            result.experimental_site.geo_point.longitude,
-            result.experimental_site.geo_point.latitude,
-          ];
-          vectorSource.addFeature(new Feature(new Circle(proj.fromLonLat(coord), 1000)));
-          //vectorSource.addFeature(new Feature(new Circle([result.experimental_site.geo_point.longitude, result.experimental_site.geo_point.latitude],10)))
-        }
-      });
-    }
-  };
-
-  // useEffect Hooks
-  // [] = component did mount
   // set the initial map targets
   useEffect(() => {
     map.setTarget('map');
@@ -308,7 +138,6 @@ const SearchMap = (props) => {
       setCenter(map.getView().getCenter());
       setZoom(map.getView().getZoom());
     });
-
     /* map.getCurrentScale = function () {
             //var map = this.getMap();
             var map = this;
@@ -319,7 +148,6 @@ const SearchMap = (props) => {
             var mpu = proj.METERS_PER_UNIT[units];
             var scale = resolution * mpu * 39.37 * dpi;
             return scale;
-
         };
         map.getView().on('change:resolution', function(evt){
 
@@ -337,14 +165,31 @@ const SearchMap = (props) => {
         });
         map.addOverlay(overlay); */
     map.getView().animate({ zoom: zoom }, { center: center }, { duration: 2000 });
-
     processData(props);
-    // clean up upon component unmount
-    /* return () => {
-            map.setTarget(null);
-        }; */
   }, [props]);
 
+  // vectorSource.addFeature(new Feature(new Polygon([[proj.fromLonLat([0, 45]), proj.fromLonLat([0, 50]), proj.fromLonLat([5, 50]), proj.fromLonLat([5, 45])]])));
+
+  const processData = (props) => {
+    if (props.searchResults) {
+      props.searchResults.forEach((result) => {
+        if (
+          result.experimental_site.geo_point &&
+          result.experimental_site.geo_point.longitude &&
+          result.experimental_site.geo_point.latitude
+        ) {
+          //vectorSource.addFeature(new Feature(new Point(proj.fromLonLat([result.experimental_site.geo_point.longitude, result.experimental_site.geo_point.latitude]))))
+          //vectorSource.addFeature(new Feature(new Circle(toStringXY([result.experimental_site.geo_point.longitude, result.experimental_site.geo_point.latitude],1),10)))
+          const pos = [
+            result.experimental_site.geo_point.longitude,
+            result.experimental_site.geo_point.latitude,
+          ];
+          vectorSource.addFeature(new Feature(new Circle(proj.fromLonLat(pos), 1000)));
+        }
+      });
+    }
+  };
+
   const getLayerIndex = (name) => {
     let index = 0;
     mapLayers.forEach((layer) => {
@@ -358,7 +203,6 @@ const SearchMap = (props) => {
   const toggleLayer = (name) => {
     let updatedLayers = mapLayers;
     const layerIndex = getLayerIndex(name);
-    // let updatedLayer = updatedLayers[getLayerIndex(name)]
     setMapLayersVisibility(
       updateArrayElement(
         mapLayersVisibility,
@@ -370,41 +214,21 @@ const SearchMap = (props) => {
     setMapLayers(updatedLayers);
   };
 
-  // helpers
-  /* const btnAction = () => {
-        // when button is clicked, recentre map
-        // this does not work :(
-        setCenter(posGreenwich);
-        setZoom(6);
-    }; */
-  // render
   return (
-    <div>
+    <>
       <div id="map" style={styles.mapContainer}></div>
       <div id="layertree">
-        <br />
-        <h5>Cliquez sur les couches pour modifier leur visibilité.</h5>
-        <br />
-        <table>
+        <table style={styles.legendTable}>
           <thead>
             <tr>
-              <th>Fonds carto ou vecteur</th>
-              {/*<th align="center">Couches INSYLVA</th>
-    <th>Infos attributs</th>*/}
+              <th>{t('layersTableHeaders.cartography')}</th>
+              <th>{t('layersTableHeaders.data')}</th>
             </tr>
           </thead>
           <tbody>
             <tr>
-              <td>
+              <td style={styles.legendTableCells}>
                 <ul>
-                  <li>
-                    <EuiCheckbox
-                      id={htmlIdGenerator()()}
-                      label="Query result"
-                      checked={mapLayersVisibility[getLayerIndex('query_results')]}
-                      onChange={(e) => toggleLayer('query_results')}
-                    />
-                  </li>
                   <li>
                     <EuiCheckbox
                       id={htmlIdGenerator()()}
@@ -429,51 +253,39 @@ const SearchMap = (props) => {
                       onChange={(e) => toggleLayer('IGN')}
                     />
                   </li>
-                  {/*<li>
-                                    <EuiCheckbox
-                                        id={htmlIdGenerator()()}
-                                        label="Départements"
-                                        checked={mapLayersVisibility[getLayerIndex("dept-layer")]}
-                                        onChange={e => toggleLayer("dept-layer")}
-                                    />
-                                </li>
-                                <li>
-                                    <EuiCheckbox
-                                        id={htmlIdGenerator()()}
-                                        label="Régions"
-                                        checked={mapLayersVisibility[getLayerIndex("regs-layer")]}
-                                        onChange={e => toggleLayer("regs-layer")}
-                                    />
-                                </li>*/}
+                  {/*
+                  <li>
+                    <EuiCheckbox
+                      id={htmlIdGenerator()()}
+                      label="Départements"
+                      checked={mapLayersVisibility[getLayerIndex("dept-layer")]}
+                      onChange={e => toggleLayer("dept-layer")}
+                    />
+                  </li>
+                  <li>
+                    <EuiCheckbox
+                      id={htmlIdGenerator()()}
+                      label="Régions"
+                      checked={mapLayersVisibility[getLayerIndex("regs-layer")]}
+                      onChange={e => toggleLayer("regs-layer")}
+                    />
+                  </li>
+                  */}
                 </ul>
               </td>
-              <td>
-                <div id="info">&nbsp;</div>
+              <td style={styles.legendTableCells}>
+                <EuiCheckbox
+                  id={htmlIdGenerator()()}
+                  label="Query result"
+                  checked={mapLayersVisibility[getLayerIndex('query_results')]}
+                  onChange={(e) => toggleLayer('query_results')}
+                />
               </td>
             </tr>
           </tbody>
         </table>
       </div>
-      {/*<div
-                style={styles.bluecircle}
-                ref={overlayRef}
-                id="overlay"
-                title="overlay"
-            />*/}
-      {/*<button
-                style={{
-                    position: "absolute",
-                    right: 10,
-                    top: 10,
-                    backgroundColor: "white"
-                }}
-                onClick={() => {
-                    btnAction();
-                }}
-            >
-                CLICK
-            </button>*/}
-    </div>
+    </>
   );
 };
 
diff --git a/src/pages/maps/styles.js b/src/pages/maps/styles.js
new file mode 100644
index 0000000..a991862
--- /dev/null
+++ b/src/pages/maps/styles.js
@@ -0,0 +1,117 @@
+import { Fill, Icon, Stroke, Style } from 'ol/style';
+
+const styles = {
+  Point: new Style({
+    image: new Icon({
+      anchor: [0.5, 46],
+      anchorXUnits: 'fraction',
+      anchorYUnits: 'pixels',
+      src: 'https://openlayers.org/en/v3.20.1/examples/data/icon.png',
+    }),
+  }),
+  /* 'Circle': new Style({
+          image: new Circle({
+              radius: 7,
+              fill: new Fill({
+                  color: 'green'
+              }),
+              stroke: new Stroke({
+                  color: 'blue',
+                  width: 2
+              })
+          })
+      }),*/
+  Circle: new Style({
+    stroke: new Stroke({
+      color: 'blue',
+      width: 2,
+    }),
+    //radius: 1000,
+    fill: new Fill({
+      color: 'rgba(0,0,255,0.3)',
+    }),
+  }),
+  /* 'LineString': new Style({
+          stroke: new Stroke({
+              color: 'green',
+              width: 1,
+          }),
+      }),
+      'MultiLineString': new Style({
+          stroke: new Stroke({
+              color: 'green',
+              width: 1,
+          }),
+      }),
+      'MultiPoint': new Style({
+          image: image,
+      }),
+      'MultiPolygon': new Style({
+          stroke: new Stroke({
+              color: 'yellow',
+              width: 1,
+          }),
+          fill: new Fill({
+              color: 'rgba(255, 255, 0, 0.1)',
+          }),
+      }),
+      'Polygon': new Style({
+          stroke: new Stroke({
+              color: 'blue',
+              lineDash: [4],
+              width: 3,
+          }),
+          fill: new Fill({
+              color: 'rgba(0, 0, 255, 0.1)',
+          }),
+      }),
+      'GeometryCollection': new Style({
+          stroke: new Stroke({
+              color: 'magenta',
+              width: 2,
+          }),
+          fill: new Fill({
+              color: 'magenta',
+          }),
+          image: new CircleStyle({
+              radius: 10,
+              fill: null,
+              stroke: new Stroke({
+                  color: 'magenta',
+              }),
+          }),
+      }),
+      'Circle': new Style({
+          stroke: new Stroke({
+              color: 'red',
+              width: 2,
+          }),
+          fill: new Fill({
+              color: 'rgba(255,0,0,0.2)',
+          }),
+      }),
+      bluecircle: {
+          width: 30,
+          height: 30,
+          border: "1px solid #088",
+          bordeRadius: "15",
+          backgroundColor: "#0ff",
+          opacity: 0.5,
+          zIndex: 9999
+      }, */
+  mapContainer: {
+    height: '80vh',
+    width: '60vw',
+  },
+  layerTree: {
+    cursor: 'pointer',
+  },
+  legendTable: {
+    margin: '20px',
+  },
+  legendTableCells: {
+    padding: '10px',
+  },
+};
+
+export default styles;
diff --git a/src/pages/profile/Profile.js b/src/pages/profile/Profile.js
index d6fecd3..3e8c303 100644
--- a/src/pages/profile/Profile.js
+++ b/src/pages/profile/Profile.js
@@ -23,20 +23,10 @@ import {
   createUserRequest,
   deleteUserRequest,
 } from '../../actions/user';
-
-/* const fieldsGridOptions = {
-    filter: true,
-    filterType: "dropdown",
-    responsive: "stacked",
-    selectableRows: 'multiple',
-    selectableRowsOnClick: true,
-    onRowsSelect: (rowsSelected, allRows) => {
-    },
-    onRowClick: (rowData, rowState) => {
-    },
-}; */
+import { useTranslation } from 'react-i18next';
 
 const Profile = () => {
+  const { t } = useTranslation('profile');
   const [user, setUser] = useState({});
   const [userRole, setUserRole] = useState('');
   const [groups, setGroups] = useState([]);
@@ -47,11 +37,32 @@ const Profile = () => {
   const [valueError, setValueError] = useState(undefined);
 
   useEffect(() => {
+    const loadUser = () => {
+      if (sessionStorage.getItem('user_id')) {
+        findOneUser(sessionStorage.getItem('user_id')).then((user) => {
+          setUser(user);
+        });
+        findOneUserWithGroupAndRole(sessionStorage.getItem('user_id')).then((result) => {
+          const userGroupList = userGroups;
+          result.forEach((user) => {
+            if (user.groupname) {
+              userGroupList.push({
+                id: user.groupid,
+                label: user.groupname,
+                description: user.groupdescription,
+              });
+            }
+            setUserRole(user.rolename);
+          });
+          setUserGroups(userGroupList);
+        });
+      }
+    };
     loadUser();
     getUserRequests();
     getUserGroups();
     getUserRoles();
-  }, []);
+  }, [userGroups]);
 
   const groupColumns = [
     { field: 'label', name: 'Group Name', width: '30%' },
@@ -112,28 +123,6 @@ const Profile = () => {
     { name: 'Delete', actions: requestActions },
   ];
 
-  const loadUser = () => {
-    if (sessionStorage.getItem('user_id')) {
-      findOneUser(sessionStorage.getItem('user_id')).then((user) => {
-        setUser(user);
-      });
-      findOneUserWithGroupAndRole(sessionStorage.getItem('user_id')).then((result) => {
-        const userGroupList = userGroups;
-        result.forEach((user) => {
-          if (user.groupname) {
-            userGroupList.push({
-              id: user.groupid,
-              label: user.groupname,
-              description: user.groupdescription,
-            });
-          }
-          setUserRole(user.rolename);
-        });
-        setUserGroups(userGroupList);
-      });
-    }
-  };
-
   const getUserGroupLabels = () => {
     let labelList = '';
     if (!!userGroups) {
@@ -161,7 +150,7 @@ const Profile = () => {
         <EuiPageContentHeader>
           <EuiPageContentHeaderSection>
             <EuiTitle>
-              <h2>Profile management</h2>
+              <h2>{t('pageTitle')}</h2>
             </EuiTitle>
           </EuiPageContentHeaderSection>
         </EuiPageContentHeader>
diff --git a/src/pages/results/Results.js b/src/pages/results/Results.js
index c54481e..0c61fce 100644
--- a/src/pages/results/Results.js
+++ b/src/pages/results/Results.js
@@ -15,8 +15,8 @@ import { createTheme, MuiThemeProvider } from '@material-ui/core/styles';
 import MUIDataTable from 'mui-datatables';
 import JsonView from '@in-sylva/json-view';
 import { updateArrayElement } from '../../Utils.js';
-
-const download = require('downloadjs');
+import download from 'downloadjs';
+import { useTranslation } from 'react-i18next';
 
 const getMuiTheme = () =>
   createTheme({
@@ -39,6 +39,7 @@ const changeFlyoutState = (array, index, value, defaultValue) => {
 };
 
 const Results = (searchResults, search, basicSearch) => {
+  const { t } = useTranslation('results');
   const [resultsCol, setResultsCol] = useState([]);
   const [results, setResults] = useState([]);
   const [isFlyoutOpen, setIsFlyoutOpen] = useState([false]);
@@ -62,6 +63,48 @@ const Results = (searchResults, search, basicSearch) => {
     return updatedResults;
   };
 
+  const processData = (metadata) => {
+    if (metadata) {
+      const columns = [];
+      const rows = [];
+      // const metadataRecords = metadata.hits.hits
+      columns.push({
+        name: 'currently open',
+        options: {
+          display: true,
+          viewColumns: true,
+          filter: true,
+        },
+      });
+      for (let recordIndex = 0; recordIndex < metadata.length; recordIndex++) {
+        const row = [];
+        const displayedFields = metadata[recordIndex].resource;
+        const flyoutCell = recordFlyout(metadata[recordIndex], recordIndex);
+        if (recordIndex >= isFlyoutOpen.length) {
+          setIsFlyoutOpen([...isFlyoutOpen, false]);
+        }
+        row.push(flyoutCell);
+        for (const fieldName in displayedFields) {
+          if (typeof displayedFields[fieldName] === 'string') {
+            if (recordIndex === 0) {
+              const column = {
+                name: fieldName,
+                options: {
+                  display: true,
+                },
+              };
+              columns.push(column);
+            }
+            row.push(displayedFields[fieldName]);
+          }
+        }
+        rows.push(row);
+      }
+      setResultsCol(columns);
+      setResults(rows);
+    }
+  };
+
   const recordFlyout = (record, recordIndex, isOpen) => {
     if (isOpen) {
       return (
@@ -270,50 +313,13 @@ const Results = (searchResults, search, basicSearch) => {
         )
     } */
 
-  const processData = (metadata) => {
-    // if (metadata && metadata.hits) {
-    if (metadata) {
-      const columns = [];
-      const rows = [];
-      // const metadataRecords = metadata.hits.hits
-      columns.push({
-        name: 'currently open',
-        options: {
-          display: true,
-          viewColumns: true,
-          filter: true,
-        },
-      });
-      /* for (let recordIndex = 0; recordIndex < metadataRecords.length; recordIndex++) {
-                const row = []
-                const displayedFields = metadataRecords[recordIndex]._source.resource
-                const flyoutCell = recordFlyout(metadataRecords[recordIndex]._source, recordIndex) */
-      for (let recordIndex = 0; recordIndex < metadata.length; recordIndex++) {
-        const row = [];
-        const displayedFields = metadata[recordIndex].resource;
-        const flyoutCell = recordFlyout(metadata[recordIndex], recordIndex);
-        if (recordIndex >= isFlyoutOpen.length) {
-          setIsFlyoutOpen([...isFlyoutOpen, false]);
-        }
-        row.push(flyoutCell);
-        for (const fieldName in displayedFields) {
-          if (typeof displayedFields[fieldName] === 'string') {
-            if (recordIndex === 0) {
-              const column = {
-                name: fieldName,
-                options: {
-                  display: true,
-                },
-              };
-              columns.push(column);
-            }
-            row.push(displayedFields[fieldName]);
-          }
-        }
-        rows.push(row);
-      }
-      setResultsCol(columns);
-      setResults(rows);
+  const downloadResults = () => {
+    if (searchResults) {
+      download(
+        `{"metadataRecords": ${JSON.stringify(searchResults, null, '\t')}}`,
+        'InSylvaSearchResults.json',
+        'application/json'
+      );
     }
   };
 
@@ -338,19 +344,8 @@ const Results = (searchResults, search, basicSearch) => {
           />
         </EuiFlexItem>
         <EuiFlexItem grow={false}>
-          <EuiButton
-            fill
-            onClick={() => {
-              if (searchResults) {
-                download(
-                  `{"metadataRecords": ${JSON.stringify(searchResults, null, '\t')}}`,
-                  'InSylvaSearchResults.json',
-                  'application/json'
-                );
-              }
-            }}
-          >
-            Download as JSON
+          <EuiButton fill onClick={() => downloadResults()}>
+            {t('downloadResultsButton.JSON')}
           </EuiButton>
         </EuiFlexItem>
       </EuiFlexGroup>
diff --git a/src/pages/search/Search.js b/src/pages/search/Search.js
index a1c0044..901e335 100644
--- a/src/pages/search/Search.js
+++ b/src/pages/search/Search.js
@@ -62,6 +62,7 @@ import {
   getQueryCount,
 } from '../../actions/source';
 import { addUserHistory, fetchUserHistory } from '../../actions/user';
+import { useTranslation } from 'react-i18next';
 
 const useStyles = makeStyles((theme) => ({
   container: {
@@ -1516,6 +1517,8 @@ const SourceSelect = (
 };
 
 const Search = () => {
+  const { t } = useTranslation('search');
+  const datePickerStyles = useStyles();
   const [isLoading, setIsLoading] = useState(false);
   const [selectedTabNumber, setSelectedTabNumber] = useState(0);
   const [userHistory, setUserHistory] = useState({});
@@ -1545,7 +1548,6 @@ const Search = () => {
   const [selectedSavedSearch, setSelectedSavedSearch] = useState();
   const [historySelectError, setHistorySelectError] = useState(undefined);
   const [notificationToasts, setNotificationToasts] = useState([]);
-  const datePickerStyles = useStyles();
 
   useEffect(() => {
     fetchPublicFields().then((resultStdFields) => {
@@ -1842,12 +1844,10 @@ const Search = () => {
   return (
     <>
       <EuiPageContent>
-        {' '}
-        {/*style={{ backgroundColor: "#fafafa" }}*/}
         <EuiPageContentHeader>
           <EuiPageContentHeaderSection>
             <EuiTitle>
-              <h2>In-Sylva Metadata Search Platform</h2>
+              <h2>{t('pageTitle')}</h2>
             </EuiTitle>
           </EuiPageContentHeaderSection>
         </EuiPageContentHeader>
-- 
GitLab


From 4c944de119a1d901cc385dc938177383e714c1e7 Mon Sep 17 00:00:00 2001
From: rbisson <remi.bisson@inrae.fr>
Date: Mon, 29 Apr 2024 11:16:40 +0200
Subject: [PATCH 04/17] added a component to switch language

---
 public/locales/en/common.json                 |  7 +-
 public/locales/fr/common.json                 |  7 +-
 src/components/Header/Header.js               | 16 ++--
 src/components/Header/HeaderUserMenu.js       | 88 +++++++++++++++++++
 src/components/Header/header_user_menu.js     | 86 ------------------
 src/components/Header/styles.js               | 16 +++-
 .../LanguageSwitcher/LanguageSwitcher.js      | 29 ++++++
 src/components/LanguageSwitcher/styles.js     |  7 ++
 8 files changed, 159 insertions(+), 97 deletions(-)
 create mode 100644 src/components/Header/HeaderUserMenu.js
 delete mode 100644 src/components/Header/header_user_menu.js
 create mode 100644 src/components/LanguageSwitcher/LanguageSwitcher.js
 create mode 100644 src/components/LanguageSwitcher/styles.js

diff --git a/public/locales/en/common.json b/public/locales/en/common.json
index 0967ef4..66bbf9e 100644
--- a/public/locales/en/common.json
+++ b/public/locales/en/common.json
@@ -1 +1,6 @@
-{}
+{
+  "languages": {
+    "en": "English",
+    "fr": "French"
+  }
+}
diff --git a/public/locales/fr/common.json b/public/locales/fr/common.json
index 0967ef4..74f9cd8 100644
--- a/public/locales/fr/common.json
+++ b/public/locales/fr/common.json
@@ -1 +1,6 @@
-{}
+{
+  "languages": {
+    "en": "Anglais",
+    "fr": "Français"
+  }
+}
diff --git a/src/components/Header/Header.js b/src/components/Header/Header.js
index 376a143..ed7ce1f 100644
--- a/src/components/Header/Header.js
+++ b/src/components/Header/Header.js
@@ -7,10 +7,11 @@ import {
   EuiHeaderLinks,
   EuiHeaderLink,
 } from '@elastic/eui';
-import HeaderUserMenu from './header_user_menu';
+import HeaderUserMenu from './HeaderUserMenu';
 import style from './styles';
 import logoInSylva from '../../assets/favicon.svg';
 import { useTranslation } from 'react-i18next';
+import LanguageSwitcher from '../LanguageSwitcher/LanguageSwitcher';
 
 const structure = [
   {
@@ -34,16 +35,16 @@ const Header = () => {
     <>
       <EuiHeader>
         <EuiHeaderSection grow={true}>
-          <EuiHeaderSectionItem border="right">
+          <EuiHeaderSectionItem>
             <img
-              style={style}
+              style={style.logo}
               src={logoInSylva}
               width="75"
               height="45"
               alt="Logo INRAE"
             />
           </EuiHeaderSectionItem>
-          <EuiHeaderLinks border="right">
+          <EuiHeaderLinks>
             {structure.map((link) => (
               <EuiHeaderLink iconType="empty" key={link.id}>
                 <Link to={link.href}>{t(link.label)}</Link>
@@ -52,7 +53,12 @@ const Header = () => {
           </EuiHeaderLinks>
         </EuiHeaderSection>
         <EuiHeaderSection side="right">
-          <EuiHeaderSectionItem>{HeaderUserMenu()}</EuiHeaderSectionItem>
+          <EuiHeaderSectionItem style={style.languageSwitcherItem} border={'none'}>
+            <LanguageSwitcher />
+          </EuiHeaderSectionItem>
+          <EuiHeaderSectionItem style={style.userMenuItem} border={'none'}>
+            <HeaderUserMenu />
+          </EuiHeaderSectionItem>
         </EuiHeaderSection>
       </EuiHeader>
     </>
diff --git a/src/components/Header/HeaderUserMenu.js b/src/components/Header/HeaderUserMenu.js
new file mode 100644
index 0000000..122e7a2
--- /dev/null
+++ b/src/components/Header/HeaderUserMenu.js
@@ -0,0 +1,88 @@
+import React, { useEffect, useState } from 'react';
+import {
+  EuiAvatar,
+  EuiFlexGroup,
+  EuiFlexItem,
+  EuiLink,
+  EuiText,
+  EuiSpacer,
+  EuiPopover,
+  EuiButtonIcon,
+} from '@elastic/eui';
+import { signOut } from '../../context/UserContext';
+import { findOneUser } from '../../actions/user';
+
+const HeaderUserMenu = () => {
+  const [isOpen, setIsOpen] = useState(false);
+  const [user, setUser] = useState({});
+
+  const onMenuButtonClick = () => {
+    setIsOpen(!isOpen);
+  };
+
+  const closeMenu = () => {
+    setIsOpen(false);
+  };
+
+  useEffect(() => {
+    const loadUser = () => {
+      if (sessionStorage.getItem('user_id')) {
+        findOneUser(sessionStorage.getItem('user_id')).then((user) => {
+          setUser(user);
+        });
+      }
+    };
+
+    loadUser();
+  }, []);
+
+  const HeaderUserButton = (
+    <EuiButtonIcon
+      size="s"
+      onClick={onMenuButtonClick}
+      iconType="user"
+      title="User profile"
+      aria-label="User profile"
+    />
+  );
+
+  return user.username ? (
+    <EuiPopover
+      id="headerUserMenu"
+      ownFocus
+      button={HeaderUserButton}
+      isOpen={isOpen}
+      anchorPosition="downRight"
+      closePopover={closeMenu}
+      panelPaddingSize="none"
+    >
+      <div style={{ width: 320 }}>
+        <EuiFlexGroup gutterSize="m" className="euiHeaderProfile" responsive={false}>
+          <EuiFlexItem grow={false}>
+            <EuiAvatar name={user.username} size="xl" />
+          </EuiFlexItem>
+          <EuiFlexItem>
+            <EuiText>{user.username}</EuiText>
+            <EuiSpacer size="m" />
+            <EuiFlexGroup>
+              <EuiFlexItem>
+                <EuiFlexGroup justifyContent="spaceBetween">
+                  <EuiFlexItem grow={false}>
+                    <EuiLink href="#/app/profile">Edit profile</EuiLink>
+                  </EuiFlexItem>
+                  <EuiFlexItem grow={false}>
+                    <EuiLink onClick={() => signOut()}>Log out</EuiLink>
+                  </EuiFlexItem>
+                </EuiFlexGroup>
+              </EuiFlexItem>
+            </EuiFlexGroup>
+          </EuiFlexItem>
+        </EuiFlexGroup>
+      </div>
+    </EuiPopover>
+  ) : (
+    <></>
+  );
+};
+
+export default HeaderUserMenu;
diff --git a/src/components/Header/header_user_menu.js b/src/components/Header/header_user_menu.js
deleted file mode 100644
index feb51bb..0000000
--- a/src/components/Header/header_user_menu.js
+++ /dev/null
@@ -1,86 +0,0 @@
-import React, { useEffect, useState } from 'react';
-import {
-  EuiAvatar,
-  EuiFlexGroup,
-  EuiFlexItem,
-  EuiLink,
-  EuiText,
-  EuiSpacer,
-  EuiPopover,
-  EuiButtonIcon,
-} from '@elastic/eui';
-import { signOut } from '../../context/UserContext';
-import { findOneUser } from '../../actions/user';
-
-export default function HeaderUserMenu() {
-  const [isOpen, setIsOpen] = useState(false);
-  const [user, setUser] = useState({});
-
-  const onMenuButtonClick = () => {
-    setIsOpen(!isOpen);
-  };
-
-  const closeMenu = () => {
-    setIsOpen(false);
-  };
-
-  const loadUser = () => {
-    if (sessionStorage.getItem('user_id')) {
-      findOneUser(sessionStorage.getItem('user_id')).then((user) => {
-        setUser(user);
-      });
-    }
-  };
-
-  useEffect(() => {
-    loadUser();
-  }, []);
-
-  const HeaderUserButton = (
-    <EuiButtonIcon
-      size="s"
-      onClick={onMenuButtonClick}
-      iconType="user"
-      title="User profile"
-      aria-label="User profile"
-    />
-  );
-
-  return (
-    user.username && (
-      <EuiPopover
-        id="headerUserMenu"
-        ownFocus
-        button={HeaderUserButton}
-        isOpen={isOpen}
-        anchorPosition="downRight"
-        closePopover={closeMenu}
-        panelPaddingSize="none"
-      >
-        <div style={{ width: 320 }}>
-          <EuiFlexGroup gutterSize="m" className="euiHeaderProfile" responsive={false}>
-            <EuiFlexItem grow={false}>
-              <EuiAvatar name={user.username} size="xl" />
-            </EuiFlexItem>
-            <EuiFlexItem>
-              <EuiText>{user.username}</EuiText>
-              <EuiSpacer size="m" />
-              <EuiFlexGroup>
-                <EuiFlexItem>
-                  <EuiFlexGroup justifyContent="spaceBetween">
-                    <EuiFlexItem grow={false}>
-                      <EuiLink href="#/app/profile">Edit profile</EuiLink>
-                    </EuiFlexItem>
-                    <EuiFlexItem grow={false}>
-                      <EuiLink onClick={() => signOut()}>Log out</EuiLink>
-                    </EuiFlexItem>
-                  </EuiFlexGroup>
-                </EuiFlexItem>
-              </EuiFlexGroup>
-            </EuiFlexItem>
-          </EuiFlexGroup>
-        </div>
-      </EuiPopover>
-    )
-  );
-}
diff --git a/src/components/Header/styles.js b/src/components/Header/styles.js
index 2515727..27b1761 100644
--- a/src/components/Header/styles.js
+++ b/src/components/Header/styles.js
@@ -1,8 +1,16 @@
 const headerStyle = {
-  paddingTop: '10px',
-  paddingBottom: '-6px',
-  paddingRight: '10px',
-  paddingLeft: '10px',
+  logo: {
+    paddingTop: '10px',
+    paddingBottom: '-6px',
+    paddingRight: '10px',
+    paddingLeft: '10px',
+  },
+  languageSwitcherItem: {
+    margin: '10px',
+  },
+  userMenuItem: {
+    marginRight: '10px',
+  },
 };
 
 export default headerStyle;
diff --git a/src/components/LanguageSwitcher/LanguageSwitcher.js b/src/components/LanguageSwitcher/LanguageSwitcher.js
new file mode 100644
index 0000000..7fb068c
--- /dev/null
+++ b/src/components/LanguageSwitcher/LanguageSwitcher.js
@@ -0,0 +1,29 @@
+import React from 'react';
+import { useTranslation } from 'react-i18next';
+import styles from './styles';
+import { EuiSelect } from '@elastic/eui';
+
+const LanguageSwitcher = () => {
+  const { t, i18n } = useTranslation('common');
+
+  const options = [
+    { text: t('languages.en'), value: 'en' },
+    { text: t('languages.fr'), value: 'fr' },
+  ];
+
+  const changeLanguage = (newLng) => {
+    i18n.changeLanguage(newLng).then();
+  };
+
+  return (
+    <EuiSelect
+      style={styles.select}
+      options={options}
+      compressed={true}
+      value={i18n.resolvedLanguage}
+      onChange={(e) => changeLanguage(e.target.value)}
+    />
+  );
+};
+
+export default LanguageSwitcher;
diff --git a/src/components/LanguageSwitcher/styles.js b/src/components/LanguageSwitcher/styles.js
new file mode 100644
index 0000000..9c4c4bb
--- /dev/null
+++ b/src/components/LanguageSwitcher/styles.js
@@ -0,0 +1,7 @@
+const styles = {
+  select: {
+    borderRadius: '6px',
+  },
+};
+
+export default styles;
-- 
GitLab


From 549ca1b9fea997216c2df800164cd327b08b5711 Mon Sep 17 00:00:00 2001
From: rbisson <remi.bisson@inrae.fr>
Date: Mon, 29 Apr 2024 14:49:48 +0200
Subject: [PATCH 05/17] added full translations for homepage

---
 public/locales/en/home.json | 10 +++++++++-
 public/locales/fr/home.json | 12 ++++++++----
 src/pages/home/Home.js      | 31 ++++++-------------------------
 3 files changed, 23 insertions(+), 30 deletions(-)

diff --git a/public/locales/en/home.json b/public/locales/en/home.json
index 0ae6026..d1038e0 100644
--- a/public/locales/en/home.json
+++ b/public/locales/en/home.json
@@ -1,3 +1,11 @@
 {
-  "pageTitle": "Welcome on In-Sylva search module's homepage."
+  "pageTitle": "Welcome on In-Sylva search module's homepage.",
+  "searchToolDescription": {
+    "part1": "As a reminder, it should be remembered that the metadata stored in IN-SYLVA IS are structured around the IN-SYLVA standard.",
+    "part2": "This standard is composed of metadata fields. A metadata record is therefore made up of a series of fields accompanied by their value.",
+    "part3": "With this part of the interface you will be able to search for metadata records (previously loaded via the portal), by defining a certain number of criteria.",
+    "part4": "By default the \"search\" interface opens to a \"plain text\" search, ie the records returned in the result are those which, in one of the field values, contains the supplied character string.",
+    "part5": "A click on the Advanced search button gives access to a more complete form via which you can do more precise searches on one or more targeted fields.",
+    "part6": "Click on the \"Search\" tab to access the search interface."
+  }
 }
diff --git a/public/locales/fr/home.json b/public/locales/fr/home.json
index 6f2a42e..f9bdcd6 100644
--- a/public/locales/fr/home.json
+++ b/public/locales/fr/home.json
@@ -1,7 +1,11 @@
 {
-  "pageTitle": "Bienvenue sur la page d'accueil du module de recherche du SI In-Sylva",
-  "description": {
-    "part1": "rezer",
-    "part2": "rezer"
+  "pageTitle": "Bienvenue sur la page d'accueil du module de recherche du Système d'Information In-Sylva",
+  "searchToolDescription": {
+    "part1": "Il est important de rappeler que les métadonnées stockées dans le SI In-Sylva sont structurées autour du standard établi par In-Sylva.",
+    "part2": "Il est composé de champs de métadonnées. Une fiche de métadonnées est donc constituée d'une série de champs accompagnés de leur valeur.",
+    "part3": "Cette interface vous permettra de rechercher des fiches de métadonnées (chargées au préalable par le Portal), en définissant un certain nombre de critères.",
+    "part4": "L'interface \"Recherche\" ouvre une zone de texte de \"Recherche basique\". Les résultats correspondent aux fiches de métadonnées contenant, dans un de leurs champs, la chaîne de caractère renseignée.",
+    "part5": "Un click sur le bouton \"Recherche avancée\" vous permets d'accéder à un formulaire plus complet qui vous permettra des recherches plus précises sur un ou plusieurs champs donnés.",
+    "part6": "Clickez sur l'onglet \"Recherche\" pour accéder à l'interface de recherche."
   }
 }
diff --git a/src/pages/home/Home.js b/src/pages/home/Home.js
index 9eb375b..28040d1 100644
--- a/src/pages/home/Home.js
+++ b/src/pages/home/Home.js
@@ -3,7 +3,6 @@ import {
   EuiPageContent,
   EuiPageContentHeader,
   EuiPageContentHeaderSection,
-  EuiPageContentBody,
   EuiTitle,
 } from '@elastic/eui';
 import { useTranslation } from 'react-i18next';
@@ -20,37 +19,19 @@ const Home = () => {
               <h2>{t('pageTitle')}</h2>
             </EuiTitle>
             <br />
-            <p>
-              As a reminder, it should be remembered that the metadata stored in IN-SYLVA
-              IS are structured around the IN-SYLVA standard.
-            </p>
+            <p>{t('searchToolDescription.part1')}</p>
             <br />
-            <p>
-              This standard is composed of metadata fields. A metadata record is therefore
-              made up of a series of fields accompanied by their value.
-            </p>
+            <p>{t('searchToolDescription.part2')}</p>
             <br />
-            <p>
-              With this part of the interface you will be able to search for metadata
-              records (previously loaded via the portal), by defining a certain number of
-              criteria.
-            </p>
+            <p>{t('searchToolDescription.part3')}</p>
             <br />
-            <p>
-              By default the "search" interface opens to a "plain text" search, ie the
-              records returned in the result are those which, in one of the field values,
-              contains the supplied character string.
-            </p>
+            <p>{t('searchToolDescription.part4')}</p>
             <br />
-            <p>
-              A click on the Advanced search button gives access to a more complete form
-              via which you can do more precise searches on one or more targeted fields.
-            </p>
+            <p>{t('searchToolDescription.part5')}</p>
             <br />
-            <p>Click on the "Search" tab to access the search interface.</p>
+            <p>{t('searchToolDescription.part6')}</p>
           </EuiPageContentHeaderSection>
         </EuiPageContentHeader>
-        <EuiPageContentBody></EuiPageContentBody>
       </EuiPageContent>
     </>
   );
-- 
GitLab


From 6939f712a291673f9913e16176c2b4e93c205691 Mon Sep 17 00:00:00 2001
From: rbisson <remi.bisson@inrae.fr>
Date: Mon, 29 Apr 2024 15:12:44 +0200
Subject: [PATCH 06/17] added full translation for Header and HeaderUserMenu

---
 public/locales/en/common.json           |  3 ++-
 public/locales/en/header.json           | 11 +++++++++--
 public/locales/fr/common.json           |  3 ++-
 public/locales/fr/header.json           | 11 +++++++++--
 src/components/Header/Header.js         |  6 +++---
 src/components/Header/HeaderUserMenu.js | 16 +++++++++++-----
 6 files changed, 36 insertions(+), 14 deletions(-)

diff --git a/public/locales/en/common.json b/public/locales/en/common.json
index 66bbf9e..deda3d5 100644
--- a/public/locales/en/common.json
+++ b/public/locales/en/common.json
@@ -2,5 +2,6 @@
   "languages": {
     "en": "English",
     "fr": "French"
-  }
+  },
+  "inSylvaLogoAlt": "In-Sylva logo"
 }
diff --git a/public/locales/en/header.json b/public/locales/en/header.json
index 9342625..889a13d 100644
--- a/public/locales/en/header.json
+++ b/public/locales/en/header.json
@@ -1,4 +1,11 @@
 {
-  "home": "Home",
-  "search": "Search"
+  "tabs": {
+    "home": "Home",
+    "search": "Search"
+  },
+  "userMenu": {
+    "title": "User profile",
+    "editProfileButton": "Edit profile",
+    "logOutButton": "Log out"
+  }
 }
diff --git a/public/locales/fr/common.json b/public/locales/fr/common.json
index 74f9cd8..fa609bd 100644
--- a/public/locales/fr/common.json
+++ b/public/locales/fr/common.json
@@ -2,5 +2,6 @@
   "languages": {
     "en": "Anglais",
     "fr": "Français"
-  }
+  },
+  "inSylvaLogoAlt": "Logo In-Sylva"
 }
diff --git a/public/locales/fr/header.json b/public/locales/fr/header.json
index 73052c5..a5e28a5 100644
--- a/public/locales/fr/header.json
+++ b/public/locales/fr/header.json
@@ -1,4 +1,11 @@
 {
-  "home": "Page d'accueil",
-  "search": "Recherche"
+  "tabs": {
+    "home": "Page d'accueil",
+    "search": "Recherche"
+  },
+  "userMenu": {
+    "title": "Profil utilisateur",
+    "editProfileButton": "Modifier mon profil",
+    "logOutButton": "Déconnexion"
+  }
 }
diff --git a/src/components/Header/Header.js b/src/components/Header/Header.js
index ed7ce1f..d44347a 100644
--- a/src/components/Header/Header.js
+++ b/src/components/Header/Header.js
@@ -29,7 +29,7 @@ const structure = [
 ];
 
 const Header = () => {
-  const { t } = useTranslation('header');
+  const { t } = useTranslation(['header', 'common']);
 
   return (
     <>
@@ -41,13 +41,13 @@ const Header = () => {
               src={logoInSylva}
               width="75"
               height="45"
-              alt="Logo INRAE"
+              alt={t('common:inSylvaLogoAlt')}
             />
           </EuiHeaderSectionItem>
           <EuiHeaderLinks>
             {structure.map((link) => (
               <EuiHeaderLink iconType="empty" key={link.id}>
-                <Link to={link.href}>{t(link.label)}</Link>
+                <Link to={link.href}>{t(`tabs.${link.label}`)}</Link>
               </EuiHeaderLink>
             ))}
           </EuiHeaderLinks>
diff --git a/src/components/Header/HeaderUserMenu.js b/src/components/Header/HeaderUserMenu.js
index 122e7a2..2235573 100644
--- a/src/components/Header/HeaderUserMenu.js
+++ b/src/components/Header/HeaderUserMenu.js
@@ -11,8 +11,10 @@ import {
 } from '@elastic/eui';
 import { signOut } from '../../context/UserContext';
 import { findOneUser } from '../../actions/user';
+import { useTranslation } from 'react-i18next';
 
 const HeaderUserMenu = () => {
+  const { t } = useTranslation('header');
   const [isOpen, setIsOpen] = useState(false);
   const [user, setUser] = useState({});
 
@@ -41,8 +43,8 @@ const HeaderUserMenu = () => {
       size="s"
       onClick={onMenuButtonClick}
       iconType="user"
-      title="User profile"
-      aria-label="User profile"
+      title={t('userMenu.title')}
+      aria-label={t('userMenu.title')}
     />
   );
 
@@ -56,7 +58,7 @@ const HeaderUserMenu = () => {
       closePopover={closeMenu}
       panelPaddingSize="none"
     >
-      <div style={{ width: 320 }}>
+      <div>
         <EuiFlexGroup gutterSize="m" className="euiHeaderProfile" responsive={false}>
           <EuiFlexItem grow={false}>
             <EuiAvatar name={user.username} size="xl" />
@@ -68,10 +70,14 @@ const HeaderUserMenu = () => {
               <EuiFlexItem>
                 <EuiFlexGroup justifyContent="spaceBetween">
                   <EuiFlexItem grow={false}>
-                    <EuiLink href="#/app/profile">Edit profile</EuiLink>
+                    <EuiLink href="#/app/profile">
+                      {t('userMenu.editProfileButton')}
+                    </EuiLink>
                   </EuiFlexItem>
                   <EuiFlexItem grow={false}>
-                    <EuiLink onClick={() => signOut()}>Log out</EuiLink>
+                    <EuiLink onClick={() => signOut()}>
+                      {t('userMenu.logOutButton')}
+                    </EuiLink>
                   </EuiFlexItem>
                 </EuiFlexGroup>
               </EuiFlexItem>
-- 
GitLab


From 3efae1c0445405166e02244ac58e2f38a05f47a9 Mon Sep 17 00:00:00 2001
From: rbisson <remi.bisson@inrae.fr>
Date: Mon, 29 Apr 2024 15:36:58 +0200
Subject: [PATCH 07/17] added full translations for Maps tab

---
 public/locales/en/maps.json |  6 ++++++
 public/locales/fr/maps.json |  6 ++++++
 src/pages/maps/SearchMap.js | 18 +++++++++---------
 src/pages/maps/styles.js    |  4 ++--
 4 files changed, 23 insertions(+), 11 deletions(-)

diff --git a/public/locales/en/maps.json b/public/locales/en/maps.json
index 2e100cb..08ae921 100644
--- a/public/locales/en/maps.json
+++ b/public/locales/en/maps.json
@@ -3,5 +3,11 @@
   "layersTableHeaders": {
     "cartography": "Cartography",
     "data": "Data"
+  },
+  "layersTable": {
+    "openStreetMap": "Open Street Map",
+    "bingAerial": "Bing Aerial",
+    "IGN": "IGN map",
+    "queryResults": "Query results"
   }
 }
diff --git a/public/locales/fr/maps.json b/public/locales/fr/maps.json
index afe4298..a03e165 100644
--- a/public/locales/fr/maps.json
+++ b/public/locales/fr/maps.json
@@ -3,5 +3,11 @@
   "layersTableHeaders": {
     "cartography": "Cartographie",
     "data": "Données"
+  },
+  "layersTable": {
+    "openStreetMap": "Open Street Map",
+    "bingAerial": "Bing carte",
+    "IGN": "Plan IGN",
+    "queryResults": "Résultats de la requête"
   }
 }
diff --git a/src/pages/maps/SearchMap.js b/src/pages/maps/SearchMap.js
index 64a2beb..db06894 100644
--- a/src/pages/maps/SearchMap.js
+++ b/src/pages/maps/SearchMap.js
@@ -2,11 +2,12 @@ import React, { useState, useEffect } from 'react';
 import { Map, View } from 'ol';
 import { Tile as TileLayer, Vector as VectorLayer } from 'ol/layer';
 //import {fromLonLat, get as getProjection} from 'ol/proj.js';
-// import TileLayer from 'ol/layer/Tile';
+//import TileLayer from 'ol/layer/Tile';
 import ImageLayer from 'ol/layer/Image';
 import GeoJSON from 'ol/format/GeoJSON';
 import ImageWMS from 'ol/source/ImageWMS';
 //import { Circle as CircleStyle, Fill, Stroke, Style, Text, Icon } from 'ol/style';
+import { Fill, Stroke, Style, Text, Icon } from 'ol/style';
 import { toStringXY } from 'ol/coordinate';
 import SourceOSM from 'ol/source/OSM';
 import BingMaps from 'ol/source/BingMaps';
@@ -14,7 +15,6 @@ import { Vector as VectorSource } from 'ol/source';
 import WMTS from 'ol/source/WMTS';
 import WMTSTileGrid from 'ol/tilegrid/WMTS';
 import { getWidth } from 'ol/extent';
-import { Fill, Stroke, Style, Text, Icon } from 'ol/style';
 import { Circle, Point, Polygon } from 'ol/geom';
 import Feature from 'ol/Feature';
 import * as proj from 'ol/proj';
@@ -218,7 +218,7 @@ const SearchMap = (props) => {
     <>
       <div id="map" style={styles.mapContainer}></div>
       <div id="layertree">
-        <table style={styles.legendTable}>
+        <table style={styles.layersTable}>
           <thead>
             <tr>
               <th>{t('layersTableHeaders.cartography')}</th>
@@ -227,12 +227,12 @@ const SearchMap = (props) => {
           </thead>
           <tbody>
             <tr>
-              <td style={styles.legendTableCells}>
+              <td style={styles.layersTableCells}>
                 <ul>
                   <li>
                     <EuiCheckbox
                       id={htmlIdGenerator()()}
-                      label="Open Street Map"
+                      label={t('layersTable.openStreetMap')}
                       checked={mapLayersVisibility[getLayerIndex('osm-layer')]}
                       onChange={(e) => toggleLayer('osm-layer')}
                     />
@@ -240,7 +240,7 @@ const SearchMap = (props) => {
                   <li>
                     <EuiCheckbox
                       id={htmlIdGenerator()()}
-                      label="Bing Aerial"
+                      label={t('layersTable.bingAerial')}
                       checked={mapLayersVisibility[getLayerIndex('Bing Aerial')]}
                       onChange={(e) => toggleLayer('Bing Aerial')}
                     />
@@ -248,7 +248,7 @@ const SearchMap = (props) => {
                   <li>
                     <EuiCheckbox
                       id={htmlIdGenerator()()}
-                      label="PLAN IGN"
+                      label={t('maps:layersTable.IGN')}
                       checked={mapLayersVisibility[getLayerIndex('IGN')]}
                       onChange={(e) => toggleLayer('IGN')}
                     />
@@ -273,10 +273,10 @@ const SearchMap = (props) => {
                   */}
                 </ul>
               </td>
-              <td style={styles.legendTableCells}>
+              <td style={styles.layersTableCells}>
                 <EuiCheckbox
                   id={htmlIdGenerator()()}
-                  label="Query result"
+                  label={t('layersTable.queryResults')}
                   checked={mapLayersVisibility[getLayerIndex('query_results')]}
                   onChange={(e) => toggleLayer('query_results')}
                 />
diff --git a/src/pages/maps/styles.js b/src/pages/maps/styles.js
index a991862..c1b8b54 100644
--- a/src/pages/maps/styles.js
+++ b/src/pages/maps/styles.js
@@ -106,10 +106,10 @@ const styles = {
   layerTree: {
     cursor: 'pointer',
   },
-  legendTable: {
+  layersTable: {
     margin: '20px',
   },
-  legendTableCells: {
+  layersTableCells: {
     padding: '10px',
   },
 };
-- 
GitLab


From fb013c13b3be49ff05f4a8400253dbdc1b4c88c8 Mon Sep 17 00:00:00 2001
From: rbisson <remi.bisson@inrae.fr>
Date: Tue, 30 Apr 2024 15:00:52 +0200
Subject: [PATCH 08/17] added full translations for profile page

---
 public/locales/en/common.json     |  6 ++-
 public/locales/en/profile.json    | 22 +++++++-
 public/locales/en/validation.json |  4 +-
 public/locales/fr/common.json     |  6 ++-
 public/locales/fr/profile.json    | 22 +++++++-
 public/locales/fr/validation.json |  4 +-
 src/pages/profile/Profile.js      | 90 ++++++++++++++++++-------------
 src/pages/profile/styles.js       |  8 +++
 8 files changed, 118 insertions(+), 44 deletions(-)
 create mode 100644 src/pages/profile/styles.js

diff --git a/public/locales/en/common.json b/public/locales/en/common.json
index deda3d5..48a5dcf 100644
--- a/public/locales/en/common.json
+++ b/public/locales/en/common.json
@@ -3,5 +3,9 @@
     "en": "English",
     "fr": "French"
   },
-  "inSylvaLogoAlt": "In-Sylva logo"
+  "inSylvaLogoAlt": "In-Sylva logo",
+  "validationActions": {
+    "cancel": "Cancel",
+    "send": "Send"
+  }
 }
diff --git a/public/locales/en/profile.json b/public/locales/en/profile.json
index 7c610a5..76ab2db 100644
--- a/public/locales/en/profile.json
+++ b/public/locales/en/profile.json
@@ -1,3 +1,23 @@
 {
-  "pageTitle": "Profile management"
+  "pageTitle": "Profile management",
+  "groups": {
+    "groupsList": "Group list",
+    "groupName": "Name",
+    "groupDescription": "Description"
+  },
+  "requestsList": {
+    "requestsList": "Requests list",
+    "requestsMessage": "Message",
+    "processed": "Processed",
+    "cancelRequest": "Cancel this request"
+  },
+  "groupRequests": {
+    "requestGroupAssignment": "Request a group assignment",
+    "currentGroups": "You currently belong to (or have a pending request for) these groups:",
+    "noGroup": "You currently don't belong to any group."
+  },
+  "roleRequests": {
+    "requestRoleAssignment": "Request an application role",
+    "currentRole": "You currently have (or have a pending request for) this role:"
+  }
 }
diff --git a/public/locales/en/validation.json b/public/locales/en/validation.json
index 0967ef4..b12bd6a 100644
--- a/public/locales/en/validation.json
+++ b/public/locales/en/validation.json
@@ -1 +1,3 @@
-{}
+{
+  "requestSent": "Your request has been sent to the administrators."
+}
diff --git a/public/locales/fr/common.json b/public/locales/fr/common.json
index fa609bd..74be05d 100644
--- a/public/locales/fr/common.json
+++ b/public/locales/fr/common.json
@@ -3,5 +3,9 @@
     "en": "Anglais",
     "fr": "Français"
   },
-  "inSylvaLogoAlt": "Logo In-Sylva"
+  "inSylvaLogoAlt": "Logo In-Sylva",
+  "validationActions": {
+    "cancel": "Annuler",
+    "send": "Envoyer"
+  }
 }
diff --git a/public/locales/fr/profile.json b/public/locales/fr/profile.json
index 983d813..9f30ddb 100644
--- a/public/locales/fr/profile.json
+++ b/public/locales/fr/profile.json
@@ -1,3 +1,23 @@
 {
-  "pageTitle": "Gestion du profil"
+  "pageTitle": "Gestion du profil",
+  "groups": {
+    "groupsList": "Liste des groupes",
+    "groupName": "Nom",
+    "groupDescription": "Description"
+  },
+  "requestsList": {
+    "requestsList": "Liste des requêtes",
+    "requestsMessage": "Message",
+    "processed": "Traitée",
+    "cancelRequest": "Annuler cette requête"
+  },
+  "groupRequests": {
+    "requestGroupAssignment": "Demander à faire parti d'un groupe",
+    "currentGroups": "Vous faites actuellement parti (ou avez une demande pour) de ces groupes :",
+    "noGroup": "Vous ne faites actuellement parti d'aucun groupe."
+  },
+  "roleRequests": {
+    "requestRoleAssignment": "Demander un rôle",
+    "currentRole": "Votre rôle actuel (ou demande en cours):"
+  }
 }
diff --git a/public/locales/fr/validation.json b/public/locales/fr/validation.json
index 0967ef4..0f29592 100644
--- a/public/locales/fr/validation.json
+++ b/public/locales/fr/validation.json
@@ -1 +1,3 @@
-{}
+{
+  "requestSent": "Votre requête à bien été envoyée."
+}
diff --git a/src/pages/profile/Profile.js b/src/pages/profile/Profile.js
index 3e8c303..89bd3a7 100644
--- a/src/pages/profile/Profile.js
+++ b/src/pages/profile/Profile.js
@@ -24,9 +24,10 @@ import {
   deleteUserRequest,
 } from '../../actions/user';
 import { useTranslation } from 'react-i18next';
+import styles from './styles';
 
 const Profile = () => {
-  const { t } = useTranslation('profile');
+  const { t } = useTranslation(['profile', 'common', 'validation']);
   const [user, setUser] = useState({});
   const [userRole, setUserRole] = useState('');
   const [groups, setGroups] = useState([]);
@@ -65,8 +66,8 @@ const Profile = () => {
   }, [userGroups]);
 
   const groupColumns = [
-    { field: 'label', name: 'Group Name', width: '30%' },
-    { field: 'description', name: 'Group description' },
+    { field: 'label', name: t('groups.groupName'), width: '30%' },
+    { field: 'description', name: t('groups.groupDescription') },
   ];
 
   const getUserRoles = () => {
@@ -109,8 +110,8 @@ const Profile = () => {
 
   const requestActions = [
     {
-      name: 'Cancel',
-      description: 'Cancel this request',
+      name: t('common:validationActions.cancel'),
+      description: t('requestsList.cancelRequest'),
       icon: 'trash',
       type: 'icon',
       onClick: onDeleteRequest,
@@ -118,9 +119,13 @@ const Profile = () => {
   ];
 
   const requestsColumns = [
-    { field: 'request_message', name: 'Message', width: '90%' },
-    { field: 'is_processed', name: 'Processed' },
-    { name: 'Delete', actions: requestActions },
+    {
+      field: 'request_message',
+      name: t('requestsList.requestsMessage'),
+      width: '85%',
+    },
+    { field: 'is_processed', name: t('requestsList.processed') },
+    { name: t('common:validationActions.cancel'), actions: requestActions },
   ];
 
   const getUserGroupLabels = () => {
@@ -144,6 +149,30 @@ const Profile = () => {
     );
   };
 
+  const onSendRoleRequest = () => {
+    if (selectedRole) {
+      const message = `The user ${user.username} (${user.email}) has made a request to get the role : ${selectedRole}.`;
+      createUserRequest(user.id, message);
+      sendMail('User role request', message);
+      alert(t('validation:requestSent'));
+    }
+    getUserRequests();
+  };
+
+  const onSendGroupRequest = () => {
+    const groupList = [];
+    if (userGroups) {
+      userGroups.forEach((group) => {
+        groupList.push(group.label);
+      });
+      const message = `The user ${user.username} (${user.email}) has made a request to be part of these groups : ${groupList}.`;
+      createUserRequest(user.id, message);
+      sendMail('User group request', message);
+      alert(t('validation:requestSent'));
+    }
+    getUserRequests();
+  };
+
   return (
     <>
       <EuiPageContent>
@@ -157,29 +186,28 @@ const Profile = () => {
         <EuiPageContentBody>
           <EuiForm component="form">
             <EuiTitle size="s">
-              <h3>Group list</h3>
+              <h3>{t('groups.groupsList')}</h3>
             </EuiTitle>
             <EuiFormRow fullWidth label="">
               <EuiBasicTable items={groups} columns={groupColumns} />
             </EuiFormRow>
             <EuiSpacer size="l" />
             <EuiTitle size="s">
-              <h3>Requests list</h3>
+              <h3>{t('requestsList.requestsList')}</h3>
             </EuiTitle>
             <EuiFormRow fullWidth label="">
               <EuiBasicTable items={userRequests} columns={requestsColumns} />
             </EuiFormRow>
             <EuiSpacer size="l" />
             <EuiTitle size="s">
-              <h3>Request group assignment modifications</h3>
+              <h3>{t('groupRequests.requestGroupAssignment')}</h3>
             </EuiTitle>
             {getUserGroupLabels() ? (
-              <p>
-                You currently belong to (or have a pending demand for) these groups :{' '}
-                {getUserGroupLabels()}{' '}
-              </p>
+              <p
+                style={styles.currentRoleOrGroupText}
+              >{`${t('groupRequests.currentGroups')} ${getUserGroupLabels()}`}</p>
             ) : (
-              <p>You currently belong to no group</p>
+              <p>{t('groupRequests.noGroup')}</p>
             )}
             <EuiFormRow error={valueError} isInvalid={valueError !== undefined}>
               <EuiComboBox
@@ -196,28 +224,20 @@ const Profile = () => {
             <EuiSpacer size="m" />
             <EuiButton
               onClick={() => {
-                if (userGroups) {
-                  const groupList = [];
-                  userGroups.forEach((group) => {
-                    groupList.push(group.label);
-                  });
-                  const message = `The user ${user.username} (${user.email}) has made a request to be part of these groups : ${groupList}.`;
-                  createUserRequest(user.id, message);
-                  sendMail('User group request', message);
-                  alert('Your group request has been sent to the administrators.');
-                }
-                getUserRequests();
+                onSendGroupRequest();
               }}
               fill
             >
-              Send request
+              {t('common:validationActions.send')}
             </EuiButton>
             <EuiSpacer size="l" />
             <EuiTitle size="s">
-              <h3>Request an application role</h3>
+              <h3>{t('roleRequests.requestRoleAssignment')}</h3>
             </EuiTitle>
             {userRole ? (
-              <p>Your current role is (or have a pending demand for) {userRole}</p>
+              <p
+                style={styles.currentRoleOrGroupText}
+              >{`${t('roleRequests.currentRole')} ${userRole}`}</p>
             ) : (
               <></>
             )}
@@ -234,17 +254,11 @@ const Profile = () => {
             <EuiSpacer size="m" />
             <EuiButton
               onClick={() => {
-                if (selectedRole) {
-                  const message = `The user ${user.username} (${user.email}) has made a request to get the role : ${selectedRole}.`;
-                  createUserRequest(user.id, message);
-                  sendMail('User role request', message);
-                  alert('Your role request has been sent to the administrators.');
-                }
-                getUserRequests();
+                onSendRoleRequest();
               }}
               fill
             >
-              Send request
+              {t('common:validationActions.send')}
             </EuiButton>
           </EuiForm>
         </EuiPageContentBody>
diff --git a/src/pages/profile/styles.js b/src/pages/profile/styles.js
new file mode 100644
index 0000000..4c2776e
--- /dev/null
+++ b/src/pages/profile/styles.js
@@ -0,0 +1,8 @@
+const style = {
+  currentRoleOrGroupText: {
+    marginTop: '10px',
+    marginBottom: '10px',
+  },
+};
+
+export default style;
-- 
GitLab


From 1eaea0274d3c0855ef1a9397fc75c6fda3b6d6a3 Mon Sep 17 00:00:00 2001
From: rbisson <remi.bisson@inrae.fr>
Date: Tue, 7 May 2024 11:28:30 +0200
Subject: [PATCH 09/17] [i18n config] updated default namespace to correct bug

---
 src/i18n.js | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/i18n.js b/src/i18n.js
index 65a0362..236acc5 100644
--- a/src/i18n.js
+++ b/src/i18n.js
@@ -8,7 +8,8 @@ i18n
   .init({
     lng: 'fr',
     fallbackLng: 'fr',
-    defaultNS: 'home',
+    ns: 'common',
+    defaultNS: 'common',
     debug: true,
     load: 'languageOnly',
     loadPath: 'locales/{{lng}}/{{ns}}.json',
-- 
GitLab


From b41b40819be9fca874d4e647732f950a86584f43 Mon Sep 17 00:00:00 2001
From: rbisson <remi.bisson@inrae.fr>
Date: Tue, 7 May 2024 11:30:58 +0200
Subject: [PATCH 10/17] [search]: divided basic search and advanced search into
 separate components

---
 src/pages/results/Results.js                  |    2 +-
 .../search/AdvancedSearch/AdvancedSearch.js   | 1627 +++++++++++++++
 src/pages/search/BasicSearch/BasicSearch.js   |   89 +
 src/pages/search/Search.js                    | 1795 +----------------
 src/pages/search/styles.js                    |   15 +
 5 files changed, 1818 insertions(+), 1710 deletions(-)
 create mode 100644 src/pages/search/AdvancedSearch/AdvancedSearch.js
 create mode 100644 src/pages/search/BasicSearch/BasicSearch.js
 create mode 100644 src/pages/search/styles.js

diff --git a/src/pages/results/Results.js b/src/pages/results/Results.js
index 0c61fce..e1dd381 100644
--- a/src/pages/results/Results.js
+++ b/src/pages/results/Results.js
@@ -38,7 +38,7 @@ const changeFlyoutState = (array, index, value, defaultValue) => {
   return newArray;
 };
 
-const Results = (searchResults, search, basicSearch) => {
+const Results = ({ searchResults, search, basicSearch }) => {
   const { t } = useTranslation('results');
   const [resultsCol, setResultsCol] = useState([]);
   const [results, setResults] = useState([]);
diff --git a/src/pages/search/AdvancedSearch/AdvancedSearch.js b/src/pages/search/AdvancedSearch/AdvancedSearch.js
new file mode 100644
index 0000000..f8db71a
--- /dev/null
+++ b/src/pages/search/AdvancedSearch/AdvancedSearch.js
@@ -0,0 +1,1627 @@
+import {
+  EuiButton,
+  EuiButtonEmpty,
+  EuiButtonIcon,
+  EuiComboBox,
+  EuiFieldText,
+  EuiFlexGroup,
+  EuiFlexItem,
+  EuiForm,
+  EuiFormRow,
+  EuiGlobalToastList,
+  EuiHealth,
+  EuiIcon,
+  EuiModal,
+  EuiModalBody,
+  EuiModalFooter,
+  EuiModalHeader,
+  EuiModalHeaderTitle,
+  EuiOverlayMask,
+  EuiPanel,
+  EuiPopover,
+  EuiPopoverFooter,
+  EuiPopoverTitle,
+  EuiProgress,
+  EuiRadioGroup,
+  EuiSelect,
+  EuiSpacer,
+  EuiSwitch,
+  EuiTextArea,
+  EuiTextColor,
+  EuiTitle,
+} from '@elastic/eui';
+import React from 'react';
+import {
+  changeNameToLabel,
+  createAdvancedQueriesBySource,
+  getFieldsBySection,
+  getSections,
+  removeArrayElement,
+  SearchField,
+  updateArrayElement,
+  updateSearchFieldValues,
+} from '../../../Utils';
+import { getQueryCount, searchQuery } from '../../../actions/source';
+import { DateOptions, NumericOptions, Operators } from '../Data';
+import TextField from '@material-ui/core/TextField';
+import { addUserHistory } from '../../../actions/user';
+import { fetchHistory } from '../Search';
+
+const updateSources = (
+  searchFields,
+  sources,
+  setSelectedSources,
+  setAvailableSources
+) => {
+  let updatedSources = [];
+  let availableSources = [];
+  let noPrivateField = true;
+  //search for policy fields to filter sources
+  searchFields.forEach((field) => {
+    if (field.isValidated) {
+      //if sources haven't already been filtered
+      if (noPrivateField && !updatedSources.length) {
+        availableSources = sources;
+      } else {
+        availableSources = updatedSources;
+      }
+      updatedSources = [];
+      field.sources.forEach((sourceId) => {
+        noPrivateField = false;
+        const source = availableSources.find((src) => src.id === sourceId);
+        if (source && !updatedSources.includes(source)) updatedSources.push(source);
+      });
+    }
+  });
+  setSelectedSources(updatedSources);
+  if (noPrivateField && !updatedSources.length) {
+    setAvailableSources(sources);
+  } else {
+    setAvailableSources(updatedSources);
+  }
+};
+
+const fieldValuesToString = (field) => {
+  let strValues = '';
+  switch (field.type) {
+    case 'Numeric':
+      field.values.forEach((element) => {
+        switch (element.option) {
+          case 'between':
+            strValues = `${strValues} ${element.value1} <= ${field.name} <= ${element.value2} or `;
+            break;
+          default:
+            strValues = `${strValues} ${field.name} ${element.option} ${element.value1} or `;
+        }
+      });
+      if (strValues.endsWith('or '))
+        strValues = strValues.substring(0, strValues.length - 4);
+      break;
+    case 'Date':
+      field.values.forEach((element) => {
+        switch (element.option) {
+          case 'between':
+            strValues = `${strValues} ${element.startDate} <= ${field.name} <= ${element.endDate} or `;
+            break;
+          default:
+            strValues = `${strValues} ${field.name} ${element.option} ${element.startDate} or `;
+        }
+      });
+      if (strValues.endsWith(' or '))
+        strValues = strValues.substring(0, strValues.length - 4);
+      break;
+    case 'List':
+      strValues = `${strValues} ${field.name} = `;
+      field.values.forEach((element) => {
+        strValues = `${strValues} ${element.label}, `;
+      });
+      if (strValues.endsWith(', '))
+        strValues = strValues.substring(0, strValues.length - 2);
+      break;
+    //type : text
+    default:
+      strValues = `${strValues} ${field.name} = ${field.values}`;
+  }
+  return strValues;
+};
+
+const addHistory = (
+  kcID,
+  search,
+  searchName,
+  searchFields,
+  searchDescription,
+  setUserHistory
+) => {
+  addUserHistory(
+    sessionStorage.getItem('user_id'),
+    search,
+    searchName,
+    searchFields,
+    searchDescription
+  ).then(() => {
+    fetchHistory(setUserHistory);
+  });
+};
+
+const updateSearch = (setSearch, searchFields, selectedOperatorId, setSearchCount) => {
+  let searchText = '';
+  searchFields.forEach((field) => {
+    if (field.isValidated) {
+      searchText =
+        searchText +
+        `{${fieldValuesToString(field)} } ${Operators[selectedOperatorId].value.toUpperCase()} `;
+    }
+  });
+  if (searchText.endsWith(' AND ')) {
+    searchText = searchText.substring(0, searchText.length - 5);
+  } else if (searchText.endsWith(' OR ')) {
+    searchText = searchText.substring(0, searchText.length - 4);
+  }
+  setSearchCount();
+  setSearch(searchText);
+};
+
+const HistorySelect = (
+  sources,
+  setAvailableSources,
+  setSelectedSources,
+  setSearch,
+  searchFields,
+  selectedOperatorId,
+  userHistory,
+  setUserHistory,
+  setSearchFields,
+  setSearchCount,
+  setFieldCount,
+  selectedSavedSearch,
+  setSelectedSavedSearch,
+  historySelectError,
+  setHistorySelectError
+) => {
+  if (Object.keys(userHistory).length !== 0) {
+    const onHistoryChange = (selectedSavedSearch) => {
+      setHistorySelectError(undefined);
+      if (!!selectedSavedSearch[0].query) {
+        setSelectedSavedSearch(selectedSavedSearch);
+        setSearch(selectedSavedSearch[0].query);
+        setSearchCount();
+        setFieldCount([]);
+      }
+      if (!!selectedSavedSearch[0].ui_structure) {
+        updateSources(
+          selectedSavedSearch[0].ui_structure,
+          sources,
+          setSelectedSources,
+          setAvailableSources
+        );
+        setSearchFields(selectedSavedSearch[0].ui_structure);
+      }
+    };
+
+    const onHistorySearchChange = (value, hasMatchingOptions) => {
+      setHistorySelectError(
+        value.length === 0 || hasMatchingOptions
+          ? undefined
+          : `"${value}" is not a valid option`
+      );
+    };
+
+    return (
+      <>
+        <EuiFormRow
+          error={historySelectError}
+          isInvalid={historySelectError !== undefined}
+        >
+          <EuiComboBox
+            placeholder={'searchHistory.placeholder'}
+            singleSelection={{ asPlainText: true }}
+            options={userHistory}
+            selectedOptions={selectedSavedSearch}
+            onChange={onHistoryChange}
+            onSearchChange={onHistorySearchChange}
+          />
+        </EuiFormRow>
+      </>
+    );
+  }
+};
+
+const SearchBar = ({
+  isLoading,
+  setIsLoading,
+  search,
+  setSearch,
+  setSearchResults,
+  searchFields,
+  setSearchFields,
+  searchName,
+  setSearchName,
+  searchDescription,
+  setSearchDescription,
+  readOnlyQuery,
+  setReadOnlyQuery,
+  selectedSources,
+  setSelectedSources,
+  availableSources,
+  setAvailableSources,
+  standardFields,
+  sources,
+  setSelectedTabNumber,
+  searchCount,
+  setSearchCount,
+  setFieldCount,
+  isSaveSearchModalOpen,
+  setIsSaveSearchModalOpen,
+  userHistory,
+  setUserHistory,
+  selectedSavedSearch,
+  setSelectedSavedSearch,
+  historySelectError,
+  setHistorySelectError,
+  selectedOperatorId,
+  createEditableQueryToast,
+}) => {
+  const closeSaveSearchModal = () => {
+    setIsSaveSearchModalOpen(false);
+  };
+
+  const onSendAdvancedSearch = () => {
+    if (search.trim()) {
+      setIsLoading(true);
+      const queriesWithIndices = createAdvancedQueriesBySource(
+        standardFields,
+        search,
+        selectedSources,
+        availableSources
+      );
+      searchQuery(queriesWithIndices).then((result) => {
+        setSearchResults(result);
+        setSelectedTabNumber(1);
+        setIsLoading(false);
+      });
+    }
+  };
+
+  let saveSearchModal;
+
+  if (isSaveSearchModalOpen) {
+    saveSearchModal = (
+      <EuiOverlayMask>
+        <EuiModal onClose={closeSaveSearchModal} initialFocus="[name=searchName]">
+          <EuiModalHeader>
+            <EuiModalHeaderTitle>Save search</EuiModalHeaderTitle>
+          </EuiModalHeader>
+
+          <EuiModalBody>
+            <EuiForm>
+              <EuiFormRow label="Search name">
+                <EuiFieldText
+                  name="searchName"
+                  value={searchName}
+                  onChange={(e) => {
+                    setSearchName(e.target.value);
+                  }}
+                />
+              </EuiFormRow>
+              <EuiFormRow label="Description (optional)">
+                <EuiTextArea
+                  value={searchDescription}
+                  onChange={(e) => setSearchDescription(e.target.value)}
+                  placeholder="Search description..."
+                  fullWidth
+                  compressed
+                />
+              </EuiFormRow>
+            </EuiForm>
+          </EuiModalBody>
+
+          <EuiModalFooter>
+            <EuiButtonEmpty
+              onClick={() => {
+                closeSaveSearchModal();
+              }}
+            >
+              Cancel
+            </EuiButtonEmpty>
+            <EuiButton
+              onClick={() => {
+                if (!!searchName) {
+                  addHistory(
+                    sessionStorage.getItem('user_id'),
+                    search,
+                    searchName,
+                    searchFields,
+                    searchDescription,
+                    setUserHistory
+                  );
+                  setSearchName('');
+                  setSearchDescription('');
+                  closeSaveSearchModal();
+                }
+              }}
+              fill
+            >
+              Save
+            </EuiButton>
+          </EuiModalFooter>
+        </EuiModal>
+      </EuiOverlayMask>
+    );
+  }
+
+  return (
+    <>
+      <EuiFlexGroup>
+        <EuiFlexItem>
+          <EuiTextArea
+            readOnly={readOnlyQuery}
+            value={search}
+            onChange={(e) => setSearch(e.target.value)}
+            placeholder="Add fields..."
+            fullWidth
+          />
+        </EuiFlexItem>
+        <EuiFlexItem grow={false}>
+          <EuiButton
+            size="s"
+            fill
+            onClick={() => {
+              onSendAdvancedSearch();
+            }}
+          >
+            Search
+          </EuiButton>
+          <EuiSpacer size="s" />
+          {isNaN(searchCount) ? (
+            <></>
+          ) : (
+            <>
+              <EuiTextColor
+                color="secondary"
+                style={{ display: 'flex', justifyContent: 'center' }}
+              >
+                {searchCount} {searchCount === 1 ? 'result' : 'results'}
+              </EuiTextColor>
+              <EuiSpacer size="s" />
+            </>
+          )}
+          <EuiButton
+            size="s"
+            onClick={() => {
+              if (!!search) {
+                const queriesWithIndices = createAdvancedQueriesBySource(
+                  standardFields,
+                  search,
+                  selectedSources,
+                  availableSources
+                );
+                getQueryCount(queriesWithIndices).then((result) => {
+                  if (result || result === 0) setSearchCount(result);
+                });
+              }
+            }}
+          >
+            Count results
+          </EuiButton>
+          <EuiSpacer size="s" />
+          <EuiButton
+            size="s"
+            onClick={() => {
+              setIsSaveSearchModalOpen(true);
+            }}
+          >
+            Save search
+          </EuiButton>
+          {saveSearchModal}
+          <EuiSpacer size="s" />
+          <EuiSwitch
+            compressed
+            label={'Editable'}
+            checked={!readOnlyQuery}
+            onChange={() => {
+              setReadOnlyQuery(!readOnlyQuery);
+              if (readOnlyQuery) {
+                createEditableQueryToast();
+              }
+            }}
+          />
+        </EuiFlexItem>
+      </EuiFlexGroup>
+      {isLoading && (
+        <EuiFlexGroup>
+          <EuiFlexItem>
+            <EuiProgress postion="fixed" size="l" color="accent" />
+          </EuiFlexItem>
+        </EuiFlexGroup>
+      )}
+      <EuiSpacer size="s" />
+      <EuiFlexGroup>
+        <EuiFlexItem>
+          {HistorySelect(
+            sources,
+            setAvailableSources,
+            setSelectedSources,
+            setSearch,
+            searchFields,
+            selectedOperatorId,
+            userHistory,
+            setUserHistory,
+            setSearchFields,
+            setSearchCount,
+            setFieldCount,
+            selectedSavedSearch,
+            setSelectedSavedSearch,
+            historySelectError,
+            setHistorySelectError
+          )}
+        </EuiFlexItem>
+      </EuiFlexGroup>
+    </>
+  );
+};
+
+const PopoverSelect = (
+  standardFields,
+  setStandardFields,
+  searchFields,
+  setSearchFields,
+  selectedField,
+  setSelectedField,
+  selectedSection,
+  setSelectedSection,
+  isPopoverSelectOpen,
+  setIsPopoverSelectOpen
+) => {
+  const handleAddfield = () => {
+    if (!!selectedField[0]) {
+      const field = standardFields.find(
+        (item) =>
+          item.field_name.replace(/_|\./g, ' ') ===
+          selectedSection[0].label + ' ' + selectedField[0].label
+      );
+      switch (field.field_type) {
+        case 'Text':
+          setSearchFields([
+            ...searchFields,
+            new SearchField(field.field_name, field.field_type, '', false, field.sources),
+          ]);
+          break;
+        case 'List':
+          setSearchFields([
+            ...searchFields,
+            new SearchField(field.field_name, field.field_type, [], false, field.sources),
+          ]);
+          break;
+        default:
+          setSearchFields([
+            ...searchFields,
+            new SearchField(
+              field.field_name,
+              field.field_type,
+              [{}],
+              false,
+              field.sources
+            ),
+          ]);
+      }
+    }
+  };
+
+  const selectField = () => {
+    const renderOption = (option, searchValue, contentClassName) => {
+      const { label, color } = option;
+      return <EuiHealth color={color}>{label}</EuiHealth>;
+    };
+    if (selectedSection.length) {
+      return (
+        <>
+          <EuiComboBox
+            placeholder="Select a field"
+            singleSelection={{ asPlainText: true }}
+            options={getFieldsBySection(standardFields, selectedSection[0])}
+            selectedOptions={selectedField}
+            onChange={(selected) => setSelectedField(selected)}
+            isClearable={true}
+            renderOption={renderOption}
+          />
+          <EuiPopoverFooter>
+            <EuiButton
+              size="s"
+              onClick={() => {
+                handleAddfield();
+                setIsPopoverSelectOpen(false);
+                setSelectedSection([]);
+                setSelectedField([]);
+              }}
+            >
+              Add this field
+            </EuiButton>
+          </EuiPopoverFooter>
+        </>
+      );
+    }
+  };
+
+  return (
+    <EuiPopover
+      panelPaddingSize="s"
+      button={
+        <EuiButton
+          iconType="listAdd"
+          iconSide="left"
+          onClick={() => setIsPopoverSelectOpen(!isPopoverSelectOpen)}
+        >
+          Add field
+        </EuiButton>
+      }
+      isOpen={isPopoverSelectOpen}
+      closePopover={() => setIsPopoverSelectOpen(false)}
+    >
+      <div style={{ width: 'intrinsic', minWidth: 240 }}>
+        <EuiPopoverTitle>Select a field</EuiPopoverTitle>
+        <EuiComboBox
+          placeholder="Select a section"
+          singleSelection={{ asPlainText: true }}
+          options={getSections(standardFields)}
+          selectedOptions={selectedSection}
+          onChange={(selected) => {
+            setSelectedSection(selected);
+            setSelectedField([]);
+          }}
+          isClearable={false}
+        />
+      </div>
+      {selectField()}
+    </EuiPopover>
+  );
+};
+
+const PopoverValueContent = (
+  index,
+  standardFields,
+  setStandardFields,
+  searchFields,
+  setSearchFields,
+  valueError,
+  setValueError,
+  search,
+  setSearch,
+  setSearchCount,
+  fieldCount,
+  setFieldCount,
+  isPopoverValueOpen,
+  setIsPopoverValueOpen,
+  selectedOperatorId,
+  datePickerStyles,
+  createPolicyToast,
+  selectedSources,
+  setSelectedSources,
+  availableSources,
+  setAvailableSources
+) => {
+  const onValueSearchChange = (value, hasMatchingOptions) => {
+    setValueError(
+      value.length === 0 || hasMatchingOptions
+        ? undefined
+        : `"${value}" is not a valid option`
+    );
+  };
+
+  const validateFieldValues = () => {
+    let fieldValues;
+    if (Array.isArray(searchFields[index].values)) {
+      fieldValues = [];
+      searchFields[index].values.forEach((value) => {
+        if (!!value) {
+          fieldValues.push(value);
+        }
+      });
+    } else {
+      fieldValues = searchFields[index].values;
+    }
+
+    const updatedSearchFields = updateArrayElement(
+      searchFields,
+      index,
+      new SearchField(
+        searchFields[index].name,
+        searchFields[index].type,
+        fieldValues,
+        true,
+        searchFields[index].sources
+      )
+    );
+    setSearchFields(updatedSearchFields);
+    updateSearch(setSearch, updatedSearchFields, selectedOperatorId, setSearchCount);
+    setFieldCount(updateArrayElement(fieldCount, index));
+    if (searchFields[index].sources.length) {
+      const filteredSources = [];
+      searchFields[index].sources.forEach((sourceId) => {
+        let source;
+        if (selectedSources.length) {
+          source = selectedSources.find((src) => src.id === sourceId);
+        } else {
+          source = availableSources.find((src) => src.id === sourceId);
+        }
+        if (source) {
+          filteredSources.push(source);
+        }
+      });
+      setAvailableSources(filteredSources);
+      setSelectedSources(filteredSources);
+      createPolicyToast();
+    }
+  };
+
+  const invalidateFieldValues = () => {
+    const updatedSearchFields = updateArrayElement(
+      searchFields,
+      index,
+      new SearchField(
+        searchFields[index].name,
+        searchFields[index].type,
+        searchFields[index].values,
+        false,
+        searchFields[index].sources
+      )
+    );
+    setSearchFields(updatedSearchFields);
+    updateSearch(setSearch, updatedSearchFields, selectedOperatorId, setSearchCount);
+  };
+
+  const ValuePopoverFooter = (i) => {
+    if (i === searchFields[index].values.length - 1) {
+      return (
+        <EuiPopoverFooter>
+          <EuiButton
+            size="s"
+            onClick={() => {
+              setSearchFields(
+                updateArrayElement(
+                  searchFields,
+                  index,
+                  new SearchField(
+                    searchFields[index].name,
+                    searchFields[index].type,
+                    [...searchFields[index].values, {}],
+                    false,
+                    searchFields[index].sources
+                  )
+                )
+              );
+            }}
+          >
+            Add value
+          </EuiButton>
+          <EuiButton
+            size="s"
+            style={{ float: 'right' }}
+            onClick={() => {
+              validateFieldValues();
+              setIsPopoverValueOpen(updateArrayElement(isPopoverValueOpen, index, false));
+            }}
+          >
+            Validate
+          </EuiButton>
+        </EuiPopoverFooter>
+      );
+    }
+  };
+
+  const addFieldValue = (i, selectedOption) => {
+    setSearchFields(
+      updateSearchFieldValues(
+        searchFields,
+        index,
+        updateArrayElement(searchFields[index].values, i, { option: selectedOption })
+      )
+    );
+  };
+
+  const getListFieldValues = () => {
+    const listFieldValues = [];
+    standardFields
+      .find((item) => item.field_name === searchFields[index].name)
+      .values.split(', ')
+      .sort()
+      .forEach((element) => {
+        listFieldValues.push({ label: element });
+      });
+    return listFieldValues;
+  };
+
+  switch (searchFields[index].type) {
+    case 'Text':
+      return (
+        <>
+          <EuiFlexItem>
+            <EuiFieldText
+              placeholder={'Type values'}
+              value={searchFields[index].values}
+              onChange={(e) =>
+                setSearchFields(
+                  updateSearchFieldValues(searchFields, index, e.target.value)
+                )
+              }
+            />
+          </EuiFlexItem>
+          <EuiPopoverFooter>
+            <EuiButton
+              size="s"
+              style={{ float: 'right' }}
+              onClick={() => {
+                validateFieldValues();
+                setIsPopoverValueOpen(
+                  updateArrayElement(isPopoverValueOpen, index, false)
+                );
+              }}
+            >
+              Validate
+            </EuiButton>
+          </EuiPopoverFooter>
+        </>
+      );
+    case 'List':
+      return (
+        <>
+          <EuiFormRow error={valueError} isInvalid={valueError !== undefined}>
+            <EuiComboBox
+              placeholder={'Select values'}
+              options={getListFieldValues()}
+              selectedOptions={searchFields[index].values}
+              onChange={(selectedOptions) => {
+                setValueError(undefined);
+                setSearchFields(
+                  updateSearchFieldValues(searchFields, index, selectedOptions)
+                );
+              }}
+              onSearchChange={onValueSearchChange}
+            />
+          </EuiFormRow>
+          <EuiPopoverFooter>
+            <EuiButton
+              size="s"
+              style={{ float: 'right' }}
+              onClick={() => {
+                validateFieldValues();
+                setIsPopoverValueOpen(
+                  updateArrayElement(isPopoverValueOpen, index, false)
+                );
+              }}
+            >
+              Validate
+            </EuiButton>
+          </EuiPopoverFooter>
+        </>
+      );
+    case 'Numeric':
+      const NumericValues = (i) => {
+        if (!!searchFields[index].values[i].option) {
+          switch (searchFields[index].values[i].option) {
+            case 'between':
+              return (
+                <>
+                  <EuiFlexItem>
+                    <EuiFieldText
+                      placeholder={'1st value'}
+                      value={searchFields[index].values[i].value1}
+                      onChange={(e) => {
+                        setSearchFields(
+                          updateSearchFieldValues(
+                            searchFields,
+                            index,
+                            updateArrayElement(searchFields[index].values, i, {
+                              option: searchFields[index].values[i].option,
+                              value1: e.target.value,
+                              value2: searchFields[index].values[i].value2,
+                            })
+                          )
+                        );
+                      }}
+                    />
+                  </EuiFlexItem>
+                  <EuiFlexItem>
+                    <EuiFieldText
+                      placeholder={'2nd value'}
+                      value={searchFields[index].values[i].value2}
+                      onChange={(e) =>
+                        setSearchFields(
+                          updateSearchFieldValues(
+                            searchFields,
+                            index,
+                            updateArrayElement(searchFields[index].values, i, {
+                              option: searchFields[index].values[i].option,
+                              value1: searchFields[index].values[i].value1,
+                              value2: e.target.value,
+                            })
+                          )
+                        )
+                      }
+                    />
+                  </EuiFlexItem>
+                  {ValuePopoverFooter(i)}
+                </>
+              );
+
+            default:
+              return (
+                <>
+                  <EuiFlexItem>
+                    <EuiFieldText
+                      placeholder={'Type value'}
+                      value={searchFields[index].values[i].value1}
+                      onChange={(e) => {
+                        setSearchFields(
+                          updateSearchFieldValues(
+                            searchFields,
+                            index,
+                            updateArrayElement(searchFields[index].values, i, {
+                              option: searchFields[index].values[i].option,
+                              value1: e.target.value,
+                              value2: searchFields[index].values[i].value2,
+                            })
+                          )
+                        );
+                      }}
+                    />
+                  </EuiFlexItem>
+                  {ValuePopoverFooter(i)}
+                </>
+              );
+          }
+        }
+      };
+
+      return (
+        <>
+          {searchFields[index].values.map((value, i) => (
+            <div key={i}>
+              <EuiSelect
+                hasNoInitialSelection
+                id="Select an option"
+                options={NumericOptions}
+                value={searchFields[index].values[i].option}
+                onChange={(e) => {
+                  addFieldValue(i, e.target.value);
+                  invalidateFieldValues();
+                }}
+              />
+              {NumericValues(i)}
+            </div>
+          ))}
+        </>
+      );
+    case 'Date':
+      const SelectDates = (i) => {
+        if (!!searchFields[index].values[i].option) {
+          switch (searchFields[index].values[i].option) {
+            case 'between':
+              return (
+                <>
+                  <form className={datePickerStyles.container} noValidate>
+                    <TextField
+                      label="between"
+                      type="date"
+                      defaultValue={
+                        !!searchFields[index].values[i].startDate
+                          ? searchFields[index].values[i].startDate
+                          : Date.now()
+                      }
+                      className={datePickerStyles.textField}
+                      InputLabelProps={{
+                        shrink: true,
+                      }}
+                      onChange={(e) =>
+                        setSearchFields(
+                          updateSearchFieldValues(
+                            searchFields,
+                            index,
+                            updateArrayElement(searchFields[index].values, i, {
+                              option: searchFields[index].values[i].option,
+                              startDate: e.target.value,
+                              endDate: searchFields[index].values[i].endDate,
+                            })
+                          )
+                        )
+                      }
+                    />
+                  </form>
+                  <form className={datePickerStyles.container} noValidate>
+                    <TextField
+                      label="and"
+                      type="date"
+                      defaultValue={
+                        !!searchFields[index].values[i].endDate
+                          ? searchFields[index].values[i].endDate
+                          : Date.now()
+                      }
+                      className={datePickerStyles.textField}
+                      InputLabelProps={{
+                        shrink: true,
+                      }}
+                      onChange={(e) =>
+                        setSearchFields(
+                          updateSearchFieldValues(
+                            searchFields,
+                            index,
+                            updateArrayElement(searchFields[index].values, i, {
+                              option: searchFields[index].values[i].option,
+                              startDate: searchFields[index].values[i].startDate,
+                              endDate: e.target.value,
+                            })
+                          )
+                        )
+                      }
+                    />
+                  </form>
+                  {ValuePopoverFooter(i)}
+                </>
+              );
+
+            default:
+              return (
+                <>
+                  <form className={datePickerStyles.container} noValidate>
+                    <TextField
+                      type="date"
+                      defaultValue={
+                        !!searchFields[index].values[i].startDate
+                          ? searchFields[index].values[i].startDate
+                          : Date.now()
+                      }
+                      className={datePickerStyles.textField}
+                      InputLabelProps={{
+                        shrink: true,
+                      }}
+                      onChange={(e) =>
+                        setSearchFields(
+                          updateSearchFieldValues(
+                            searchFields,
+                            index,
+                            updateArrayElement(searchFields[index].values, i, {
+                              option: searchFields[index].values[i].option,
+                              startDate: e.target.value,
+                              endDate: Date.now(),
+                            })
+                          )
+                        )
+                      }
+                    />
+                  </form>
+                  {ValuePopoverFooter(i)}
+                </>
+              );
+          }
+        }
+      };
+
+      return (
+        <>
+          {searchFields[index].values.map((value, i) => (
+            <div key={i}>
+              <EuiSelect
+                hasNoInitialSelection
+                id="Select an option"
+                options={DateOptions}
+                value={searchFields[index].values[i].option}
+                onChange={(e) => {
+                  addFieldValue(i, e.target.value);
+                  invalidateFieldValues();
+                }}
+              />
+              {SelectDates(i)}
+            </div>
+          ))}
+        </>
+      );
+    default:
+  }
+};
+
+const PopoverValueButton = (
+  index,
+  standardFields,
+  setStandardFields,
+  searchFields,
+  setSearchFields,
+  isPopoverValueOpen,
+  setIsPopoverValueOpen,
+  valueError,
+  setValueError,
+  search,
+  setSearch,
+  setSearchCount,
+  fieldCount,
+  setFieldCount,
+  selectedOperatorId,
+  datePickerStyles,
+  createPolicyToast,
+  selectedSources,
+  setSelectedSources,
+  availableSources,
+  setAvailableSources
+) => {
+  return (
+    <EuiPopover
+      panelPaddingSize="s"
+      button={
+        <EuiButtonIcon
+          size="s"
+          color="primary"
+          onClick={() =>
+            setIsPopoverValueOpen(
+              updateArrayElement(isPopoverValueOpen, index, !isPopoverValueOpen[index])
+            )
+          }
+          iconType="documentEdit"
+          title="Give field values"
+        />
+      }
+      isOpen={isPopoverValueOpen[index]}
+      closePopover={() =>
+        setIsPopoverValueOpen(updateArrayElement(isPopoverValueOpen, index, false))
+      }
+    >
+      <div style={{ width: 240 }}>
+        {PopoverValueContent(
+          index,
+          standardFields,
+          setStandardFields,
+          searchFields,
+          setSearchFields,
+          valueError,
+          setValueError,
+          search,
+          setSearch,
+          setSearchCount,
+          fieldCount,
+          setFieldCount,
+          isPopoverValueOpen,
+          setIsPopoverValueOpen,
+          selectedOperatorId,
+          datePickerStyles,
+          createPolicyToast,
+          selectedSources,
+          setSelectedSources,
+          availableSources,
+          setAvailableSources
+        )}
+      </div>
+    </EuiPopover>
+  );
+};
+
+const FieldsPanel = (
+  standardFields,
+  setStandardFields,
+  searchFields,
+  setSearchFields,
+  selectedField,
+  setSelectedField,
+  selectedSection,
+  setSelectedSection,
+  isPopoverSelectOpen,
+  setIsPopoverSelectOpen,
+  isPopoverValueOpen,
+  setIsPopoverValueOpen,
+  valueError,
+  setValueError,
+  search,
+  setSearch,
+  setSearchCount,
+  selectedOperatorId,
+  setSelectedOperatorId,
+  fieldCount,
+  setFieldCount,
+  availableSources,
+  setAvailableSources,
+  selectedSources,
+  setSelectedSources,
+  sources,
+  datePickerStyles,
+  createPolicyToast
+) => {
+  const countFieldValues = (field, index) => {
+    const fieldStr = `{${fieldValuesToString(field)}}`;
+    const queriesWithIndices = createAdvancedQueriesBySource(
+      standardFields,
+      fieldStr,
+      selectedSources,
+      availableSources
+    );
+    getQueryCount(queriesWithIndices).then((result) => {
+      if (result || result === 0)
+        setFieldCount(updateArrayElement(fieldCount, index, result));
+    });
+  };
+
+  const handleRemoveField = (index) => {
+    const updatedSearchFields = removeArrayElement(searchFields, index);
+    setSearchFields(updatedSearchFields);
+    updateSources(updatedSearchFields, sources, setSelectedSources, setAvailableSources);
+    updateSearch(setSearch, updatedSearchFields, selectedOperatorId, setSearchCount);
+  };
+
+  const handleClearValues = (index) => {
+    let updatedSearchFields = [];
+    switch (searchFields[index].type) {
+      case 'Text':
+        updatedSearchFields = updateArrayElement(
+          searchFields,
+          index,
+          new SearchField(
+            searchFields[index].name,
+            searchFields[index].type,
+            '',
+            false,
+            searchFields[index].sources
+          )
+        );
+        break;
+      case 'List':
+        updatedSearchFields = updateArrayElement(
+          searchFields,
+          index,
+          new SearchField(
+            searchFields[index].name,
+            searchFields[index].type,
+            [],
+            false,
+            searchFields[index].sources
+          )
+        );
+        break;
+      default:
+        updatedSearchFields = updateArrayElement(
+          searchFields,
+          index,
+          new SearchField(
+            searchFields[index].name,
+            searchFields[index].type,
+            [{}],
+            false,
+            searchFields[index].sources
+          )
+        );
+    }
+    setSearchFields(updatedSearchFields);
+    updateSources(updatedSearchFields, sources, setSelectedSources, setAvailableSources);
+    setFieldCount(updateArrayElement(fieldCount, index));
+    updateSearch(setSearch, updatedSearchFields, selectedOperatorId, setSearchCount);
+  };
+
+  if (standardFields === []) {
+    return <h2>Loading user fields...</h2>;
+  }
+
+  return (
+    <>
+      <EuiTitle size="xs">
+        <h2>Field search</h2>
+      </EuiTitle>
+      <EuiPanel paddingSize="m">
+        <EuiFlexGroup direction="column">
+          {searchFields.map((field, index) => (
+            <EuiPanel key={'field' + index} paddingSize="s">
+              <EuiFlexItem grow={false}>
+                <EuiFlexGroup direction="row" alignItems="center">
+                  <EuiFlexItem grow={false}>
+                    <EuiButtonIcon
+                      size="s"
+                      color="danger"
+                      onClick={() => handleRemoveField(index)}
+                      iconType="indexClose"
+                      title="Remove field"
+                    />
+                  </EuiFlexItem>
+                  <EuiFlexItem>
+                    {field.isValidated ? (
+                      <>
+                        {field.sources.length ? (
+                          <EuiHealth color="danger">
+                            {fieldValuesToString(field).replace(/_|\./g, ' ')}
+                          </EuiHealth>
+                        ) : (
+                          <EuiHealth color="primary">
+                            {fieldValuesToString(field).replace(/_|\./g, ' ')}
+                          </EuiHealth>
+                        )}
+                      </>
+                    ) : (
+                      <>
+                        {field.sources.length ? (
+                          <EuiHealth color="danger">
+                            {field.name.replace(/_|\./g, ' ')}
+                          </EuiHealth>
+                        ) : (
+                          <EuiHealth color="primary">
+                            {field.name.replace(/_|\./g, ' ')}
+                          </EuiHealth>
+                        )}
+                      </>
+                    )}
+                  </EuiFlexItem>
+                  <EuiFlexItem grow={false}>
+                    {isNaN(fieldCount[index]) ? (
+                      <></>
+                    ) : (
+                      <>
+                        <EuiTextColor color="secondary">
+                          {fieldCount[index]}{' '}
+                          {fieldCount[index] === 1 ? 'result' : 'results'}
+                        </EuiTextColor>
+                      </>
+                    )}
+                  </EuiFlexItem>
+                  <EuiFlexItem grow={false}>
+                    {field.isValidated ? (
+                      <>
+                        <EuiButtonIcon
+                          size="s"
+                          onClick={() => countFieldValues(field, index)}
+                          iconType="number"
+                          title="Count results"
+                        />
+                      </>
+                    ) : (
+                      <></>
+                    )}
+                  </EuiFlexItem>
+                  <EuiFlexItem grow={false}>
+                    {field.isValidated ? (
+                      <>
+                        <EuiButtonIcon
+                          size="s"
+                          color="danger"
+                          onClick={() => handleClearValues(index)}
+                          iconType="trash"
+                          title="Clear values"
+                        />
+                      </>
+                    ) : (
+                      <></>
+                    )}
+                  </EuiFlexItem>
+                  <EuiFlexItem grow={false}>
+                    {PopoverValueButton(
+                      index,
+                      standardFields,
+                      setStandardFields,
+                      searchFields,
+                      setSearchFields,
+                      isPopoverValueOpen,
+                      setIsPopoverValueOpen,
+                      valueError,
+                      setValueError,
+                      search,
+                      setSearch,
+                      setSearchCount,
+                      fieldCount,
+                      setFieldCount,
+                      selectedOperatorId,
+                      datePickerStyles,
+                      createPolicyToast,
+                      selectedSources,
+                      setSelectedSources,
+                      availableSources,
+                      setAvailableSources
+                    )}
+                  </EuiFlexItem>
+                </EuiFlexGroup>
+              </EuiFlexItem>
+            </EuiPanel>
+          ))}
+        </EuiFlexGroup>
+        <EuiSpacer size="l" />
+        {PopoverSelect(
+          standardFields,
+          setStandardFields,
+          searchFields,
+          setSearchFields,
+          selectedField,
+          setSelectedField,
+          selectedSection,
+          setSelectedSection,
+          isPopoverSelectOpen,
+          setIsPopoverSelectOpen,
+          fieldCount,
+          setFieldCount,
+          selectedSources,
+          setSelectedSources
+        )}
+      </EuiPanel>
+      <EuiSpacer size="s" />
+      <EuiRadioGroup
+        options={Operators}
+        idSelected={selectedOperatorId}
+        onChange={(id) => {
+          setSelectedOperatorId(id);
+          updateSearch(setSearch, searchFields, id, setSearchCount);
+        }}
+        name="operators group"
+        legend={{
+          children: <span>Search option</span>,
+        }}
+      />
+    </>
+  );
+};
+
+const SourceSelect = (
+  availableSources,
+  selectedSources,
+  setSelectedSources,
+  sourceSelectError,
+  setSourceSelectError
+) => {
+  if (Object.keys(availableSources).length !== 0) {
+    availableSources.forEach((source) => {
+      if (source.name) {
+        source = changeNameToLabel(source);
+      }
+    });
+
+    const onSourceChange = (selectedOptions) => {
+      setSourceSelectError(undefined);
+      setSelectedSources(selectedOptions);
+    };
+
+    const onSourceSearchChange = (value, hasMatchingOptions) => {
+      setSourceSelectError(
+        value.length === 0 || hasMatchingOptions
+          ? undefined
+          : `"${value}" is not a valid option`
+      );
+    };
+    return (
+      <>
+        <EuiTitle size="xs">
+          <h2>Partner sources</h2>
+        </EuiTitle>
+        <EuiSpacer size="s" />
+        <EuiFlexItem>
+          <EuiFormRow
+            error={sourceSelectError}
+            isInvalid={sourceSelectError !== undefined}
+          >
+            <EuiComboBox
+              placeholder="By default, all sources are selected"
+              options={availableSources}
+              selectedOptions={selectedSources}
+              onChange={onSourceChange}
+              onSearchChange={onSourceSearchChange}
+            />
+          </EuiFormRow>
+        </EuiFlexItem>
+      </>
+    );
+  } else {
+    return (
+      <p>
+        <EuiIcon type="alert" color="danger" /> No source available !
+      </p>
+    );
+  }
+};
+
+const AdvancedSearch = ({
+  isLoading,
+  setIsLoading,
+  search,
+  setSearch,
+  searchResults,
+  setSearchResults,
+  searchFields,
+  setSearchFields,
+  searchName,
+  setSearchName,
+  searchDescription,
+  setSearchDescription,
+  readOnlyQuery,
+  setReadOnlyQuery,
+  selectedSources,
+  setSelectedSources,
+  availableSources,
+  setAvailableSources,
+  standardFields,
+  setStandardFields,
+  sources,
+  setSelectedTabNumber,
+  searchCount,
+  setSearchCount,
+  setFieldCount,
+  isReadOnlyModalOpen,
+  setIsReadOnlyModalOpen,
+  isSaveSearchModalOpen,
+  setIsSaveSearchModalOpen,
+  userHistory,
+  setUserHistory,
+  selectedSavedSearch,
+  setSelectedSavedSearch,
+  historySelectError,
+  setHistorySelectError,
+  selectedOperatorId,
+  notificationToasts,
+  setNotificationToasts,
+  setIsAdvancedSearch,
+  isAdvancedSearch,
+  selectedField,
+  selectedSection,
+  setSelectedField,
+  setSelectedSection,
+  isPopoverSelectOpen,
+  setIsPopoverSelectOpen,
+  setIsPopoverValueOpen,
+  isPopoverValueOpen,
+  valueError,
+  setValueError,
+  setSelectedOperatorId,
+  fieldCount,
+  sourceSelectError,
+  datePickerStyles,
+  setSourceSelectError,
+}) => {
+  const createPolicyToast = () => {
+    const toast = {
+      title: 'Policy field selected',
+      color: 'warning',
+      iconType: 'alert',
+      toastLifeTimeMs: 15000,
+      text: (
+        <>
+          <p>You selected a private field.</p>
+          <p>
+            Access to this field was granted for specific sources, which means that your
+            search will be restricted to those.
+          </p>
+          <p>Please check the sources list before searching.</p>
+        </>
+      ),
+    };
+    setNotificationToasts(notificationToasts.concat(toast));
+  };
+
+  const createEditableQueryToast = () => {
+    const toast = {
+      title: 'Proceed with caution',
+      color: 'warning',
+      iconType: 'alert',
+      toastLifeTimeMs: 15000,
+      text: (
+        <>
+          <p>
+            Be aware that manually editing the query can spoil search results. The syntax
+            must be respected :
+          </p>
+          <ul>
+            Fields and their values should be put between brackets : &#123; &#125; - Make
+            sure every opened bracket is properly closed
+          </ul>
+          <ul>
+            "AND" and "OR" should be capitalized between different fields conditions and
+            lowercased within a field expression
+          </ul>
+          <ul>Make sure to check eventual typing mistakes</ul>
+        </>
+      ),
+    };
+    setNotificationToasts(notificationToasts.concat(toast));
+  };
+
+  const removeToast = (removedToast) => {
+    setNotificationToasts(
+      notificationToasts.filter((toast) => toast.id !== removedToast.id)
+    );
+  };
+
+  return (
+    <>
+      <EuiFlexGroup>
+        <EuiFlexItem grow={false}>
+          <EuiSpacer size="s" />
+          <EuiButtonEmpty
+            onClick={() => {
+              setIsAdvancedSearch(!isAdvancedSearch);
+            }}
+          >
+            Switch to basic search
+          </EuiButtonEmpty>
+        </EuiFlexItem>
+      </EuiFlexGroup>
+      <EuiFlexGroup>
+        <EuiFlexItem>
+          <EuiSpacer size="s" />
+          <SearchBar
+            isLoading={isLoading}
+            setIsLoading={setIsLoading}
+            search={search}
+            setSearch={setSearch}
+            searchResults={searchResults}
+            setSearchResults={setSearchResults}
+            searchFields={searchFields}
+            setSearchFields={setSearchFields}
+            searchName={searchName}
+            setSearchName={setSearchName}
+            searchDescription={searchDescription}
+            setSearchDescription={setSearchDescription}
+            readOnlyQuery={readOnlyQuery}
+            setReadOnlyQuery={setReadOnlyQuery}
+            selectedSources={selectedSources}
+            setSelectedSources={setSelectedSources}
+            availableSources={availableSources}
+            setAvailableSources={setAvailableSources}
+            standardFields={standardFields}
+            sources={sources}
+            setSelectedTabNumber={setSelectedTabNumber}
+            searchCount={searchCount}
+            setSearchCount={setSearchCount}
+            setFieldCount={setFieldCount}
+            isReadOnlyModalOpen={isReadOnlyModalOpen}
+            setIsReadOnlyModalOpen={setIsReadOnlyModalOpen}
+            isSaveSearchModalOpen={isSaveSearchModalOpen}
+            setIsSaveSearchModalOpen={setIsSaveSearchModalOpen}
+            userHistory={userHistory}
+            setUserHistory={setUserHistory}
+            selectedSavedSearch={selectedSavedSearch}
+            setSelectedSavedSearch={setSelectedSavedSearch}
+            historySelectError={historySelectError}
+            setHistorySelectError={setHistorySelectError}
+            selectedOperatorId={selectedOperatorId}
+            createEditableQueryToast={createEditableQueryToast}
+          />
+        </EuiFlexItem>
+      </EuiFlexGroup>
+      <EuiFlexGroup>
+        <EuiFlexItem>
+          <EuiSpacer size="s" />
+          {FieldsPanel(
+            standardFields,
+            setStandardFields,
+            searchFields,
+            setSearchFields,
+            selectedField,
+            setSelectedField,
+            selectedSection,
+            setSelectedSection,
+            isPopoverSelectOpen,
+            setIsPopoverSelectOpen,
+            isPopoverValueOpen,
+            setIsPopoverValueOpen,
+            valueError,
+            setValueError,
+            search,
+            setSearch,
+            setSearchCount,
+            selectedOperatorId,
+            setSelectedOperatorId,
+            fieldCount,
+            setFieldCount,
+            availableSources,
+            setAvailableSources,
+            selectedSources,
+            setSelectedSources,
+            sources,
+            datePickerStyles,
+            createPolicyToast
+          )}
+          <EuiSpacer size="s" />
+          {SourceSelect(
+            availableSources,
+            selectedSources,
+            setSelectedSources,
+            sourceSelectError,
+            setSourceSelectError
+          )}
+        </EuiFlexItem>
+      </EuiFlexGroup>
+      <EuiGlobalToastList
+        toasts={notificationToasts}
+        dismissToast={removeToast}
+        toastLifeTimeMs={2500}
+      />
+    </>
+  );
+};
+
+export default AdvancedSearch;
diff --git a/src/pages/search/BasicSearch/BasicSearch.js b/src/pages/search/BasicSearch/BasicSearch.js
new file mode 100644
index 0000000..20e7296
--- /dev/null
+++ b/src/pages/search/BasicSearch/BasicSearch.js
@@ -0,0 +1,89 @@
+import React from 'react';
+import {
+  EuiButton,
+  EuiButtonEmpty,
+  EuiFieldSearch,
+  EuiFlexGroup,
+  EuiFlexItem,
+  EuiProgress,
+  EuiSpacer,
+} from '@elastic/eui';
+import { createBasicQueriesBySource } from '../../../Utils';
+import { searchQuery } from '../../../actions/source';
+
+const BasicSearch = ({
+  standardFields,
+  availableSources,
+  selectedSources,
+  basicSearch,
+  setBasicSearch,
+  isLoading,
+  setIsAdvancedSearch,
+  isAdvancedSearch,
+  setIsLoading,
+  setSearchResults,
+  setSelectedTabNumber,
+}) => {
+  const onFormSubmit = () => {
+    setIsLoading(true);
+    const queriesWithIndices = createBasicQueriesBySource(
+      standardFields,
+      basicSearch,
+      selectedSources,
+      availableSources
+    );
+    searchQuery(queriesWithIndices).then((result) => {
+      setSearchResults(result);
+      setSelectedTabNumber(1);
+      setIsLoading(false);
+    });
+  };
+
+  return (
+    <>
+      <EuiFlexGroup>
+        <EuiFlexItem grow={false}>
+          <EuiSpacer size="s" />
+          <EuiButtonEmpty
+            onClick={() => {
+              setIsAdvancedSearch(!isAdvancedSearch);
+            }}
+          >
+            Switch to advanced search
+          </EuiButtonEmpty>
+        </EuiFlexItem>
+      </EuiFlexGroup>
+      <EuiFlexGroup>
+        <EuiFlexItem>
+          <EuiSpacer size="s" />
+          <form onSubmit={onFormSubmit}>
+            <EuiFlexGroup>
+              <EuiFlexItem>
+                <EuiFieldSearch
+                  value={basicSearch}
+                  onChange={(e) => setBasicSearch(e.target.value)}
+                  placeholder="Search..."
+                  fullWidth
+                />
+              </EuiFlexItem>
+              <EuiFlexItem grow={false}>
+                <EuiButton type="submit" fill isDisabled={isAdvancedSearch}>
+                  Search
+                </EuiButton>
+              </EuiFlexItem>
+            </EuiFlexGroup>
+          </form>
+          {isLoading && (
+            <EuiFlexGroup>
+              <EuiFlexItem>
+                <EuiProgress postion="fixed" size="l" color="accent" />
+              </EuiFlexItem>
+            </EuiFlexGroup>
+          )}
+        </EuiFlexItem>
+      </EuiFlexGroup>
+    </>
+  );
+};
+
+export default BasicSearch;
diff --git a/src/pages/search/Search.js b/src/pages/search/Search.js
index 901e335..16d3043 100644
--- a/src/pages/search/Search.js
+++ b/src/pages/search/Search.js
@@ -1,161 +1,31 @@
 import React, { useState, useEffect } from 'react';
 import {
-  EuiProgress,
-  EuiRadioGroup,
-  EuiFieldText,
-  EuiPanel,
-  EuiPopover,
-  EuiPopoverTitle,
-  EuiPopoverFooter,
   EuiTabbedContent,
-  EuiFormRow,
-  EuiComboBox,
   EuiPageContentBody,
   EuiForm,
-  EuiTextArea,
   EuiFlexGroup,
   EuiFlexItem,
-  EuiFieldSearch,
-  EuiButton,
-  EuiButtonEmpty,
-  EuiSwitch,
-  EuiButtonIcon,
-  EuiIcon,
   EuiSpacer,
   EuiPageContent,
   EuiPageContentHeader,
   EuiTitle,
   EuiPageContentHeaderSection,
-  EuiTextColor,
-  EuiOverlayMask,
-  EuiModal,
-  EuiModalHeader,
-  EuiModalHeaderTitle,
-  EuiModalBody,
-  EuiModalFooter,
-  EuiSelect,
-  EuiGlobalToastList,
-  EuiHealth,
 } from '@elastic/eui';
-import { makeStyles } from '@material-ui/core/styles';
-import TextField from '@material-ui/core/TextField';
-import { Operators, NumericOptions, DateOptions } from './Data';
 import Results from '../results/Results';
 import SearchMap from '../maps/SearchMap';
-import {
-  createBasicQueriesBySource,
-  changeNameToLabel,
-  SearchField,
-  removeNullFields,
-  getSections,
-  getFieldsBySection,
-  updateArrayElement,
-  removeArrayElement,
-  updateSearchFieldValues,
-  createAdvancedQueriesBySource,
-} from '../../Utils.js';
+import { removeNullFields } from '../../Utils.js';
 import {
   fetchPublicFields,
   fetchUserPolicyFields,
   fetchSources,
-  searchQuery,
-  getQueryCount,
 } from '../../actions/source';
-import { addUserHistory, fetchUserHistory } from '../../actions/user';
+import { fetchUserHistory } from '../../actions/user';
 import { useTranslation } from 'react-i18next';
+import AdvancedSearch from './AdvancedSearch/AdvancedSearch';
+import BasicSearch from './BasicSearch/BasicSearch';
+import styles from './styles';
 
-const useStyles = makeStyles((theme) => ({
-  container: {
-    display: 'flex',
-    flexWrap: 'wrap',
-  },
-  textField: {
-    marginLeft: theme.spacing(1),
-    marginRight: theme.spacing(1),
-    width: 240,
-  },
-}));
-
-const fieldValuesToString = (field) => {
-  let strValues = '';
-  switch (field.type) {
-    case 'Numeric':
-      field.values.forEach((element) => {
-        switch (element.option) {
-          case 'between':
-            strValues = `${strValues} ${element.value1} <= ${field.name} <= ${element.value2} or `;
-            break;
-          default:
-            strValues = `${strValues} ${field.name} ${element.option} ${element.value1} or `;
-        }
-      });
-      if (strValues.endsWith('or '))
-        strValues = strValues.substring(0, strValues.length - 4);
-      break;
-    case 'Date':
-      field.values.forEach((element) => {
-        switch (element.option) {
-          case 'between':
-            strValues = `${strValues} ${element.startDate} <= ${field.name} <= ${element.endDate} or `;
-            break;
-          default:
-            strValues = `${strValues} ${field.name} ${element.option} ${element.startDate} or `;
-        }
-      });
-      if (strValues.endsWith(' or '))
-        strValues = strValues.substring(0, strValues.length - 4);
-      break;
-    case 'List':
-      strValues = `${strValues} ${field.name} = `;
-      field.values.forEach((element) => {
-        strValues = `${strValues} ${element.label}, `;
-      });
-      if (strValues.endsWith(', '))
-        strValues = strValues.substring(0, strValues.length - 2);
-      break;
-
-    //type : text
-    default:
-      strValues = `${strValues} ${field.name} = ${field.values}`;
-  }
-  return strValues;
-};
-
-const updateSources = (
-  searchFields,
-  sources,
-  setSelectedSources,
-  setAvailableSources
-) => {
-  let updatedSources = [];
-  let availableSources = [];
-  let noPrivateField = true;
-  //search for policy fields to filter sources
-  searchFields.forEach((field) => {
-    if (field.isValidated) {
-      //if sources haven't already been filtered
-      if (noPrivateField && !updatedSources.length) {
-        availableSources = sources;
-      } else {
-        availableSources = updatedSources;
-      }
-      updatedSources = [];
-      field.sources.forEach((sourceId) => {
-        noPrivateField = false;
-        const source = availableSources.find((src) => src.id === sourceId);
-        if (source && !updatedSources.includes(source)) updatedSources.push(source);
-      });
-    }
-  });
-  setSelectedSources(updatedSources);
-  if (noPrivateField && !updatedSources.length) {
-    setAvailableSources(sources);
-  } else {
-    setAvailableSources(updatedSources);
-  }
-};
-
-const fetchHistory = (setUserHistory) => {
+export const fetchHistory = (setUserHistory) => {
   fetchUserHistory(sessionStorage.getItem('user_id')).then((result) => {
     if (result[0] && result[0].ui_structure) {
       result.forEach((item) => {
@@ -167,1362 +37,13 @@ const fetchHistory = (setUserHistory) => {
   });
 };
 
-const addHistory = (
-  kcID,
-  search,
-  searchName,
-  searchFields,
-  searchDescription,
-  setUserHistory
-) => {
-  addUserHistory(
-    sessionStorage.getItem('user_id'),
-    search,
-    searchName,
-    searchFields,
-    searchDescription
-  ).then(() => {
-    fetchHistory(setUserHistory);
-  });
-};
-
-const updateSearch = (setSearch, searchFields, selectedOperatorId, setSearchCount) => {
-  let searchText = '';
-  searchFields.forEach((field) => {
-    if (field.isValidated) {
-      searchText =
-        searchText +
-        `{${fieldValuesToString(field)} } ${Operators[selectedOperatorId].value.toUpperCase()} `;
-    }
-  });
-  if (searchText.endsWith(' AND ')) {
-    searchText = searchText.substring(0, searchText.length - 5);
-  } else if (searchText.endsWith(' OR ')) {
-    searchText = searchText.substring(0, searchText.length - 4);
-  }
-  setSearchCount();
-  setSearch(searchText);
-};
-
-const HistorySelect = (
-  sources,
-  setAvailableSources,
-  setSelectedSources,
-  setSearch,
-  searchFields,
-  selectedOperatorId,
-  userHistory,
-  setUserHistory,
-  setSearchFields,
-  setSearchCount,
-  setFieldCount,
-  selectedSavedSearch,
-  setSelectedSavedSearch,
-  historySelectError,
-  setHistorySelectError
-) => {
-  if (Object.keys(userHistory).length !== 0) {
-    const onHistoryChange = (selectedSavedSearch) => {
-      setHistorySelectError(undefined);
-      if (!!selectedSavedSearch[0].query) {
-        setSelectedSavedSearch(selectedSavedSearch);
-        setSearch(selectedSavedSearch[0].query);
-        setSearchCount();
-        setFieldCount([]);
-      }
-      if (!!selectedSavedSearch[0].ui_structure) {
-        updateSources(
-          selectedSavedSearch[0].ui_structure,
-          sources,
-          setSelectedSources,
-          setAvailableSources
-        );
-        setSearchFields(selectedSavedSearch[0].ui_structure);
-      }
-    };
-
-    const onHistorySearchChange = (value, hasMatchingOptions) => {
-      setHistorySelectError(
-        value.length === 0 || hasMatchingOptions
-          ? undefined
-          : `"${value}" is not a valid option`
-      );
-    };
-
-    return (
-      <>
-        <EuiFormRow
-          error={historySelectError}
-          isInvalid={historySelectError !== undefined}
-        >
-          <EuiComboBox
-            placeholder="Load a previous search"
-            singleSelection={{ asPlainText: true }}
-            options={userHistory}
-            selectedOptions={selectedSavedSearch}
-            onChange={onHistoryChange}
-            onSearchChange={onHistorySearchChange}
-          />
-        </EuiFormRow>
-      </>
-    );
-  }
-};
-
-const SearchBar = (
-  isLoading,
-  setIsLoading,
-  search,
-  setSearch,
-  searchResults,
-  setSearchResults,
-  searchFields,
-  setSearchFields,
-  searchName,
-  setSearchName,
-  searchDescription,
-  setSearchDescription,
-  readOnlyQuery,
-  setReadOnlyQuery,
-  selectedSources,
-  setSelectedSources,
-  availableSources,
-  setAvailableSources,
-  standardFields,
-  sources,
-  setSelectedTabNumber,
-  searchCount,
-  setSearchCount,
-  setFieldCount,
-  isReadOnlyModalOpen,
-  setIsReadOnlyModalOpen,
-  isSaveSearchModalOpen,
-  setIsSaveSearchModalOpen,
-  userHistory,
-  setUserHistory,
-  selectedSavedSearch,
-  setSelectedSavedSearch,
-  historySelectError,
-  setHistorySelectError,
-  selectedOperatorId,
-  createEditableQueryToast
-) => {
-  // const closeReadOnlyModal = () => setIsReadOnlyModalOpen(false)
-
-  /* const switchReadOnly = (readOnlyQuery, isReadOnlyModalOpen) => {
-        if (readOnlyQuery) {
-            setIsReadOnlyModalOpen(true)
-        } else {
-            setReadOnlyQuery(true)
-        } */
-  /* if (!localStorage.getItem("InSylvaReadOnlySearch") && readOnlyQuery) {
-        setIsReadOnlyModalOpen(!isReadOnlyModalOpen)
-    } */
-  // }
-
-  /* let readOnlyModal;
-
-    if (isReadOnlyModalOpen) {
-        readOnlyModal = (
-            <EuiOverlayMask>
-                <EuiConfirmModal
-                    title="Allow query editing"
-                    onCancel={() => closeReadOnlyModal()}
-                    onConfirm={() => {
-                        setReadOnlyQuery(!readOnlyQuery)
-                        closeReadOnlyModal()
-                    }}
-                    cancelButtonText="No"
-                    confirmButtonText="Yes"
-                    buttonColor="danger"
-                    defaultFocusedButton="confirm">
-                    <p>Be aware that manually editing the query can spoil search results.</p>
-                    <p>The syntax needs to be respected :</p>
-                    <ul>Fields and their values must be given between brackets : &#123; &#125;</ul>
-                    <ul>Check eventual typing mistakes</ul>
-                    <ul>Make sure every opened bracket is properly closed</ul>
-                    <p>Are you sure you want to do this?</p>
-                </EuiConfirmModal>
-            </EuiOverlayMask>
-        )
-    }*/
-
-  const closeSaveSearchModal = () => setIsSaveSearchModalOpen(false);
-
-  let saveSearchModal;
-
-  if (isSaveSearchModalOpen) {
-    saveSearchModal = (
-      <EuiOverlayMask>
-        <EuiModal onClose={closeSaveSearchModal} initialFocus="[name=searchName]">
-          <EuiModalHeader>
-            <EuiModalHeaderTitle>Save search</EuiModalHeaderTitle>
-          </EuiModalHeader>
-
-          <EuiModalBody>
-            <EuiForm>
-              <EuiFormRow label="Search name">
-                <EuiFieldText
-                  name="searchName"
-                  value={searchName}
-                  onChange={(e) => {
-                    setSearchName(e.target.value);
-                  }}
-                />
-              </EuiFormRow>
-              <EuiFormRow label="Description (optional)">
-                <EuiTextArea
-                  value={searchDescription}
-                  onChange={(e) => setSearchDescription(e.target.value)}
-                  placeholder="Search description..."
-                  fullWidth
-                  compressed
-                />
-              </EuiFormRow>
-            </EuiForm>
-          </EuiModalBody>
-
-          <EuiModalFooter>
-            <EuiButtonEmpty
-              onClick={() => {
-                closeSaveSearchModal();
-              }}
-            >
-              Cancel
-            </EuiButtonEmpty>
-            <EuiButton
-              onClick={() => {
-                if (!!searchName) {
-                  addHistory(
-                    sessionStorage.getItem('user_id'),
-                    search,
-                    searchName,
-                    searchFields,
-                    searchDescription,
-                    setUserHistory
-                  );
-                  setSearchName('');
-                  setSearchDescription('');
-                  closeSaveSearchModal();
-                }
-              }}
-              fill
-            >
-              Save
-            </EuiButton>
-          </EuiModalFooter>
-        </EuiModal>
-      </EuiOverlayMask>
-    );
-  }
-
-  return (
-    <>
-      {/*!readOnlyQuery ?
-                <>
-                    <EuiCallOut title="Proceed with caution!" color="warning" iconType="alert">
-                        <p>Be aware that manually editing the query can spoil search results. The syntax must be respected :</p>
-                        <ul>Fields and their values should be put between brackets : &#123; &#125;  -  Make sure every opened bracket is properly closed</ul>
-                        <ul>"AND" and "OR" should be capitalized between different fields conditions and lowercased within a field expression</ul>
-                        <ul>Make sure to check eventual typing mistakes</ul>
-
-                    </EuiCallOut>
-                    <EuiSpacer size="s" />
-                </>
-                : <></>
-            */}
-      <EuiFlexGroup>
-        <EuiFlexItem>
-          <EuiTextArea
-            readOnly={readOnlyQuery}
-            value={search}
-            onChange={(e) => setSearch(e.target.value)}
-            placeholder="Add fields..."
-            fullWidth
-          />
-        </EuiFlexItem>
-        <EuiFlexItem grow={false}>
-          <EuiButton
-            size="s"
-            fill
-            onClick={() => {
-              if (search.trim()) {
-                setIsLoading(true);
-                const queriesWithIndices = createAdvancedQueriesBySource(
-                  standardFields,
-                  search,
-                  selectedSources,
-                  availableSources
-                );
-                searchQuery(queriesWithIndices).then((result) => {
-                  // sessionStorage.setItem("searchResults", JSON.stringify(result))
-                  setSearchResults(result);
-                  setSelectedTabNumber(1);
-                  setIsLoading(false);
-                });
-              }
-            }}
-          >
-            Search
-          </EuiButton>
-          <EuiSpacer size="s" />
-          {isNaN(searchCount) ? (
-            <></>
-          ) : (
-            <>
-              <EuiTextColor
-                color="secondary"
-                style={{ display: 'flex', justifyContent: 'center' }}
-              >
-                {searchCount} {searchCount === 1 ? 'result' : 'results'}
-              </EuiTextColor>
-              <EuiSpacer size="s" />
-            </>
-          )}
-          <EuiButton
-            size="s"
-            onClick={() => {
-              if (!!search) {
-                const queriesWithIndices = createAdvancedQueriesBySource(
-                  standardFields,
-                  search,
-                  selectedSources,
-                  availableSources
-                );
-                getQueryCount(queriesWithIndices).then((result) => {
-                  if (result || result === 0) setSearchCount(result);
-                });
-              }
-            }}
-          >
-            Count results
-          </EuiButton>
-          <EuiSpacer size="s" />
-          <EuiButton
-            size="s"
-            onClick={() => {
-              setIsSaveSearchModalOpen(true);
-            }}
-          >
-            Save search
-          </EuiButton>
-          {saveSearchModal}
-          <EuiSpacer size="s" />
-          <EuiSwitch
-            compressed
-            label={'Editable'}
-            checked={!readOnlyQuery}
-            onChange={() => {
-              // switchReadOnly(readOnlyQuery, isReadOnlyModalOpen)
-              setReadOnlyQuery(!readOnlyQuery);
-              if (readOnlyQuery) {
-                createEditableQueryToast();
-              }
-            }}
-          />
-          {/* readOnlyModal */}
-        </EuiFlexItem>
-      </EuiFlexGroup>
-      {isLoading && (
-        <EuiFlexGroup>
-          <EuiFlexItem>
-            <EuiProgress postion="fixed" size="l" color="accent" />
-          </EuiFlexItem>
-        </EuiFlexGroup>
-      )}
-      <EuiSpacer size="s" />
-      <EuiFlexGroup>
-        <EuiFlexItem>
-          {HistorySelect(
-            sources,
-            setAvailableSources,
-            setSelectedSources,
-            setSearch,
-            searchFields,
-            selectedOperatorId,
-            userHistory,
-            setUserHistory,
-            setSearchFields,
-            setSearchCount,
-            setFieldCount,
-            selectedSavedSearch,
-            setSelectedSavedSearch,
-            historySelectError,
-            setHistorySelectError
-          )}
-        </EuiFlexItem>
-      </EuiFlexGroup>
-    </>
-  );
-};
-
-const PopoverSelect = (
-  standardFields,
-  setStandardFields,
-  searchFields,
-  setSearchFields,
-  selectedField,
-  setSelectedField,
-  selectedSection,
-  setSelectedSection,
-  isPopoverSelectOpen,
-  setIsPopoverSelectOpen,
-  fieldCount,
-  setFieldCount,
-  selectedSources,
-  setSelectedSources
-) => {
-  const handleAddfield = () => {
-    if (!!selectedField[0]) {
-      const field = standardFields.find(
-        (item) =>
-          item.field_name.replace(/_|\./g, ' ') ===
-          selectedSection[0].label + ' ' + selectedField[0].label
-      );
-      switch (field.field_type) {
-        case 'Text':
-          setSearchFields([
-            ...searchFields,
-            new SearchField(field.field_name, field.field_type, '', false, field.sources),
-          ]);
-          break;
-        case 'List':
-          setSearchFields([
-            ...searchFields,
-            new SearchField(field.field_name, field.field_type, [], false, field.sources),
-          ]);
-          break;
-        default:
-          setSearchFields([
-            ...searchFields,
-            new SearchField(
-              field.field_name,
-              field.field_type,
-              [{}],
-              false,
-              field.sources
-            ),
-          ]);
-      }
-    }
-  };
-
-  const selectField = () => {
-    const renderOption = (option, searchValue, contentClassName) => {
-      const { label, color } = option;
-      return <EuiHealth color={color}>{label}</EuiHealth>;
-    };
-    if (selectedSection.length) {
-      return (
-        <>
-          <EuiComboBox
-            placeholder="Select a field"
-            singleSelection={{ asPlainText: true }}
-            options={getFieldsBySection(standardFields, selectedSection[0])}
-            selectedOptions={selectedField}
-            onChange={(selected) => setSelectedField(selected)}
-            isClearable={true}
-            renderOption={renderOption}
-          />
-          <EuiPopoverFooter>
-            <EuiButton
-              size="s"
-              onClick={() => {
-                handleAddfield();
-                setIsPopoverSelectOpen(false);
-                setSelectedSection([]);
-                setSelectedField([]);
-              }}
-            >
-              Add this field
-            </EuiButton>
-          </EuiPopoverFooter>
-        </>
-      );
-    }
-  };
-
-  return (
-    <EuiPopover
-      panelPaddingSize="s"
-      button={
-        <EuiButton
-          iconType="listAdd"
-          iconSide="left"
-          onClick={() => setIsPopoverSelectOpen(!isPopoverSelectOpen)}
-        >
-          Add field
-        </EuiButton>
-      }
-      isOpen={isPopoverSelectOpen}
-      closePopover={() => setIsPopoverSelectOpen(false)}
-    >
-      <div style={{ width: 'intrinsic', minWidth: 240 }}>
-        <EuiPopoverTitle>Select a field</EuiPopoverTitle>
-        <EuiComboBox
-          placeholder="Select a section"
-          singleSelection={{ asPlainText: true }}
-          options={getSections(standardFields)}
-          selectedOptions={selectedSection}
-          onChange={(selected) => {
-            setSelectedSection(selected);
-            setSelectedField([]);
-          }}
-          isClearable={false}
-        />
-      </div>
-      {selectField()}
-    </EuiPopover>
-  );
-};
-
-const PopoverValueContent = (
-  index,
-  standardFields,
-  setStandardFields,
-  searchFields,
-  setSearchFields,
-  valueError,
-  setValueError,
-  search,
-  setSearch,
-  setSearchCount,
-  fieldCount,
-  setFieldCount,
-  isPopoverValueOpen,
-  setIsPopoverValueOpen,
-  selectedOperatorId,
-  datePickerStyles,
-  createPolicyToast,
-  selectedSources,
-  setSelectedSources,
-  availableSources,
-  setAvailableSources
-) => {
-  const onValueSearchChange = (value, hasMatchingOptions) => {
-    setValueError(
-      value.length === 0 || hasMatchingOptions
-        ? undefined
-        : `"${value}" is not a valid option`
-    );
-  };
-
-  const validateFieldValues = () => {
-    let fieldValues;
-    if (Array.isArray(searchFields[index].values)) {
-      fieldValues = [];
-      searchFields[index].values.forEach((value) => {
-        if (!!value) {
-          fieldValues.push(value);
-        }
-      });
-    } else {
-      fieldValues = searchFields[index].values;
-    }
-
-    const updatedSearchFields = updateArrayElement(
-      searchFields,
-      index,
-      new SearchField(
-        searchFields[index].name,
-        searchFields[index].type,
-        fieldValues,
-        true,
-        searchFields[index].sources
-      )
-    );
-    setSearchFields(updatedSearchFields);
-    updateSearch(setSearch, updatedSearchFields, selectedOperatorId, setSearchCount);
-    setFieldCount(updateArrayElement(fieldCount, index));
-    if (searchFields[index].sources.length) {
-      const filteredSources = [];
-      searchFields[index].sources.forEach((sourceId) => {
-        let source;
-        if (selectedSources.length) {
-          source = selectedSources.find((src) => src.id === sourceId);
-        } else {
-          source = availableSources.find((src) => src.id === sourceId);
-        }
-        if (source) {
-          filteredSources.push(source);
-        }
-      });
-      setAvailableSources(filteredSources);
-      setSelectedSources(filteredSources);
-      createPolicyToast();
-    }
-  };
-
-  const invalidateFieldValues = () => {
-    const updatedSearchFields = updateArrayElement(
-      searchFields,
-      index,
-      new SearchField(
-        searchFields[index].name,
-        searchFields[index].type,
-        searchFields[index].values,
-        false,
-        searchFields[index].sources
-      )
-    );
-    setSearchFields(updatedSearchFields);
-    updateSearch(setSearch, updatedSearchFields, selectedOperatorId, setSearchCount);
-  };
-
-  const ValuePopoverFooter = (i) => {
-    if (i === searchFields[index].values.length - 1) {
-      return (
-        <EuiPopoverFooter>
-          <EuiButton
-            size="s"
-            onClick={() => {
-              setSearchFields(
-                updateArrayElement(
-                  searchFields,
-                  index,
-                  new SearchField(
-                    searchFields[index].name,
-                    searchFields[index].type,
-                    [...searchFields[index].values, {}],
-                    false,
-                    searchFields[index].sources
-                  )
-                )
-              );
-            }}
-          >
-            Add value
-          </EuiButton>
-          <EuiButton
-            size="s"
-            style={{ float: 'right' }}
-            onClick={() => {
-              validateFieldValues();
-              setIsPopoverValueOpen(updateArrayElement(isPopoverValueOpen, index, false));
-            }}
-          >
-            Validate
-          </EuiButton>
-        </EuiPopoverFooter>
-      );
-    }
-  };
-
-  const addFieldValue = (i, selectedOption) => {
-    setSearchFields(
-      updateSearchFieldValues(
-        searchFields,
-        index,
-        updateArrayElement(searchFields[index].values, i, { option: selectedOption })
-      )
-    );
-  };
-
-  const getListFieldValues = () => {
-    const listFieldValues = [];
-    standardFields
-      .find((item) => item.field_name === searchFields[index].name)
-      .values.split(', ')
-      .sort()
-      .forEach((element) => {
-        listFieldValues.push({ label: element });
-      });
-    return listFieldValues;
-  };
-
-  switch (searchFields[index].type) {
-    case 'Text':
-      return (
-        <>
-          <EuiFlexItem>
-            <EuiFieldText
-              placeholder={'Type values'}
-              value={searchFields[index].values}
-              onChange={(e) =>
-                setSearchFields(
-                  updateSearchFieldValues(searchFields, index, e.target.value)
-                )
-              }
-            />
-          </EuiFlexItem>
-          <EuiPopoverFooter>
-            <EuiButton
-              size="s"
-              style={{ float: 'right' }}
-              onClick={() => {
-                validateFieldValues();
-                setIsPopoverValueOpen(
-                  updateArrayElement(isPopoverValueOpen, index, false)
-                );
-              }}
-            >
-              Validate
-            </EuiButton>
-          </EuiPopoverFooter>
-        </>
-      );
-    case 'List':
-      return (
-        <>
-          <EuiFormRow error={valueError} isInvalid={valueError !== undefined}>
-            <EuiComboBox
-              placeholder={'Select values'}
-              options={getListFieldValues()}
-              selectedOptions={searchFields[index].values}
-              onChange={(selectedOptions) => {
-                setValueError(undefined);
-                setSearchFields(
-                  updateSearchFieldValues(searchFields, index, selectedOptions)
-                );
-              }}
-              onSearchChange={onValueSearchChange}
-            />
-          </EuiFormRow>
-          <EuiPopoverFooter>
-            <EuiButton
-              size="s"
-              style={{ float: 'right' }}
-              onClick={() => {
-                validateFieldValues();
-                setIsPopoverValueOpen(
-                  updateArrayElement(isPopoverValueOpen, index, false)
-                );
-              }}
-            >
-              Validate
-            </EuiButton>
-          </EuiPopoverFooter>
-        </>
-      );
-
-    case 'Numeric':
-      const NumericValues = (i) => {
-        if (!!searchFields[index].values[i].option) {
-          switch (searchFields[index].values[i].option) {
-            case 'between':
-              return (
-                <>
-                  <EuiFlexItem>
-                    <EuiFieldText
-                      placeholder={'1st value'}
-                      value={searchFields[index].values[i].value1}
-                      onChange={(e) => {
-                        setSearchFields(
-                          updateSearchFieldValues(
-                            searchFields,
-                            index,
-                            updateArrayElement(searchFields[index].values, i, {
-                              option: searchFields[index].values[i].option,
-                              value1: e.target.value,
-                              value2: searchFields[index].values[i].value2,
-                            })
-                          )
-                        );
-                      }}
-                    />
-                  </EuiFlexItem>
-                  <EuiFlexItem>
-                    <EuiFieldText
-                      placeholder={'2nd value'}
-                      value={searchFields[index].values[i].value2}
-                      onChange={(e) =>
-                        setSearchFields(
-                          updateSearchFieldValues(
-                            searchFields,
-                            index,
-                            updateArrayElement(searchFields[index].values, i, {
-                              option: searchFields[index].values[i].option,
-                              value1: searchFields[index].values[i].value1,
-                              value2: e.target.value,
-                            })
-                          )
-                        )
-                      }
-                    />
-                  </EuiFlexItem>
-                  {ValuePopoverFooter(i)}
-                </>
-              );
-
-            default:
-              return (
-                <>
-                  <EuiFlexItem>
-                    <EuiFieldText
-                      placeholder={'Type value'}
-                      value={searchFields[index].values[i].value1}
-                      onChange={(e) => {
-                        setSearchFields(
-                          updateSearchFieldValues(
-                            searchFields,
-                            index,
-                            updateArrayElement(searchFields[index].values, i, {
-                              option: searchFields[index].values[i].option,
-                              value1: e.target.value,
-                              value2: searchFields[index].values[i].value2,
-                            })
-                          )
-                        );
-                      }}
-                    />
-                  </EuiFlexItem>
-                  {ValuePopoverFooter(i)}
-                </>
-              );
-          }
-        }
-      };
-
-      return (
-        <>
-          {searchFields[index].values.map((value, i) => (
-            <div key={i}>
-              <EuiSelect
-                hasNoInitialSelection
-                id="Select an option"
-                options={NumericOptions}
-                value={searchFields[index].values[i].option}
-                onChange={(e) => {
-                  addFieldValue(i, e.target.value);
-                  invalidateFieldValues();
-                }}
-              />
-              {NumericValues(i)}
-            </div>
-          ))}
-        </>
-      );
-
-    case 'Date':
-      const SelectDates = (i) => {
-        if (!!searchFields[index].values[i].option) {
-          switch (searchFields[index].values[i].option) {
-            case 'between':
-              return (
-                <>
-                  <form className={datePickerStyles.container} noValidate>
-                    <TextField
-                      label="between"
-                      type="date"
-                      defaultValue={
-                        !!searchFields[index].values[i].startDate
-                          ? searchFields[index].values[i].startDate
-                          : Date.now()
-                      }
-                      className={datePickerStyles.textField}
-                      InputLabelProps={{
-                        shrink: true,
-                      }}
-                      onChange={(e) =>
-                        setSearchFields(
-                          updateSearchFieldValues(
-                            searchFields,
-                            index,
-                            updateArrayElement(searchFields[index].values, i, {
-                              option: searchFields[index].values[i].option,
-                              startDate: e.target.value,
-                              endDate: searchFields[index].values[i].endDate,
-                            })
-                          )
-                        )
-                      }
-                    />
-                  </form>
-                  <form className={datePickerStyles.container} noValidate>
-                    <TextField
-                      label="and"
-                      type="date"
-                      defaultValue={
-                        !!searchFields[index].values[i].endDate
-                          ? searchFields[index].values[i].endDate
-                          : Date.now()
-                      }
-                      className={datePickerStyles.textField}
-                      InputLabelProps={{
-                        shrink: true,
-                      }}
-                      onChange={(e) =>
-                        setSearchFields(
-                          updateSearchFieldValues(
-                            searchFields,
-                            index,
-                            updateArrayElement(searchFields[index].values, i, {
-                              option: searchFields[index].values[i].option,
-                              startDate: searchFields[index].values[i].startDate,
-                              endDate: e.target.value,
-                            })
-                          )
-                        )
-                      }
-                    />
-                  </form>
-                  {ValuePopoverFooter(i)}
-                </>
-              );
-
-            default:
-              return (
-                <>
-                  <form className={datePickerStyles.container} noValidate>
-                    <TextField
-                      type="date"
-                      defaultValue={
-                        !!searchFields[index].values[i].startDate
-                          ? searchFields[index].values[i].startDate
-                          : Date.now()
-                      }
-                      className={datePickerStyles.textField}
-                      InputLabelProps={{
-                        shrink: true,
-                      }}
-                      onChange={(e) =>
-                        setSearchFields(
-                          updateSearchFieldValues(
-                            searchFields,
-                            index,
-                            updateArrayElement(searchFields[index].values, i, {
-                              option: searchFields[index].values[i].option,
-                              startDate: e.target.value,
-                              endDate: Date.now(),
-                            })
-                          )
-                        )
-                      }
-                    />
-                  </form>
-                  {ValuePopoverFooter(i)}
-                </>
-              );
-          }
-        }
-      };
-
-      return (
-        <>
-          {searchFields[index].values.map((value, i) => (
-            <div key={i}>
-              <EuiSelect
-                hasNoInitialSelection
-                id="Select an option"
-                options={DateOptions}
-                value={searchFields[index].values[i].option}
-                onChange={(e) => {
-                  addFieldValue(i, e.target.value);
-                  invalidateFieldValues();
-                }}
-              />
-              {SelectDates(i)}
-            </div>
-          ))}
-        </>
-      );
-    default:
-  }
-};
-
-const PopoverValueButton = (
-  index,
-  standardFields,
-  setStandardFields,
-  searchFields,
-  setSearchFields,
-  isPopoverValueOpen,
-  setIsPopoverValueOpen,
-  valueError,
-  setValueError,
-  search,
-  setSearch,
-  setSearchCount,
-  fieldCount,
-  setFieldCount,
-  selectedOperatorId,
-  datePickerStyles,
-  createPolicyToast,
-  selectedSources,
-  setSelectedSources,
-  availableSources,
-  setAvailableSources
-) => {
-  return (
-    <EuiPopover
-      panelPaddingSize="s"
-      button={
-        <EuiButtonIcon
-          size="s"
-          color="primary"
-          onClick={() =>
-            setIsPopoverValueOpen(
-              updateArrayElement(isPopoverValueOpen, index, !isPopoverValueOpen[index])
-            )
-          }
-          iconType="documentEdit"
-          title="Give field values"
-        />
-      }
-      isOpen={isPopoverValueOpen[index]}
-      closePopover={() =>
-        setIsPopoverValueOpen(updateArrayElement(isPopoverValueOpen, index, false))
-      }
-    >
-      {/*<div style={{ width: 240 }}>
-                <EuiButtonIcon
-                    size="s"
-                    style={{ float: 'right' }}
-                    color="danger"
-                    onClick={() => setIsPopoverValueOpen(updateArrayElement(isPopoverValueOpen, index, false))}
-                    iconType="crossInACircleFilled"
-                    title="Close popover"
-                />
-            </div>*/}
-      <div style={{ width: 240 }}>
-        {PopoverValueContent(
-          index,
-          standardFields,
-          setStandardFields,
-          searchFields,
-          setSearchFields,
-          valueError,
-          setValueError,
-          search,
-          setSearch,
-          setSearchCount,
-          fieldCount,
-          setFieldCount,
-          isPopoverValueOpen,
-          setIsPopoverValueOpen,
-          selectedOperatorId,
-          datePickerStyles,
-          createPolicyToast,
-          selectedSources,
-          setSelectedSources,
-          availableSources,
-          setAvailableSources
-        )}
-      </div>
-    </EuiPopover>
-  );
-};
-
-const FieldsPanel = (
-  standardFields,
-  setStandardFields,
-  searchFields,
-  setSearchFields,
-  selectedField,
-  setSelectedField,
-  selectedSection,
-  setSelectedSection,
-  isPopoverSelectOpen,
-  setIsPopoverSelectOpen,
-  isPopoverValueOpen,
-  setIsPopoverValueOpen,
-  valueError,
-  setValueError,
-  search,
-  setSearch,
-  setSearchCount,
-  selectedOperatorId,
-  setSelectedOperatorId,
-  fieldCount,
-  setFieldCount,
-  availableSources,
-  setAvailableSources,
-  selectedSources,
-  setSelectedSources,
-  sources,
-  datePickerStyles,
-  createPolicyToast
-) => {
-  const countFieldValues = (field, index) => {
-    const fieldStr = `{${fieldValuesToString(field)}}`;
-    const queriesWithIndices = createAdvancedQueriesBySource(
-      standardFields,
-      fieldStr,
-      selectedSources,
-      availableSources
-    );
-    getQueryCount(queriesWithIndices).then((result) => {
-      if (result || result === 0)
-        setFieldCount(updateArrayElement(fieldCount, index, result));
-    });
-  };
-
-  const handleRemoveField = (index) => {
-    const updatedSearchFields = removeArrayElement(searchFields, index);
-    setSearchFields(updatedSearchFields);
-    updateSources(updatedSearchFields, sources, setSelectedSources, setAvailableSources);
-    updateSearch(setSearch, updatedSearchFields, selectedOperatorId, setSearchCount);
-  };
-
-  const handleClearValues = (index) => {
-    let updatedSearchFields = [];
-    switch (searchFields[index].type) {
-      case 'Text':
-        updatedSearchFields = updateArrayElement(
-          searchFields,
-          index,
-          new SearchField(
-            searchFields[index].name,
-            searchFields[index].type,
-            '',
-            false,
-            searchFields[index].sources
-          )
-        );
-        break;
-      case 'List':
-        updatedSearchFields = updateArrayElement(
-          searchFields,
-          index,
-          new SearchField(
-            searchFields[index].name,
-            searchFields[index].type,
-            [],
-            false,
-            searchFields[index].sources
-          )
-        );
-        break;
-      default:
-        updatedSearchFields = updateArrayElement(
-          searchFields,
-          index,
-          new SearchField(
-            searchFields[index].name,
-            searchFields[index].type,
-            [{}],
-            false,
-            searchFields[index].sources
-          )
-        );
-    }
-    setSearchFields(updatedSearchFields);
-    updateSources(updatedSearchFields, sources, setSelectedSources, setAvailableSources);
-    setFieldCount(updateArrayElement(fieldCount, index));
-    updateSearch(setSearch, updatedSearchFields, selectedOperatorId, setSearchCount);
-  };
-
-  if (standardFields === []) {
-    return <h2>Loading user fields...</h2>;
-  }
-
-  return (
-    <>
-      <EuiTitle size="xs">
-        <h2>Field search</h2>
-      </EuiTitle>
-      <EuiPanel paddingSize="m">
-        <EuiFlexGroup direction="column">
-          {searchFields.map((field, index) => (
-            <EuiPanel key={'field' + index} paddingSize="s">
-              <EuiFlexItem grow={false}>
-                <EuiFlexGroup direction="row" alignItems="center">
-                  <EuiFlexItem grow={false}>
-                    <EuiButtonIcon
-                      size="s"
-                      color="danger"
-                      onClick={() => handleRemoveField(index)}
-                      iconType="indexClose"
-                      title="Remove field"
-                    />
-                  </EuiFlexItem>
-                  <EuiFlexItem>
-                    {field.isValidated ? (
-                      <>
-                        {field.sources.length ? (
-                          <EuiHealth color="danger">
-                            {fieldValuesToString(field).replace(/_|\./g, ' ')}
-                          </EuiHealth>
-                        ) : (
-                          <EuiHealth color="primary">
-                            {fieldValuesToString(field).replace(/_|\./g, ' ')}
-                          </EuiHealth>
-                        )}
-                      </>
-                    ) : (
-                      <>
-                        {field.sources.length ? (
-                          <EuiHealth color="danger">
-                            {field.name.replace(/_|\./g, ' ')}
-                          </EuiHealth>
-                        ) : (
-                          <EuiHealth color="primary">
-                            {field.name.replace(/_|\./g, ' ')}
-                          </EuiHealth>
-                        )}
-                      </>
-                    )}
-                  </EuiFlexItem>
-                  <EuiFlexItem grow={false}>
-                    {isNaN(fieldCount[index]) ? (
-                      <></>
-                    ) : (
-                      <>
-                        <EuiTextColor color="secondary">
-                          {fieldCount[index]}{' '}
-                          {fieldCount[index] === 1 ? 'result' : 'results'}
-                        </EuiTextColor>
-                      </>
-                    )}
-                  </EuiFlexItem>
-                  <EuiFlexItem grow={false}>
-                    {field.isValidated ? (
-                      <>
-                        <EuiButtonIcon
-                          size="s"
-                          onClick={() => countFieldValues(field, index)}
-                          iconType="number"
-                          title="Count results"
-                        />
-                      </>
-                    ) : (
-                      <></>
-                    )}
-                  </EuiFlexItem>
-                  <EuiFlexItem grow={false}>
-                    {field.isValidated ? (
-                      <>
-                        <EuiButtonIcon
-                          size="s"
-                          color="danger"
-                          onClick={() => handleClearValues(index)}
-                          iconType="trash"
-                          title="Clear values"
-                        />
-                      </>
-                    ) : (
-                      <></>
-                    )}
-                  </EuiFlexItem>
-                  <EuiFlexItem grow={false}>
-                    {PopoverValueButton(
-                      index,
-                      standardFields,
-                      setStandardFields,
-                      searchFields,
-                      setSearchFields,
-                      isPopoverValueOpen,
-                      setIsPopoverValueOpen,
-                      valueError,
-                      setValueError,
-                      search,
-                      setSearch,
-                      setSearchCount,
-                      fieldCount,
-                      setFieldCount,
-                      selectedOperatorId,
-                      datePickerStyles,
-                      createPolicyToast,
-                      selectedSources,
-                      setSelectedSources,
-                      availableSources,
-                      setAvailableSources
-                    )}
-                  </EuiFlexItem>
-                </EuiFlexGroup>
-              </EuiFlexItem>
-            </EuiPanel>
-          ))}
-        </EuiFlexGroup>
-        <EuiSpacer size="l" />
-        {PopoverSelect(
-          standardFields,
-          setStandardFields,
-          searchFields,
-          setSearchFields,
-          selectedField,
-          setSelectedField,
-          selectedSection,
-          setSelectedSection,
-          isPopoverSelectOpen,
-          setIsPopoverSelectOpen,
-          fieldCount,
-          setFieldCount,
-          selectedSources,
-          setSelectedSources
-        )}
-      </EuiPanel>
-      <EuiSpacer size="s" />
-      <EuiRadioGroup
-        options={Operators}
-        idSelected={selectedOperatorId}
-        onChange={(id) => {
-          setSelectedOperatorId(id);
-          updateSearch(setSearch, searchFields, id, setSearchCount);
-        }}
-        name="operators group"
-        legend={{
-          children: <span>Search option</span>,
-        }}
-      />
-    </>
-  );
-};
-
-const SourceSelect = (
-  availableSources,
-  selectedSources,
-  setSelectedSources,
-  sourceSelectError,
-  setSourceSelectError
-) => {
-  if (Object.keys(availableSources).length !== 0) {
-    availableSources.forEach((source) => {
-      if (source.name) {
-        source = changeNameToLabel(source);
-      }
-    });
-
-    const onSourceChange = (selectedOptions) => {
-      setSourceSelectError(undefined);
-      setSelectedSources(selectedOptions);
-    };
-
-    const onSourceSearchChange = (value, hasMatchingOptions) => {
-      setSourceSelectError(
-        value.length === 0 || hasMatchingOptions
-          ? undefined
-          : `"${value}" is not a valid option`
-      );
-    };
-    return (
-      <>
-        <EuiTitle size="xs">
-          <h2>Partner sources</h2>
-        </EuiTitle>
-        <EuiSpacer size="s" />
-        <EuiFlexItem>
-          <EuiFormRow
-            error={sourceSelectError}
-            isInvalid={sourceSelectError !== undefined}
-          >
-            <EuiComboBox
-              placeholder="By default, all sources are selected"
-              options={availableSources}
-              selectedOptions={selectedSources}
-              onChange={onSourceChange}
-              onSearchChange={onSourceSearchChange}
-            />
-          </EuiFormRow>
-        </EuiFlexItem>
-      </>
-    );
-  } else {
-    return (
-      <p>
-        <EuiIcon type="alert" color="danger" /> No source available !
-      </p>
-    );
-  }
-};
-
 const Search = () => {
   const { t } = useTranslation('search');
-  const datePickerStyles = useStyles();
+  const datePickerStyles = styles();
   const [isLoading, setIsLoading] = useState(false);
   const [selectedTabNumber, setSelectedTabNumber] = useState(0);
   const [userHistory, setUserHistory] = useState({});
-  const [advancedSearch, setAdvancedSearch] = useState(false);
+  const [isAdvancedSearch, setIsAdvancedSearch] = useState(false);
   const [readOnlyQuery, setReadOnlyQuery] = useState(true);
   const [selectedField, setSelectedField] = useState([]);
   const [selectedSection, setSelectedSection] = useState([]);
@@ -1578,9 +99,6 @@ const Search = () => {
           setStandardFields(removeNullFields(userFields));
         }
       );
-
-      // policyField => {
-      //     policyField.forEach(
     });
     fetchSources(sessionStorage.getItem('user_id')).then((result) => {
       setSources(result);
@@ -1589,230 +107,84 @@ const Search = () => {
     fetchHistory(setUserHistory);
   }, []);
 
-  const createPolicyToast = () => {
-    const toast = {
-      title: 'Policy field selected',
-      color: 'warning',
-      iconType: 'alert',
-      toastLifeTimeMs: 15000,
-      text: (
-        <>
-          <p>You selected a private field.</p>
-          <p>
-            Access to this field was granted for specific sources, which means that your
-            search will be restricted to those.
-          </p>
-          <p>Please check the sources list before searching.</p>
-        </>
-      ),
-    };
-    setNotificationToasts(notificationToasts.concat(toast));
-  };
-
-  const createEditableQueryToast = () => {
-    const toast = {
-      title: 'Proceed with caution',
-      color: 'warning',
-      iconType: 'alert',
-      toastLifeTimeMs: 15000,
-      text: (
-        <>
-          <p>
-            Be aware that manually editing the query can spoil search results. The syntax
-            must be respected :
-          </p>
-          <ul>
-            Fields and their values should be put between brackets : &#123; &#125; - Make
-            sure every opened bracket is properly closed
-          </ul>
-          <ul>
-            "AND" and "OR" should be capitalized between different fields conditions and
-            lowercased within a field expression
-          </ul>
-          <ul>Make sure to check eventual typing mistakes</ul>
-        </>
-      ),
-    };
-    setNotificationToasts(notificationToasts.concat(toast));
-  };
-
-  const removeToast = (removedToast) => {
-    setNotificationToasts(
-      notificationToasts.filter((toast) => toast.id !== removedToast.id)
-    );
-  };
-
-  const onFormSubmit = () => {
-    setIsLoading(true);
-    const queriesWithIndices = createBasicQueriesBySource(
-      standardFields,
-      basicSearch,
-      selectedSources,
-      availableSources
-    );
-    searchQuery(queriesWithIndices).then((result) => {
-      setSearchResults(result);
-      setSelectedTabNumber(1);
-      setIsLoading(false);
-    });
-  };
-
   const tabsContent = [
     {
       id: 'tab1',
       name: 'Compose search',
       content: (
         <>
-          {advancedSearch ? (
-            <>
-              <EuiFlexGroup>
-                <EuiFlexItem grow={false}>
-                  <EuiSpacer size="s" />
-                  <EuiButtonEmpty
-                    onClick={() => {
-                      setAdvancedSearch(!advancedSearch);
-                    }}
-                  >
-                    Switch to basic search
-                  </EuiButtonEmpty>
-                </EuiFlexItem>
-              </EuiFlexGroup>
-              <EuiFlexGroup>
-                <EuiFlexItem>
-                  <EuiSpacer size="s" />
-                  {SearchBar(
-                    isLoading,
-                    setIsLoading,
-                    search,
-                    setSearch,
-                    searchResults,
-                    setSearchResults,
-                    searchFields,
-                    setSearchFields,
-                    searchName,
-                    setSearchName,
-                    searchDescription,
-                    setSearchDescription,
-                    readOnlyQuery,
-                    setReadOnlyQuery,
-                    selectedSources,
-                    setSelectedSources,
-                    availableSources,
-                    setAvailableSources,
-                    standardFields,
-                    sources,
-                    setSelectedTabNumber,
-                    searchCount,
-                    setSearchCount,
-                    setFieldCount,
-                    isReadOnlyModalOpen,
-                    setIsReadOnlyModalOpen,
-                    isSaveSearchModalOpen,
-                    setIsSaveSearchModalOpen,
-                    userHistory,
-                    setUserHistory,
-                    selectedSavedSearch,
-                    setSelectedSavedSearch,
-                    historySelectError,
-                    setHistorySelectError,
-                    selectedOperatorId,
-                    createEditableQueryToast
-                  )}
-                </EuiFlexItem>
-              </EuiFlexGroup>
-              <EuiFlexGroup>
-                <EuiFlexItem>
-                  <EuiSpacer size="s" />
-                  {FieldsPanel(
-                    standardFields,
-                    setStandardFields,
-                    searchFields,
-                    setSearchFields,
-                    selectedField,
-                    setSelectedField,
-                    selectedSection,
-                    setSelectedSection,
-                    isPopoverSelectOpen,
-                    setIsPopoverSelectOpen,
-                    isPopoverValueOpen,
-                    setIsPopoverValueOpen,
-                    valueError,
-                    setValueError,
-                    search,
-                    setSearch,
-                    setSearchCount,
-                    selectedOperatorId,
-                    setSelectedOperatorId,
-                    fieldCount,
-                    setFieldCount,
-                    availableSources,
-                    setAvailableSources,
-                    selectedSources,
-                    setSelectedSources,
-                    sources,
-                    datePickerStyles,
-                    createPolicyToast
-                  )}
-                  <EuiSpacer size="s" />
-                  {SourceSelect(
-                    availableSources,
-                    selectedSources,
-                    setSelectedSources,
-                    sourceSelectError,
-                    setSourceSelectError
-                  )}
-                </EuiFlexItem>
-              </EuiFlexGroup>
-              <EuiGlobalToastList
-                toasts={notificationToasts}
-                dismissToast={removeToast}
-                toastLifeTimeMs={2500}
-              />
-            </>
+          {isAdvancedSearch ? (
+            <AdvancedSearch
+              isLoading={isLoading}
+              setIsLoading={setIsLoading}
+              search={search}
+              setSearch={setSearch}
+              searchResults={searchResults}
+              setSearchResults={setSearchResults}
+              searchFields={searchFields}
+              setSearchFields={setSearchFields}
+              searchName={searchName}
+              setSearchName={setSearchName}
+              searchDescription={searchDescription}
+              setSearchDescription={setSearchDescription}
+              readOnlyQuery={readOnlyQuery}
+              setReadOnlyQuery={setReadOnlyQuery}
+              selectedSources={selectedSources}
+              setSelectedSources={setSelectedSources}
+              availableSources={availableSources}
+              setAvailableSources={setAvailableSources}
+              standardFields={standardFields}
+              setStandardFields={setStandardFields}
+              sources={sources}
+              setSelectedTabNumber={setSelectedTabNumber}
+              searchCount={searchCount}
+              setSearchCount={setSearchCount}
+              setFieldCount={setFieldCount}
+              isReadOnlyModalOpen={isReadOnlyModalOpen}
+              setIsReadOnlyModalOpen={setIsReadOnlyModalOpen}
+              isSaveSearchModalOpen={isSaveSearchModalOpen}
+              setIsSaveSearchModalOpen={setIsSaveSearchModalOpen}
+              userHistory={userHistory}
+              setUserHistory={setUserHistory}
+              selectedSavedSearch={selectedSavedSearch}
+              setSelectedSavedSearch={setSelectedSavedSearch}
+              historySelectError={historySelectError}
+              setHistorySelectError={setHistorySelectError}
+              selectedOperatorId={selectedOperatorId}
+              notificationToasts={notificationToasts}
+              setNotificationToasts={setNotificationToasts}
+              setIsAdvancedSearch={setIsAdvancedSearch}
+              isAdvancedSearch={isAdvancedSearch}
+              selectedField={selectedField}
+              selectedSection={selectedSection}
+              setSelectedField={setSelectedField}
+              setSelectedSection={setSelectedSection}
+              isPopoverSelectOpen={isPopoverSelectOpen}
+              setIsPopoverSelectOpen={setIsPopoverSelectOpen}
+              setIsPopoverValueOpen={setIsPopoverValueOpen}
+              isPopoverValueOpen={isPopoverValueOpen}
+              valueError={valueError}
+              setValueError={setValueError}
+              setSelectedOperatorId={setSelectedOperatorId}
+              fieldCount={fieldCount}
+              sourceSelectError={sourceSelectError}
+              datePickerStyles={datePickerStyles}
+              setSourceSelectError={setSourceSelectError}
+            />
           ) : (
-            <>
-              <EuiFlexGroup>
-                <EuiFlexItem grow={false}>
-                  <EuiSpacer size="s" />
-                  <EuiButtonEmpty
-                    onClick={() => {
-                      setAdvancedSearch(!advancedSearch);
-                    }}
-                  >
-                    Switch to advanced search
-                  </EuiButtonEmpty>
-                </EuiFlexItem>
-              </EuiFlexGroup>
-              <EuiFlexGroup>
-                <EuiFlexItem>
-                  <EuiSpacer size="s" />
-                  <form onSubmit={onFormSubmit}>
-                    <EuiFlexGroup>
-                      <EuiFlexItem>
-                        <EuiFieldSearch
-                          value={basicSearch}
-                          onChange={(e) => setBasicSearch(e.target.value)}
-                          placeholder="Search..."
-                          fullWidth
-                        />
-                      </EuiFlexItem>
-                      <EuiFlexItem grow={false}>
-                        <EuiButton type="submit" fill isDisabled={advancedSearch}>
-                          Search
-                        </EuiButton>
-                      </EuiFlexItem>
-                    </EuiFlexGroup>
-                  </form>
-                  {isLoading && (
-                    <EuiFlexGroup>
-                      <EuiFlexItem>
-                        <EuiProgress postion="fixed" size="l" color="accent" />
-                      </EuiFlexItem>
-                    </EuiFlexGroup>
-                  )}
-                </EuiFlexItem>
-              </EuiFlexGroup>
-            </>
+            <BasicSearch
+              isAdvancedSearch={isAdvancedSearch}
+              setIsAdvancedSearch={setIsAdvancedSearch}
+              standardFields={standardFields}
+              availableSources={availableSources}
+              selectedSources={selectedSources}
+              basicSearch={basicSearch}
+              setBasicSearch={setBasicSearch}
+              isLoading={isLoading}
+              setIsLoading={setIsLoading}
+              setSearchResults={setSearchResults}
+              setSelectedTabNumber={setSelectedTabNumber}
+            />
           )}
         </>
       ),
@@ -1822,7 +194,13 @@ const Search = () => {
       name: 'Results',
       content: (
         <EuiFlexGroup>
-          <EuiFlexItem>{Results(searchResults, search, basicSearch)}</EuiFlexItem>
+          <EuiFlexItem>
+            <Results
+              searchResults={searchResults}
+              search={search}
+              basicSearch={basicSearch}
+            />
+          </EuiFlexItem>
         </EuiFlexGroup>
       ),
     },
@@ -1833,7 +211,6 @@ const Search = () => {
         <EuiFlexGroup>
           <EuiFlexItem>
             <EuiSpacer size="l" />
-            {/*<a href="https://agroenvgeo.data.inra.fr/mapfishapp/"><img src={map} width="460" height="400" alt='Map' /></a>*/}
             <SearchMap searchResults={searchResults} />
           </EuiFlexItem>
         </EuiFlexGroup>
diff --git a/src/pages/search/styles.js b/src/pages/search/styles.js
new file mode 100644
index 0000000..66022dc
--- /dev/null
+++ b/src/pages/search/styles.js
@@ -0,0 +1,15 @@
+import { makeStyles } from '@material-ui/core/styles';
+
+const style = makeStyles((theme) => ({
+  container: {
+    display: 'flex',
+    flexWrap: 'wrap',
+  },
+  textField: {
+    marginLeft: theme.spacing(1),
+    marginRight: theme.spacing(1),
+    width: 240,
+  },
+}));
+
+export default style;
-- 
GitLab


From cce4543ec8b123b853eb1bcb2936d972a5121f3e Mon Sep 17 00:00:00 2001
From: rbisson <remi.bisson@inrae.fr>
Date: Tue, 7 May 2024 11:42:46 +0200
Subject: [PATCH 11/17] [BasicSearch.js]: added full translations

---
 public/locales/en/search.json               | 12 +++++++++++-
 public/locales/fr/search.json               | 10 +++++++++-
 src/pages/search/BasicSearch/BasicSearch.js |  9 ++++++---
 3 files changed, 26 insertions(+), 5 deletions(-)

diff --git a/public/locales/en/search.json b/public/locales/en/search.json
index 26c286e..a7edd65 100644
--- a/public/locales/en/search.json
+++ b/public/locales/en/search.json
@@ -1,3 +1,13 @@
 {
-  "pageTitle": "In-Sylva Metadata Search Platform"
+  "pageTitle": "In-Sylva Metadata Search Platform",
+  "basicSearch": {
+    "switchSearchMode": "Switch to advanced search",
+    "searchInputPlaceholder": "Search...",
+    "sendSearchButton": "Search"
+  },
+  "advancedSearch": {
+    "searchHistory": {
+      "placeholder": "Load a previous search"
+    }
+  }
 }
diff --git a/public/locales/fr/search.json b/public/locales/fr/search.json
index 866c88a..812460c 100644
--- a/public/locales/fr/search.json
+++ b/public/locales/fr/search.json
@@ -1,3 +1,11 @@
 {
-  "pageTitle": "Plateforme de recherche de métadonnées In-Sylva"
+  "pageTitle": "Plateforme de recherche de métadonnées In-Sylva",
+  "basicSearch": {
+    "switchSearchMode": "Passer en recherche avancée",
+    "searchInputPlaceholder": "Chercher...",
+    "sendSearchButton": "Lancer la recherche"
+  },
+  "searchHistory": {
+    "placeholder": "Charger une recherche précédente"
+  }
 }
diff --git a/src/pages/search/BasicSearch/BasicSearch.js b/src/pages/search/BasicSearch/BasicSearch.js
index 20e7296..fc9710d 100644
--- a/src/pages/search/BasicSearch/BasicSearch.js
+++ b/src/pages/search/BasicSearch/BasicSearch.js
@@ -10,6 +10,7 @@ import {
 } from '@elastic/eui';
 import { createBasicQueriesBySource } from '../../../Utils';
 import { searchQuery } from '../../../actions/source';
+import { useTranslation } from 'react-i18next';
 
 const BasicSearch = ({
   standardFields,
@@ -24,6 +25,8 @@ const BasicSearch = ({
   setSearchResults,
   setSelectedTabNumber,
 }) => {
+  const { t } = useTranslation('search');
+
   const onFormSubmit = () => {
     setIsLoading(true);
     const queriesWithIndices = createBasicQueriesBySource(
@@ -49,7 +52,7 @@ const BasicSearch = ({
               setIsAdvancedSearch(!isAdvancedSearch);
             }}
           >
-            Switch to advanced search
+            {t('basicSearch.switchSearchMode')}
           </EuiButtonEmpty>
         </EuiFlexItem>
       </EuiFlexGroup>
@@ -62,13 +65,13 @@ const BasicSearch = ({
                 <EuiFieldSearch
                   value={basicSearch}
                   onChange={(e) => setBasicSearch(e.target.value)}
-                  placeholder="Search..."
+                  placeholder={t('basicSearch.searchInputPlaceholder')}
                   fullWidth
                 />
               </EuiFlexItem>
               <EuiFlexItem grow={false}>
                 <EuiButton type="submit" fill isDisabled={isAdvancedSearch}>
-                  Search
+                  {t('basicSearch.sendSearchButton')}
                 </EuiButton>
               </EuiFlexItem>
             </EuiFlexGroup>
-- 
GitLab


From 1d755870424373d5206fe3d1ce75002da35ac183 Mon Sep 17 00:00:00 2001
From: rbisson <remi.bisson@inrae.fr>
Date: Tue, 7 May 2024 12:08:20 +0200
Subject: [PATCH 12/17] [Search.js]: added full translations

---
 public/locales/en/search.json | 5 +++++
 public/locales/fr/search.json | 5 +++++
 src/pages/search/Search.js    | 6 +++---
 3 files changed, 13 insertions(+), 3 deletions(-)

diff --git a/public/locales/en/search.json b/public/locales/en/search.json
index a7edd65..5af75fb 100644
--- a/public/locales/en/search.json
+++ b/public/locales/en/search.json
@@ -1,5 +1,10 @@
 {
   "pageTitle": "In-Sylva Metadata Search Platform",
+  "tabs": {
+    "composeSearch": "Compose search",
+    "results": "Results",
+    "map": "Map"
+  },
   "basicSearch": {
     "switchSearchMode": "Switch to advanced search",
     "searchInputPlaceholder": "Search...",
diff --git a/public/locales/fr/search.json b/public/locales/fr/search.json
index 812460c..3c50743 100644
--- a/public/locales/fr/search.json
+++ b/public/locales/fr/search.json
@@ -1,5 +1,10 @@
 {
   "pageTitle": "Plateforme de recherche de métadonnées In-Sylva",
+  "tabs": {
+    "composeSearch": "Composer une recherche",
+    "results": "Résultats",
+    "map": "Carte"
+  },
   "basicSearch": {
     "switchSearchMode": "Passer en recherche avancée",
     "searchInputPlaceholder": "Chercher...",
diff --git a/src/pages/search/Search.js b/src/pages/search/Search.js
index 16d3043..256ce8d 100644
--- a/src/pages/search/Search.js
+++ b/src/pages/search/Search.js
@@ -110,7 +110,7 @@ const Search = () => {
   const tabsContent = [
     {
       id: 'tab1',
-      name: 'Compose search',
+      name: t('tabs.composeSearch'),
       content: (
         <>
           {isAdvancedSearch ? (
@@ -191,7 +191,7 @@ const Search = () => {
     },
     {
       id: 'tab3',
-      name: 'Results',
+      name: t('tabs.results'),
       content: (
         <EuiFlexGroup>
           <EuiFlexItem>
@@ -206,7 +206,7 @@ const Search = () => {
     },
     {
       id: 'tab2',
-      name: 'Map',
+      name: t('tabs.map'),
       content: (
         <EuiFlexGroup>
           <EuiFlexItem>
-- 
GitLab


From 603541fbdaa2da73fb1c5e789b5bda3e56f46483 Mon Sep 17 00:00:00 2001
From: rbisson <remi.bisson@inrae.fr>
Date: Wed, 15 May 2024 17:14:38 +0200
Subject: [PATCH 13/17] [AdvancedSearch.js]: added full translation for
 component

---
 public/locales/en/common.json                 |   4 +-
 public/locales/en/search.json                 |  66 +-
 public/locales/fr/common.json                 |   4 +-
 public/locales/fr/search.json                 |  70 +-
 .../search/AdvancedSearch/AdvancedSearch.js   | 748 +++++++++---------
 src/pages/search/BasicSearch/BasicSearch.js   |  13 +-
 src/pages/search/Data.js                      |   4 +-
 src/pages/search/Search.js                    |  28 -
 8 files changed, 527 insertions(+), 410 deletions(-)

diff --git a/public/locales/en/common.json b/public/locales/en/common.json
index 48a5dcf..506293c 100644
--- a/public/locales/en/common.json
+++ b/public/locales/en/common.json
@@ -6,6 +6,8 @@
   "inSylvaLogoAlt": "In-Sylva logo",
   "validationActions": {
     "cancel": "Cancel",
-    "send": "Send"
+    "send": "Send",
+    "save": "Save",
+    "validate": "Validate"
   }
 }
diff --git a/public/locales/en/search.json b/public/locales/en/search.json
index 5af75fb..cebc9eb 100644
--- a/public/locales/en/search.json
+++ b/public/locales/en/search.json
@@ -5,14 +5,74 @@
     "results": "Results",
     "map": "Map"
   },
+  "sendSearchButton": "Search",
   "basicSearch": {
     "switchSearchMode": "Switch to advanced search",
-    "searchInputPlaceholder": "Search...",
-    "sendSearchButton": "Search"
+    "searchInputPlaceholder": "Search..."
   },
   "advancedSearch": {
+    "switchSearchMode": "Switch to basic search",
+    "textQueryPlaceholder": "Add fields...",
+    "countResultsButton": "Count results",
+    "resultsCount_one": "{{count}} result",
+    "resultsCount_other": "{{count}} results",
+    "editableSearchButton": "Editable",
+    "errorInvalidOption": "\"{{value}}\" is not a valid option.",
+    "fields": {
+      "title": "Field search",
+      "loadingFields": "Loading fields...",
+      "removeFieldButton": "Remove field",
+      "clearValues": "Clear values",
+      "addFieldPopover": {
+        "openPopoverButton": "Add field",
+        "title": "Select a field",
+        "button": "Add this field",
+        "selectSection": "Select a section"
+      },
+      "fieldContentPopover": {
+        "addFieldValues": "Add field values",
+        "addValue": "Add value",
+        "firstValue": "1st value",
+        "secondValue": "2nd value",
+        "inputTextValue": "Type value",
+        "betweenDate": "between",
+        "andDate": "and",
+        "selectValues": "Select values"
+      }
+    },
     "searchHistory": {
-      "placeholder": "Load a previous search"
+      "placeholder": "Load a previous request",
+      "saveSearch": "Save search",
+      "addSavedSearchName": "Search name",
+      "addSavedSearchDescription": "Description (optional)",
+      "addSavedSearchDescriptionPlaceholder": "Search description..."
+    },
+    "searchOptions": {
+      "title": "Search option",
+      "matchAll": "Match all criterias",
+      "matchAtLeastOne": "Match at least one criteria"
+    },
+    "partnerSources": {
+      "title": "Partner sources",
+      "allSourcesSelected": "By default, all sources are selected",
+      "noSourceAvailable": "No source available."
+    },
+    "policyToast": {
+      "title": "Private field selected",
+      "content": [
+        "You selected a private field.",
+        "Access to this field was granted for specific sources, which means that your search will be restricted to those.",
+        "Please check the sources list before searching."
+      ]
+    },
+    "editableQueryToast": {
+      "title": "Proceed with caution",
+      "content": {
+        "part1": "Manually editing can spoil query results. Syntax must be respected:",
+        "part2": "Fields and their values should be put between brackets: { } - Make sure every opened bracket is closed",
+        "part3": "\"AND\" and \"OR\" should be capitalized between fields and lowercase within a field expression",
+        "part4": "Make sure to check for typing errors"
+      }
     }
   }
 }
diff --git a/public/locales/fr/common.json b/public/locales/fr/common.json
index 74be05d..8e9085f 100644
--- a/public/locales/fr/common.json
+++ b/public/locales/fr/common.json
@@ -6,6 +6,8 @@
   "inSylvaLogoAlt": "Logo In-Sylva",
   "validationActions": {
     "cancel": "Annuler",
-    "send": "Envoyer"
+    "send": "Envoyer",
+    "save": "Sauvegarder",
+    "validate": "Valider"
   }
 }
diff --git a/public/locales/fr/search.json b/public/locales/fr/search.json
index 3c50743..5f6ab39 100644
--- a/public/locales/fr/search.json
+++ b/public/locales/fr/search.json
@@ -5,12 +5,74 @@
     "results": "Résultats",
     "map": "Carte"
   },
+  "sendSearchButton": "Lancer la recherche",
   "basicSearch": {
     "switchSearchMode": "Passer en recherche avancée",
-    "searchInputPlaceholder": "Chercher...",
-    "sendSearchButton": "Lancer la recherche"
+    "searchInputPlaceholder": "Chercher..."
   },
-  "searchHistory": {
-    "placeholder": "Charger une recherche précédente"
+  "advancedSearch": {
+    "switchSearchMode": "Passer en recherche basique",
+    "textQueryPlaceholder": "Ajoutez des champs...",
+    "countResultsButton": "Compter les résultats",
+    "resultsCount_one": "{{count}} résultat",
+    "resultsCount_other": "{{count}} résultats",
+    "editableSearchButton": "Modifiable",
+    "errorInvalidOption": "\"{{value}}\" n'est pas une option valide.",
+    "fields": {
+      "title": "Recherche de champ",
+      "loadingFields": "Chargement des champs...",
+      "removeFieldButton": "Supprimer le champ",
+      "clearValues": "Vider les valeurs",
+      "addFieldPopover": {
+        "openPopoverButton": "Selectionnez un champ",
+        "title": "Ajouter ce champ",
+        "button": "Selectionnez une section",
+        "selectSection": "Ajouter un champ"
+      },
+      "fieldContentPopover": {
+        "addValue": "Ajouter une valeur",
+        "addFieldValues": "Ajouter des valeurs de champ",
+        "firstValue": "1ère valeur",
+        "secondValue": "2ème valeur",
+        "inputTextValue": "Entrez une valeur",
+        "betweenDate": "entre",
+        "andDate": "et",
+        "selectValues": "Sélectionnez au moins une valeur"
+      }
+    },
+    "searchHistory": {
+      "placeholder": "Charger une recherche précédente",
+      "saveSearch": "Sauvegarder ma recherche",
+      "addSavedSearchName": "Nom de la recherche",
+      "addSavedSearchDescription": "Description (optionel)",
+      "addSavedSearchDescriptionPlaceholder": "Description de la recherche..."
+    },
+    "searchOptions": {
+      "title": "Option de recherche",
+      "matchAll": "Répondre à tous les critères",
+      "matchAtLeastOne": "Répondre à au moins un critère"
+    },
+    "partnerSources": {
+      "title": "Liste des sources de partenaires",
+      "allSourcesSelected": "Toutes les sources sont sélectionnées par défaut",
+      "noSourceAvailable": "Pas de source disponible."
+    },
+    "policyToast": {
+      "title": "Champ privé sélectionné",
+      "content": [
+        "Vous avez sélectionné un champ privé.",
+        "L'accès à ce champ à été donné par certaines sources, ce qui veut dire que votre recherche va être limitée à celles-ci.",
+        "Veuillez prếter attention à la liste des sources avant de continuer."
+      ]
+    },
+    "editableQueryToast": {
+      "title": "Procéder avec prudence",
+      "content": {
+        "part1": "En éditant manuellement la recherche, vous pouvez facilement gâcher les résultats. Veuillez respecter la syntaxe :",
+        "part2": "Les champs et leurs valeurs doivent être comprises entre accolade: { } - Bien fermer toute accolade ouverte",
+        "part3": "\"AND\" et \"OR\" en majuscule entre les champs et en minuscule à l'intérieur d'une valeur de champ",
+        "part4": "Attention à corriger vos fautes de frappe"
+      }
+    }
   }
 }
diff --git a/src/pages/search/AdvancedSearch/AdvancedSearch.js b/src/pages/search/AdvancedSearch/AdvancedSearch.js
index f8db71a..cc2030a 100644
--- a/src/pages/search/AdvancedSearch/AdvancedSearch.js
+++ b/src/pages/search/AdvancedSearch/AdvancedSearch.js
@@ -30,7 +30,7 @@ import {
   EuiTextColor,
   EuiTitle,
 } from '@elastic/eui';
-import React from 'react';
+import React, { useEffect, useState } from 'react';
 import {
   changeNameToLabel,
   createAdvancedQueriesBySource,
@@ -44,8 +44,8 @@ import {
 import { getQueryCount, searchQuery } from '../../../actions/source';
 import { DateOptions, NumericOptions, Operators } from '../Data';
 import TextField from '@material-ui/core/TextField';
-import { addUserHistory } from '../../../actions/user';
-import { fetchHistory } from '../Search';
+import { addUserHistory, fetchUserHistory } from '../../../actions/user';
+import { useTranslation } from 'react-i18next';
 
 const updateSources = (
   searchFields,
@@ -144,6 +144,18 @@ const addHistory = (
   });
 };
 
+const fetchHistory = (setUserHistory) => {
+  fetchUserHistory(sessionStorage.getItem('user_id')).then((result) => {
+    if (result[0] && result[0].ui_structure) {
+      result.forEach((item) => {
+        item.ui_structure = JSON.parse(item.ui_structure);
+        item.label = `${item.name} - ${new Date(item.createdat).toLocaleString()}`;
+      });
+    }
+    setUserHistory(result);
+  });
+};
+
 const updateSearch = (setSearch, searchFields, selectedOperatorId, setSearchCount) => {
   let searchText = '';
   searchFields.forEach((field) => {
@@ -162,59 +174,62 @@ const updateSearch = (setSearch, searchFields, selectedOperatorId, setSearchCoun
   setSearch(searchText);
 };
 
-const HistorySelect = (
+const HistorySelect = ({
   sources,
   setAvailableSources,
   setSelectedSources,
   setSearch,
-  searchFields,
-  selectedOperatorId,
-  userHistory,
-  setUserHistory,
   setSearchFields,
   setSearchCount,
   setFieldCount,
+  userHistory,
+  setUserHistory,
   selectedSavedSearch,
   setSelectedSavedSearch,
-  historySelectError,
-  setHistorySelectError
-) => {
-  if (Object.keys(userHistory).length !== 0) {
-    const onHistoryChange = (selectedSavedSearch) => {
-      setHistorySelectError(undefined);
-      if (!!selectedSavedSearch[0].query) {
-        setSelectedSavedSearch(selectedSavedSearch);
-        setSearch(selectedSavedSearch[0].query);
-        setSearchCount();
-        setFieldCount([]);
-      }
-      if (!!selectedSavedSearch[0].ui_structure) {
-        updateSources(
-          selectedSavedSearch[0].ui_structure,
-          sources,
-          setSelectedSources,
-          setAvailableSources
-        );
-        setSearchFields(selectedSavedSearch[0].ui_structure);
-      }
-    };
+}) => {
+  const [historySelectError, setHistorySelectError] = useState(undefined);
+  const { t } = useTranslation('search');
 
-    const onHistorySearchChange = (value, hasMatchingOptions) => {
-      setHistorySelectError(
-        value.length === 0 || hasMatchingOptions
-          ? undefined
-          : `"${value}" is not a valid option`
+  useEffect(() => {
+    fetchHistory(setUserHistory);
+  }, [setUserHistory]);
+
+  const onHistoryChange = (selectedSavedSearch) => {
+    setHistorySelectError(undefined);
+    if (!!selectedSavedSearch[0].query) {
+      setSelectedSavedSearch(selectedSavedSearch);
+      setSearch(selectedSavedSearch[0].query);
+      setSearchCount();
+      setFieldCount([]);
+    }
+    if (!!selectedSavedSearch[0].ui_structure) {
+      updateSources(
+        selectedSavedSearch[0].ui_structure,
+        sources,
+        setSelectedSources,
+        setAvailableSources
       );
-    };
+      setSearchFields(selectedSavedSearch[0].ui_structure);
+    }
+  };
 
-    return (
-      <>
+  const onHistorySearchChange = (value, hasMatchingOptions) => {
+    if (value.length === 0 || hasMatchingOptions) {
+      setHistorySelectError(undefined);
+    } else {
+      setHistorySelectError(t('search:advancedSearch.errorInvalidOption', { value }));
+    }
+  };
+
+  return (
+    <>
+      {userHistory && Object.keys(userHistory).length !== 0 && (
         <EuiFormRow
           error={historySelectError}
           isInvalid={historySelectError !== undefined}
         >
           <EuiComboBox
-            placeholder={'searchHistory.placeholder'}
+            placeholder={t('search:advancedSearch.searchHistory.placeholder')}
             singleSelection={{ asPlainText: true }}
             options={userHistory}
             selectedOptions={selectedSavedSearch}
@@ -222,14 +237,12 @@ const HistorySelect = (
             onSearchChange={onHistorySearchChange}
           />
         </EuiFormRow>
-      </>
-    );
-  }
+      )}
+    </>
+  );
 };
 
 const SearchBar = ({
-  isLoading,
-  setIsLoading,
   search,
   setSearch,
   setSearchResults,
@@ -253,20 +266,20 @@ const SearchBar = ({
   setFieldCount,
   isSaveSearchModalOpen,
   setIsSaveSearchModalOpen,
-  userHistory,
-  setUserHistory,
   selectedSavedSearch,
   setSelectedSavedSearch,
-  historySelectError,
-  setHistorySelectError,
   selectedOperatorId,
   createEditableQueryToast,
 }) => {
+  const { t } = useTranslation(['search', 'common']);
+  const [userHistory, setUserHistory] = useState({});
+  const [isLoading, setIsLoading] = useState(false);
+
   const closeSaveSearchModal = () => {
     setIsSaveSearchModalOpen(false);
   };
 
-  const onSendAdvancedSearch = () => {
+  const onClickAdvancedSearch = () => {
     if (search.trim()) {
       setIsLoading(true);
       const queriesWithIndices = createAdvancedQueriesBySource(
@@ -283,19 +296,48 @@ const SearchBar = ({
     }
   };
 
-  let saveSearchModal;
+  const onClickCountResults = () => {
+    if (!!search) {
+      const queriesWithIndices = createAdvancedQueriesBySource(
+        standardFields,
+        search,
+        selectedSources,
+        availableSources
+      );
+      getQueryCount(queriesWithIndices).then((result) => {
+        if (result || result === 0) setSearchCount(result);
+      });
+    }
+  };
+
+  const onClickSaveSearch = () => {
+    if (!!searchName) {
+      addHistory(
+        sessionStorage.getItem('user_id'),
+        search,
+        searchName,
+        searchFields,
+        searchDescription,
+        setUserHistory
+      );
+      setSearchName('');
+      setSearchDescription('');
+      closeSaveSearchModal();
+    }
+  };
 
-  if (isSaveSearchModalOpen) {
-    saveSearchModal = (
+  const SaveSearchModal = () => {
+    return (
       <EuiOverlayMask>
         <EuiModal onClose={closeSaveSearchModal} initialFocus="[name=searchName]">
           <EuiModalHeader>
-            <EuiModalHeaderTitle>Save search</EuiModalHeaderTitle>
+            <EuiModalHeaderTitle>
+              {t('advancedSearch.searchHistory.saveSearch')}
+            </EuiModalHeaderTitle>
           </EuiModalHeader>
-
           <EuiModalBody>
             <EuiForm>
-              <EuiFormRow label="Search name">
+              <EuiFormRow label={t('advancedSearch.searchHistory.addSavedSearchName')}>
                 <EuiFieldText
                   name="searchName"
                   value={searchName}
@@ -304,11 +346,15 @@ const SearchBar = ({
                   }}
                 />
               </EuiFormRow>
-              <EuiFormRow label="Description (optional)">
+              <EuiFormRow
+                label={t('advancedSearch.searchHistory.addSavedSearchDescription')}
+              >
                 <EuiTextArea
                   value={searchDescription}
                   onChange={(e) => setSearchDescription(e.target.value)}
-                  placeholder="Search description..."
+                  placeholder={t(
+                    'advancedSearch.searchHistory.addSavedSearchDescriptionPlaceholder'
+                  )}
                   fullWidth
                   compressed
                 />
@@ -322,33 +368,21 @@ const SearchBar = ({
                 closeSaveSearchModal();
               }}
             >
-              Cancel
+              {t('common:validationActions.cancel')}
             </EuiButtonEmpty>
             <EuiButton
               onClick={() => {
-                if (!!searchName) {
-                  addHistory(
-                    sessionStorage.getItem('user_id'),
-                    search,
-                    searchName,
-                    searchFields,
-                    searchDescription,
-                    setUserHistory
-                  );
-                  setSearchName('');
-                  setSearchDescription('');
-                  closeSaveSearchModal();
-                }
+                onClickSaveSearch();
               }}
               fill
             >
-              Save
+              {t('common:validationActions.save')}
             </EuiButton>
           </EuiModalFooter>
         </EuiModal>
       </EuiOverlayMask>
     );
-  }
+  };
 
   return (
     <>
@@ -358,7 +392,7 @@ const SearchBar = ({
             readOnly={readOnlyQuery}
             value={search}
             onChange={(e) => setSearch(e.target.value)}
-            placeholder="Add fields..."
+            placeholder={t('search:advancedSearch.textQueryPlaceholder')}
             fullWidth
           />
         </EuiFlexItem>
@@ -367,21 +401,19 @@ const SearchBar = ({
             size="s"
             fill
             onClick={() => {
-              onSendAdvancedSearch();
+              onClickAdvancedSearch();
             }}
           >
-            Search
+            {t('search:sendSearchButton')}
           </EuiButton>
           <EuiSpacer size="s" />
-          {isNaN(searchCount) ? (
-            <></>
-          ) : (
+          {!isNaN(searchCount) && (
             <>
               <EuiTextColor
                 color="secondary"
                 style={{ display: 'flex', justifyContent: 'center' }}
               >
-                {searchCount} {searchCount === 1 ? 'result' : 'results'}
+                {t('search:advancedSearch.resultsCount', { count: searchCount })}
               </EuiTextColor>
               <EuiSpacer size="s" />
             </>
@@ -389,20 +421,10 @@ const SearchBar = ({
           <EuiButton
             size="s"
             onClick={() => {
-              if (!!search) {
-                const queriesWithIndices = createAdvancedQueriesBySource(
-                  standardFields,
-                  search,
-                  selectedSources,
-                  availableSources
-                );
-                getQueryCount(queriesWithIndices).then((result) => {
-                  if (result || result === 0) setSearchCount(result);
-                });
-              }
+              onClickCountResults();
             }}
           >
-            Count results
+            {t('search:advancedSearch.countResultsButton')}
           </EuiButton>
           <EuiSpacer size="s" />
           <EuiButton
@@ -411,13 +433,13 @@ const SearchBar = ({
               setIsSaveSearchModalOpen(true);
             }}
           >
-            Save search
+            {t('search:advancedSearch.searchHistory.saveSearch')}
           </EuiButton>
-          {saveSearchModal}
+          {isSaveSearchModalOpen && <SaveSearchModal />}
           <EuiSpacer size="s" />
           <EuiSwitch
             compressed
-            label={'Editable'}
+            label={t('search:advancedSearch.editableSearchButton')}
             checked={!readOnlyQuery}
             onChange={() => {
               setReadOnlyQuery(!readOnlyQuery);
@@ -438,32 +460,29 @@ const SearchBar = ({
       <EuiSpacer size="s" />
       <EuiFlexGroup>
         <EuiFlexItem>
-          {HistorySelect(
-            sources,
-            setAvailableSources,
-            setSelectedSources,
-            setSearch,
-            searchFields,
-            selectedOperatorId,
-            userHistory,
-            setUserHistory,
-            setSearchFields,
-            setSearchCount,
-            setFieldCount,
-            selectedSavedSearch,
-            setSelectedSavedSearch,
-            historySelectError,
-            setHistorySelectError
-          )}
+          <HistorySelect
+            sources={sources}
+            setAvailableSources={setAvailableSources}
+            setSelectedSources={setSelectedSources}
+            setSearch={setSearch}
+            searchFields={searchFields}
+            selectedOperatorId={selectedOperatorId}
+            userHistory={userHistory}
+            setUserHistory={setUserHistory}
+            setSearchFields={setSearchFields}
+            setSearchCount={setSearchCount}
+            setFieldCount={setFieldCount}
+            selectedSavedSearch={selectedSavedSearch}
+            setSelectedSavedSearch={setSelectedSavedSearch}
+          />
         </EuiFlexItem>
       </EuiFlexGroup>
     </>
   );
 };
 
-const PopoverSelect = (
+const PopoverSelect = ({
   standardFields,
-  setStandardFields,
   searchFields,
   setSearchFields,
   selectedField,
@@ -471,9 +490,11 @@ const PopoverSelect = (
   selectedSection,
   setSelectedSection,
   isPopoverSelectOpen,
-  setIsPopoverSelectOpen
-) => {
-  const handleAddfield = () => {
+  setIsPopoverSelectOpen,
+}) => {
+  const { t } = useTranslation('search');
+
+  const handleAddField = () => {
     if (!!selectedField[0]) {
       const field = standardFields.find(
         (item) =>
@@ -517,7 +538,7 @@ const PopoverSelect = (
       return (
         <>
           <EuiComboBox
-            placeholder="Select a field"
+            placeholder={t('search:advancedSearch.fields.addFieldPopover.title')}
             singleSelection={{ asPlainText: true }}
             options={getFieldsBySection(standardFields, selectedSection[0])}
             selectedOptions={selectedField}
@@ -529,13 +550,13 @@ const PopoverSelect = (
             <EuiButton
               size="s"
               onClick={() => {
-                handleAddfield();
+                handleAddField();
                 setIsPopoverSelectOpen(false);
                 setSelectedSection([]);
                 setSelectedField([]);
               }}
             >
-              Add this field
+              {t('search:advancedSearch.fields.addFieldPopover.button')}
             </EuiButton>
           </EuiPopoverFooter>
         </>
@@ -552,16 +573,18 @@ const PopoverSelect = (
           iconSide="left"
           onClick={() => setIsPopoverSelectOpen(!isPopoverSelectOpen)}
         >
-          Add field
+          {t('search:advancedSearch.fields.addFieldPopover.openPopoverButton')}
         </EuiButton>
       }
       isOpen={isPopoverSelectOpen}
       closePopover={() => setIsPopoverSelectOpen(false)}
     >
       <div style={{ width: 'intrinsic', minWidth: 240 }}>
-        <EuiPopoverTitle>Select a field</EuiPopoverTitle>
+        <EuiPopoverTitle>
+          {t('search:advancedSearch.fields.addFieldPopover.title')}
+        </EuiPopoverTitle>
         <EuiComboBox
-          placeholder="Select a section"
+          placeholder={t('search:advancedSearch.fields.addFieldPopover.selectSection')}
           singleSelection={{ asPlainText: true }}
           options={getSections(standardFields)}
           selectedOptions={selectedSection}
@@ -577,15 +600,13 @@ const PopoverSelect = (
   );
 };
 
-const PopoverValueContent = (
+const PopoverValueContent = ({
   index,
   standardFields,
-  setStandardFields,
   searchFields,
   setSearchFields,
   valueError,
   setValueError,
-  search,
   setSearch,
   setSearchCount,
   fieldCount,
@@ -598,14 +619,16 @@ const PopoverValueContent = (
   selectedSources,
   setSelectedSources,
   availableSources,
-  setAvailableSources
-) => {
+  setAvailableSources,
+}) => {
+  const { t } = useTranslation(['search', 'common']);
+
   const onValueSearchChange = (value, hasMatchingOptions) => {
-    setValueError(
-      value.length === 0 || hasMatchingOptions
-        ? undefined
-        : `"${value}" is not a valid option`
-    );
+    if (value.length === 0 || hasMatchingOptions) {
+      setValueError(undefined);
+    } else {
+      setValueError(t('search:advancedSearch.errorInvalidOption', { value }));
+    }
   };
 
   const validateFieldValues = () => {
@@ -692,7 +715,7 @@ const PopoverValueContent = (
               );
             }}
           >
-            Add value
+            {t('search:advancedSearch.fields.fieldContentPopover.addValue')}
           </EuiButton>
           <EuiButton
             size="s"
@@ -702,7 +725,7 @@ const PopoverValueContent = (
               setIsPopoverValueOpen(updateArrayElement(isPopoverValueOpen, index, false));
             }}
           >
-            Validate
+            {t('common:validationActions.validate')}
           </EuiButton>
         </EuiPopoverFooter>
       );
@@ -737,7 +760,9 @@ const PopoverValueContent = (
         <>
           <EuiFlexItem>
             <EuiFieldText
-              placeholder={'Type values'}
+              placeholder={t(
+                'search:advancedSearch.fields.fieldContentPopover.inputTextValue'
+              )}
               value={searchFields[index].values}
               onChange={(e) =>
                 setSearchFields(
@@ -757,7 +782,7 @@ const PopoverValueContent = (
                 );
               }}
             >
-              Validate
+              {t('common:validationActions.validate')}
             </EuiButton>
           </EuiPopoverFooter>
         </>
@@ -767,7 +792,9 @@ const PopoverValueContent = (
         <>
           <EuiFormRow error={valueError} isInvalid={valueError !== undefined}>
             <EuiComboBox
-              placeholder={'Select values'}
+              placeholder={t(
+                'search:advancedSearch.fields.fieldContentPopover.selectValues'
+              )}
               options={getListFieldValues()}
               selectedOptions={searchFields[index].values}
               onChange={(selectedOptions) => {
@@ -790,7 +817,7 @@ const PopoverValueContent = (
                 );
               }}
             >
-              Validate
+              {t('common:validationActions.validate')}
             </EuiButton>
           </EuiPopoverFooter>
         </>
@@ -804,7 +831,9 @@ const PopoverValueContent = (
                 <>
                   <EuiFlexItem>
                     <EuiFieldText
-                      placeholder={'1st value'}
+                      placeholder={t(
+                        'search:advancedSearch.fields.fieldContentPopover.firstValue'
+                      )}
                       value={searchFields[index].values[i].value1}
                       onChange={(e) => {
                         setSearchFields(
@@ -823,7 +852,9 @@ const PopoverValueContent = (
                   </EuiFlexItem>
                   <EuiFlexItem>
                     <EuiFieldText
-                      placeholder={'2nd value'}
+                      placeholder={t(
+                        'search:advancedSearch.fields.fieldContentPopover.secondValue'
+                      )}
                       value={searchFields[index].values[i].value2}
                       onChange={(e) =>
                         setSearchFields(
@@ -849,7 +880,9 @@ const PopoverValueContent = (
                 <>
                   <EuiFlexItem>
                     <EuiFieldText
-                      placeholder={'Type value'}
+                      placeholder={t(
+                        'search:advancedSearch.fields.fieldContentPopover.inputTextValue'
+                      )}
                       value={searchFields[index].values[i].value1}
                       onChange={(e) => {
                         setSearchFields(
@@ -901,7 +934,9 @@ const PopoverValueContent = (
                 <>
                   <form className={datePickerStyles.container} noValidate>
                     <TextField
-                      label="between"
+                      label={t(
+                        'search:advancedSearch.fields.fieldContentPopover.betweenDate'
+                      )}
                       type="date"
                       defaultValue={
                         !!searchFields[index].values[i].startDate
@@ -929,7 +964,9 @@ const PopoverValueContent = (
                   </form>
                   <form className={datePickerStyles.container} noValidate>
                     <TextField
-                      label="and"
+                      label={t(
+                        'search:advancedSearch.fields.fieldContentPopover.andDate'
+                      )}
                       type="date"
                       defaultValue={
                         !!searchFields[index].values[i].endDate
@@ -1019,17 +1056,15 @@ const PopoverValueContent = (
   }
 };
 
-const PopoverValueButton = (
+const PopoverValueButton = ({
   index,
   standardFields,
-  setStandardFields,
   searchFields,
   setSearchFields,
   isPopoverValueOpen,
   setIsPopoverValueOpen,
   valueError,
   setValueError,
-  search,
   setSearch,
   setSearchCount,
   fieldCount,
@@ -1040,8 +1075,10 @@ const PopoverValueButton = (
   selectedSources,
   setSelectedSources,
   availableSources,
-  setAvailableSources
-) => {
+  setAvailableSources,
+}) => {
+  const { t } = useTranslation('search');
+
   return (
     <EuiPopover
       panelPaddingSize="s"
@@ -1055,7 +1092,10 @@ const PopoverValueButton = (
             )
           }
           iconType="documentEdit"
-          title="Give field values"
+          title={t('search:advancedSearch.fields.fieldContentPopover.addFieldValues')}
+          aria-label={t(
+            'search:advancedSearch.fields.fieldContentPopover.addFieldValues'
+          )}
         />
       }
       isOpen={isPopoverValueOpen[index]}
@@ -1064,35 +1104,33 @@ const PopoverValueButton = (
       }
     >
       <div style={{ width: 240 }}>
-        {PopoverValueContent(
-          index,
-          standardFields,
-          setStandardFields,
-          searchFields,
-          setSearchFields,
-          valueError,
-          setValueError,
-          search,
-          setSearch,
-          setSearchCount,
-          fieldCount,
-          setFieldCount,
-          isPopoverValueOpen,
-          setIsPopoverValueOpen,
-          selectedOperatorId,
-          datePickerStyles,
-          createPolicyToast,
-          selectedSources,
-          setSelectedSources,
-          availableSources,
-          setAvailableSources
-        )}
+        <PopoverValueContent
+          index={index}
+          standardFields={standardFields}
+          searchFields={searchFields}
+          setSearchFields={setSearchFields}
+          valueError={valueError}
+          setValueError={setValueError}
+          setSearch={setSearch}
+          setSearchCount={setSearchCount}
+          fieldCount={fieldCount}
+          setFieldCount={setFieldCount}
+          isPopoverValueOpen={isPopoverValueOpen}
+          setIsPopoverValueOpen={setIsPopoverValueOpen}
+          selectedOperatorId={selectedOperatorId}
+          datePickerStyles={datePickerStyles}
+          createPolicyToast={createPolicyToast}
+          selectedSources={selectedSources}
+          setSelectedSources={setSelectedSources}
+          availableSources={availableSources}
+          setAvailableSources={setAvailableSources}
+        />
       </div>
     </EuiPopover>
   );
 };
 
-const FieldsPanel = (
+const FieldsPanel = ({
   standardFields,
   setStandardFields,
   searchFields,
@@ -1120,8 +1158,10 @@ const FieldsPanel = (
   setSelectedSources,
   sources,
   datePickerStyles,
-  createPolicyToast
-) => {
+  createPolicyToast,
+}) => {
+  const { t } = useTranslation('search');
+
   const countFieldValues = (field, index) => {
     const fieldStr = `{${fieldValuesToString(field)}}`;
     const queriesWithIndices = createAdvancedQueriesBySource(
@@ -1144,7 +1184,7 @@ const FieldsPanel = (
   };
 
   const handleClearValues = (index) => {
-    let updatedSearchFields = [];
+    let updatedSearchFields;
     switch (searchFields[index].type) {
       case 'Text':
         updatedSearchFields = updateArrayElement(
@@ -1192,13 +1232,13 @@ const FieldsPanel = (
   };
 
   if (standardFields === []) {
-    return <h2>Loading user fields...</h2>;
+    return <h2>{t('search:advancedSearch.fields.loadingFields')}</h2>;
   }
 
   return (
     <>
       <EuiTitle size="xs">
-        <h2>Field search</h2>
+        <h2>{t('search:advancedSearch.fields.title')}</h2>
       </EuiTitle>
       <EuiPanel paddingSize="m">
         <EuiFlexGroup direction="column">
@@ -1212,7 +1252,8 @@ const FieldsPanel = (
                       color="danger"
                       onClick={() => handleRemoveField(index)}
                       iconType="indexClose"
-                      title="Remove field"
+                      title={t('search:advancedSearch.fields.removeFieldButton')}
+                      aria-label={t('search:advancedSearch.fields.removeFieldButton')}
                     />
                   </EuiFlexItem>
                   <EuiFlexItem>
@@ -1243,70 +1284,61 @@ const FieldsPanel = (
                     )}
                   </EuiFlexItem>
                   <EuiFlexItem grow={false}>
-                    {isNaN(fieldCount[index]) ? (
-                      <></>
-                    ) : (
-                      <>
-                        <EuiTextColor color="secondary">
-                          {fieldCount[index]}{' '}
-                          {fieldCount[index] === 1 ? 'result' : 'results'}
-                        </EuiTextColor>
-                      </>
+                    {!isNaN(fieldCount[index]) && (
+                      <EuiTextColor color="secondary">
+                        {t('search:advancedSearch.resultsCount', {
+                          count: fieldCount[index],
+                        })}
+                      </EuiTextColor>
                     )}
                   </EuiFlexItem>
                   <EuiFlexItem grow={false}>
-                    {field.isValidated ? (
-                      <>
-                        <EuiButtonIcon
-                          size="s"
-                          onClick={() => countFieldValues(field, index)}
-                          iconType="number"
-                          title="Count results"
-                        />
-                      </>
-                    ) : (
-                      <></>
+                    {field.isValidated && (
+                      <EuiButtonIcon
+                        size="s"
+                        onClick={() => countFieldValues(field, index)}
+                        iconType="number"
+                        title={t('search:advancedSearch.countResultsButton')}
+                        aria-label={t('search:advancedSearch.countResultsButton')}
+                      />
                     )}
                   </EuiFlexItem>
                   <EuiFlexItem grow={false}>
-                    {field.isValidated ? (
-                      <>
-                        <EuiButtonIcon
-                          size="s"
-                          color="danger"
-                          onClick={() => handleClearValues(index)}
-                          iconType="trash"
-                          title="Clear values"
-                        />
-                      </>
-                    ) : (
-                      <></>
+                    {field.isValidated && (
+                      <EuiButtonIcon
+                        size="s"
+                        color="danger"
+                        onClick={() => handleClearValues(index)}
+                        iconType="trash"
+                        title={t('search:advancedSearch.fields.clearValues')}
+                        aria-label={t('search:advancedSearch.fields.clearValues')}
+                      />
                     )}
                   </EuiFlexItem>
                   <EuiFlexItem grow={false}>
-                    {PopoverValueButton(
-                      index,
-                      standardFields,
-                      setStandardFields,
-                      searchFields,
-                      setSearchFields,
-                      isPopoverValueOpen,
-                      setIsPopoverValueOpen,
-                      valueError,
-                      setValueError,
-                      search,
-                      setSearch,
-                      setSearchCount,
-                      fieldCount,
-                      setFieldCount,
-                      selectedOperatorId,
-                      datePickerStyles,
-                      createPolicyToast,
-                      selectedSources,
-                      setSelectedSources,
-                      availableSources,
-                      setAvailableSources
-                    )}
+                    <PopoverValueButton
+                      index={index}
+                      standardFields={standardFields}
+                      setStandardFields={setStandardFields}
+                      searchFields={searchFields}
+                      setSearchFields={setSearchFields}
+                      isPopoverValueOpen={isPopoverValueOpen}
+                      setIsPopoverValueOpen={setIsPopoverValueOpen}
+                      valueError={valueError}
+                      setValueError={setValueError}
+                      search={search}
+                      setSearch={setSearch}
+                      setSearchCount={setSearchCount}
+                      fieldCount={fieldCount}
+                      setFieldCount={setFieldCount}
+                      selectedOperatorId={selectedOperatorId}
+                      datePickerStyles={datePickerStyles}
+                      createPolicyToast={createPolicyToast}
+                      selectedSources={selectedSources}
+                      setSelectedSources={setSelectedSources}
+                      availableSources={availableSources}
+                      setAvailableSources={setAvailableSources}
+                    />
                   </EuiFlexItem>
                 </EuiFlexGroup>
               </EuiFlexItem>
@@ -1314,26 +1346,28 @@ const FieldsPanel = (
           ))}
         </EuiFlexGroup>
         <EuiSpacer size="l" />
-        {PopoverSelect(
-          standardFields,
-          setStandardFields,
-          searchFields,
-          setSearchFields,
-          selectedField,
-          setSelectedField,
-          selectedSection,
-          setSelectedSection,
-          isPopoverSelectOpen,
-          setIsPopoverSelectOpen,
-          fieldCount,
-          setFieldCount,
-          selectedSources,
-          setSelectedSources
-        )}
+        <PopoverSelect
+          standardFields={standardFields}
+          setStandardFields={setStandardFields}
+          searchFields={searchFields}
+          setSearchFields={setSearchFields}
+          selectedField={selectedField}
+          setSelectedField={setSelectedField}
+          selectedSection={selectedSection}
+          setSelectedSection={setSelectedSection}
+          isPopoverSelectOpen={isPopoverSelectOpen}
+          setIsPopoverSelectOpen={setIsPopoverSelectOpen}
+          fieldCount={fieldCount}
+          setFieldCount={setFieldCount}
+          selectedSources={selectedSources}
+          setSelectedSources={setSelectedSources}
+        />
       </EuiPanel>
       <EuiSpacer size="s" />
       <EuiRadioGroup
-        options={Operators}
+        options={Operators.map((operator) => {
+          return { ...operator, label: t(operator.label) };
+        })}
         idSelected={selectedOperatorId}
         onChange={(id) => {
           setSelectedOperatorId(id);
@@ -1341,73 +1375,72 @@ const FieldsPanel = (
         }}
         name="operators group"
         legend={{
-          children: <span>Search option</span>,
+          children: <span>{t('search:advancedSearch.searchOptions.title')}</span>,
         }}
       />
     </>
   );
 };
 
-const SourceSelect = (
+const SourceSelect = ({
   availableSources,
   selectedSources,
   setSelectedSources,
   sourceSelectError,
-  setSourceSelectError
-) => {
-  if (Object.keys(availableSources).length !== 0) {
-    availableSources.forEach((source) => {
-      if (source.name) {
-        source = changeNameToLabel(source);
-      }
-    });
-
-    const onSourceChange = (selectedOptions) => {
-      setSourceSelectError(undefined);
-      setSelectedSources(selectedOptions);
-    };
+  setSourceSelectError,
+}) => {
+  const { t } = useTranslation('search');
 
-    const onSourceSearchChange = (value, hasMatchingOptions) => {
-      setSourceSelectError(
-        value.length === 0 || hasMatchingOptions
-          ? undefined
-          : `"${value}" is not a valid option`
-      );
-    };
-    return (
-      <>
-        <EuiTitle size="xs">
-          <h2>Partner sources</h2>
-        </EuiTitle>
-        <EuiSpacer size="s" />
-        <EuiFlexItem>
-          <EuiFormRow
-            error={sourceSelectError}
-            isInvalid={sourceSelectError !== undefined}
-          >
-            <EuiComboBox
-              placeholder="By default, all sources are selected"
-              options={availableSources}
-              selectedOptions={selectedSources}
-              onChange={onSourceChange}
-              onSearchChange={onSourceSearchChange}
-            />
-          </EuiFormRow>
-        </EuiFlexItem>
-      </>
-    );
-  } else {
+  if (Object.keys(availableSources).length === 0) {
     return (
       <p>
-        <EuiIcon type="alert" color="danger" /> No source available !
+        <EuiIcon type="alert" color="danger" />
       </p>
     );
   }
+  availableSources.forEach((source) => {
+    if (source.name) {
+      source = changeNameToLabel(source);
+    }
+  });
+
+  const onSourceChange = (selectedOptions) => {
+    setSourceSelectError(undefined);
+    setSelectedSources(selectedOptions);
+  };
+
+  const onSourceSearchChange = (value, hasMatchingOptions) => {
+    if (value.length === 0 || hasMatchingOptions) {
+      setSourceSelectError(undefined);
+    } else {
+      setSourceSelectError(
+        t('search:advancedSearch.errorInvalidOption', { value: value })
+      );
+    }
+  };
+
+  return (
+    <>
+      <EuiTitle size="xs">
+        <h2>{t('search:advancedSearch.partnerSources.title')}</h2>
+      </EuiTitle>
+      <EuiSpacer size="s" />
+      <EuiFlexItem>
+        <EuiFormRow error={sourceSelectError} isInvalid={sourceSelectError !== undefined}>
+          <EuiComboBox
+            placeholder={t('search:advancedSearch.partnerSources.allSourcesSelected')}
+            options={availableSources}
+            selectedOptions={selectedSources}
+            onChange={onSourceChange}
+            onSearchChange={onSourceSearchChange}
+          />
+        </EuiFormRow>
+      </EuiFlexItem>
+    </>
+  );
 };
 
 const AdvancedSearch = ({
-  isLoading,
-  setIsLoading,
   search,
   setSearch,
   searchResults,
@@ -1439,11 +1472,7 @@ const AdvancedSearch = ({
   setUserHistory,
   selectedSavedSearch,
   setSelectedSavedSearch,
-  historySelectError,
-  setHistorySelectError,
   selectedOperatorId,
-  notificationToasts,
-  setNotificationToasts,
   setIsAdvancedSearch,
   isAdvancedSearch,
   selectedField,
@@ -1462,20 +1491,20 @@ const AdvancedSearch = ({
   datePickerStyles,
   setSourceSelectError,
 }) => {
+  const { t } = useTranslation('search');
+  const [notificationToasts, setNotificationToasts] = useState([]);
+
   const createPolicyToast = () => {
     const toast = {
-      title: 'Policy field selected',
+      title: t('search:advancedSearch.policyToast.title'),
       color: 'warning',
       iconType: 'alert',
       toastLifeTimeMs: 15000,
       text: (
         <>
-          <p>You selected a private field.</p>
-          <p>
-            Access to this field was granted for specific sources, which means that your
-            search will be restricted to those.
-          </p>
-          <p>Please check the sources list before searching.</p>
+          <p>{t('search:advancedSearch.policyToast.content.0')}</p>
+          <p>{t('search:advancedSearch.policyToast.content.1')}</p>
+          <p>{t('search:advancedSearch.policyToast.content.2')}</p>
         </>
       ),
     };
@@ -1484,25 +1513,18 @@ const AdvancedSearch = ({
 
   const createEditableQueryToast = () => {
     const toast = {
-      title: 'Proceed with caution',
+      title: t('search:advancedSearch.policyToast.title'),
       color: 'warning',
       iconType: 'alert',
       toastLifeTimeMs: 15000,
       text: (
         <>
-          <p>
-            Be aware that manually editing the query can spoil search results. The syntax
-            must be respected :
-          </p>
-          <ul>
-            Fields and their values should be put between brackets : &#123; &#125; - Make
-            sure every opened bracket is properly closed
-          </ul>
+          <p>{t('search:advancedSearch.editableQueryToast.content.part1')}</p>
           <ul>
-            "AND" and "OR" should be capitalized between different fields conditions and
-            lowercased within a field expression
+            <li>{t('search:advancedSearch.editableQueryToast.content.part2')}</li>
+            <li>{t('search:advancedSearch.editableQueryToast.content.part3')}</li>
+            <li>{t('search:advancedSearch.editableQueryToast.content.part4')}</li>
           </ul>
-          <ul>Make sure to check eventual typing mistakes</ul>
         </>
       ),
     };
@@ -1525,7 +1547,7 @@ const AdvancedSearch = ({
               setIsAdvancedSearch(!isAdvancedSearch);
             }}
           >
-            Switch to basic search
+            {t('search:advancedSearch.switchSearchMode')}
           </EuiButtonEmpty>
         </EuiFlexItem>
       </EuiFlexGroup>
@@ -1533,8 +1555,6 @@ const AdvancedSearch = ({
         <EuiFlexItem>
           <EuiSpacer size="s" />
           <SearchBar
-            isLoading={isLoading}
-            setIsLoading={setIsLoading}
             search={search}
             setSearch={setSearch}
             searchResults={searchResults}
@@ -1565,8 +1585,6 @@ const AdvancedSearch = ({
             setUserHistory={setUserHistory}
             selectedSavedSearch={selectedSavedSearch}
             setSelectedSavedSearch={setSelectedSavedSearch}
-            historySelectError={historySelectError}
-            setHistorySelectError={setHistorySelectError}
             selectedOperatorId={selectedOperatorId}
             createEditableQueryToast={createEditableQueryToast}
           />
@@ -1575,44 +1593,44 @@ const AdvancedSearch = ({
       <EuiFlexGroup>
         <EuiFlexItem>
           <EuiSpacer size="s" />
-          {FieldsPanel(
-            standardFields,
-            setStandardFields,
-            searchFields,
-            setSearchFields,
-            selectedField,
-            setSelectedField,
-            selectedSection,
-            setSelectedSection,
-            isPopoverSelectOpen,
-            setIsPopoverSelectOpen,
-            isPopoverValueOpen,
-            setIsPopoverValueOpen,
-            valueError,
-            setValueError,
-            search,
-            setSearch,
-            setSearchCount,
-            selectedOperatorId,
-            setSelectedOperatorId,
-            fieldCount,
-            setFieldCount,
-            availableSources,
-            setAvailableSources,
-            selectedSources,
-            setSelectedSources,
-            sources,
-            datePickerStyles,
-            createPolicyToast
-          )}
+          <FieldsPanel
+            standardFields={standardFields}
+            setStandardFields={setStandardFields}
+            searchFields={searchFields}
+            setSearchFields={setSearchFields}
+            selectedField={selectedField}
+            setSelectedField={setSelectedField}
+            selectedSection={selectedSection}
+            setSelectedSection={setSelectedSection}
+            isPopoverSelectOpen={isPopoverSelectOpen}
+            setIsPopoverSelectOpen={setIsPopoverSelectOpen}
+            isPopoverValueOpen={isPopoverValueOpen}
+            setIsPopoverValueOpen={setIsPopoverValueOpen}
+            valueError={valueError}
+            setValueError={setValueError}
+            search={search}
+            setSearch={setSearch}
+            setSearchCount={setSearchCount}
+            selectedOperatorId={selectedOperatorId}
+            setSelectedOperatorId={setSelectedOperatorId}
+            fieldCount={fieldCount}
+            setFieldCount={setFieldCount}
+            availableSources={availableSources}
+            setAvailableSources={setAvailableSources}
+            selectedSources={selectedSources}
+            setSelectedSources={setSelectedSources}
+            sources={sources}
+            datePickerStyles={datePickerStyles}
+            createPolicyToast={createPolicyToast}
+          />
           <EuiSpacer size="s" />
-          {SourceSelect(
-            availableSources,
-            selectedSources,
-            setSelectedSources,
-            sourceSelectError,
-            setSourceSelectError
-          )}
+          <SourceSelect
+            availableSources={availableSources}
+            selectedSources={selectedSources}
+            setSelectedSources={setSelectedSources}
+            sourceSelectError={sourceSelectError}
+            setSourceSelectError={setSourceSelectError}
+          />
         </EuiFlexItem>
       </EuiFlexGroup>
       <EuiGlobalToastList
diff --git a/src/pages/search/BasicSearch/BasicSearch.js b/src/pages/search/BasicSearch/BasicSearch.js
index fc9710d..6f8892d 100644
--- a/src/pages/search/BasicSearch/BasicSearch.js
+++ b/src/pages/search/BasicSearch/BasicSearch.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import React, { useState } from 'react';
 import {
   EuiButton,
   EuiButtonEmpty,
@@ -18,14 +18,13 @@ const BasicSearch = ({
   selectedSources,
   basicSearch,
   setBasicSearch,
-  isLoading,
   setIsAdvancedSearch,
   isAdvancedSearch,
-  setIsLoading,
   setSearchResults,
   setSelectedTabNumber,
 }) => {
   const { t } = useTranslation('search');
+  const [isLoading, setIsLoading] = useState(false);
 
   const onFormSubmit = () => {
     setIsLoading(true);
@@ -38,7 +37,9 @@ const BasicSearch = ({
     searchQuery(queriesWithIndices).then((result) => {
       setSearchResults(result);
       setSelectedTabNumber(1);
-      setIsLoading(false);
+      if (isLoading) {
+        setIsLoading(false);
+      }
     });
   };
 
@@ -59,7 +60,7 @@ const BasicSearch = ({
       <EuiFlexGroup>
         <EuiFlexItem>
           <EuiSpacer size="s" />
-          <form onSubmit={onFormSubmit}>
+          <form onSubmit={() => onFormSubmit()}>
             <EuiFlexGroup>
               <EuiFlexItem>
                 <EuiFieldSearch
@@ -71,7 +72,7 @@ const BasicSearch = ({
               </EuiFlexItem>
               <EuiFlexItem grow={false}>
                 <EuiButton type="submit" fill isDisabled={isAdvancedSearch}>
-                  {t('basicSearch.sendSearchButton')}
+                  {t('sendSearchButton')}
                 </EuiButton>
               </EuiFlexItem>
             </EuiFlexGroup>
diff --git a/src/pages/search/Data.js b/src/pages/search/Data.js
index c657764..d0a399a 100644
--- a/src/pages/search/Data.js
+++ b/src/pages/search/Data.js
@@ -2,12 +2,12 @@ export const Operators = [
   {
     id: '0',
     value: 'And',
-    label: 'Match all criterias',
+    label: 'search:advancedSearch.searchOptions.matchAll',
   },
   {
     id: '1',
     value: 'Or',
-    label: 'Match at least one criteria',
+    label: 'search:advancedSearch.searchOptions.matchAtLeastOne',
   },
 ];
 
diff --git a/src/pages/search/Search.js b/src/pages/search/Search.js
index 256ce8d..32b18a2 100644
--- a/src/pages/search/Search.js
+++ b/src/pages/search/Search.js
@@ -19,30 +19,15 @@ import {
   fetchUserPolicyFields,
   fetchSources,
 } from '../../actions/source';
-import { fetchUserHistory } from '../../actions/user';
 import { useTranslation } from 'react-i18next';
 import AdvancedSearch from './AdvancedSearch/AdvancedSearch';
 import BasicSearch from './BasicSearch/BasicSearch';
 import styles from './styles';
 
-export const fetchHistory = (setUserHistory) => {
-  fetchUserHistory(sessionStorage.getItem('user_id')).then((result) => {
-    if (result[0] && result[0].ui_structure) {
-      result.forEach((item) => {
-        item.ui_structure = JSON.parse(item.ui_structure);
-        item.label = `${item.name} - ${new Date(item.createdat).toLocaleString()}`;
-      });
-    }
-    setUserHistory(result);
-  });
-};
-
 const Search = () => {
   const { t } = useTranslation('search');
   const datePickerStyles = styles();
-  const [isLoading, setIsLoading] = useState(false);
   const [selectedTabNumber, setSelectedTabNumber] = useState(0);
-  const [userHistory, setUserHistory] = useState({});
   const [isAdvancedSearch, setIsAdvancedSearch] = useState(false);
   const [readOnlyQuery, setReadOnlyQuery] = useState(true);
   const [selectedField, setSelectedField] = useState([]);
@@ -67,8 +52,6 @@ const Search = () => {
   const [isReadOnlyModalOpen, setIsReadOnlyModalOpen] = useState(false);
   const [isSaveSearchModalOpen, setIsSaveSearchModalOpen] = useState(false);
   const [selectedSavedSearch, setSelectedSavedSearch] = useState();
-  const [historySelectError, setHistorySelectError] = useState(undefined);
-  const [notificationToasts, setNotificationToasts] = useState([]);
 
   useEffect(() => {
     fetchPublicFields().then((resultStdFields) => {
@@ -104,7 +87,6 @@ const Search = () => {
       setSources(result);
       setAvailableSources(result);
     });
-    fetchHistory(setUserHistory);
   }, []);
 
   const tabsContent = [
@@ -115,8 +97,6 @@ const Search = () => {
         <>
           {isAdvancedSearch ? (
             <AdvancedSearch
-              isLoading={isLoading}
-              setIsLoading={setIsLoading}
               search={search}
               setSearch={setSearch}
               searchResults={searchResults}
@@ -144,15 +124,9 @@ const Search = () => {
               setIsReadOnlyModalOpen={setIsReadOnlyModalOpen}
               isSaveSearchModalOpen={isSaveSearchModalOpen}
               setIsSaveSearchModalOpen={setIsSaveSearchModalOpen}
-              userHistory={userHistory}
-              setUserHistory={setUserHistory}
               selectedSavedSearch={selectedSavedSearch}
               setSelectedSavedSearch={setSelectedSavedSearch}
-              historySelectError={historySelectError}
-              setHistorySelectError={setHistorySelectError}
               selectedOperatorId={selectedOperatorId}
-              notificationToasts={notificationToasts}
-              setNotificationToasts={setNotificationToasts}
               setIsAdvancedSearch={setIsAdvancedSearch}
               isAdvancedSearch={isAdvancedSearch}
               selectedField={selectedField}
@@ -180,8 +154,6 @@ const Search = () => {
               selectedSources={selectedSources}
               basicSearch={basicSearch}
               setBasicSearch={setBasicSearch}
-              isLoading={isLoading}
-              setIsLoading={setIsLoading}
               setSearchResults={setSearchResults}
               setSelectedTabNumber={setSelectedTabNumber}
             />
-- 
GitLab


From 550ee628fa6dd326c30d3d022e5a36a0b86fc2e2 Mon Sep 17 00:00:00 2001
From: rbisson <remi.bisson@inrae.fr>
Date: Wed, 15 May 2024 17:45:42 +0200
Subject: [PATCH 14/17] [AdvancedSearch.js]: corrected a React memory leak
 error caused by isLoading bool

---
 src/pages/search/AdvancedSearch/AdvancedSearch.js | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/pages/search/AdvancedSearch/AdvancedSearch.js b/src/pages/search/AdvancedSearch/AdvancedSearch.js
index cc2030a..cd4b8a0 100644
--- a/src/pages/search/AdvancedSearch/AdvancedSearch.js
+++ b/src/pages/search/AdvancedSearch/AdvancedSearch.js
@@ -291,7 +291,9 @@ const SearchBar = ({
       searchQuery(queriesWithIndices).then((result) => {
         setSearchResults(result);
         setSelectedTabNumber(1);
-        setIsLoading(false);
+        if (isLoading) {
+          setIsLoading(false);
+        }
       });
     }
   };
-- 
GitLab


From 8119aadc1cee82589d18b80517bdfda39547937b Mon Sep 17 00:00:00 2001
From: rbisson <remi.bisson@inrae.fr>
Date: Wed, 15 May 2024 17:50:49 +0200
Subject: [PATCH 15/17] [Results]: corrected #6 bug, causing query text not to
 update when switching to basic search from advancedSearch

---
 src/pages/results/Results.js | 10 +++++-----
 src/pages/search/Search.js   |  3 +--
 2 files changed, 6 insertions(+), 7 deletions(-)

diff --git a/src/pages/results/Results.js b/src/pages/results/Results.js
index e1dd381..1d75887 100644
--- a/src/pages/results/Results.js
+++ b/src/pages/results/Results.js
@@ -38,17 +38,17 @@ const changeFlyoutState = (array, index, value, defaultValue) => {
   return newArray;
 };
 
-const Results = ({ searchResults, search, basicSearch }) => {
+const Results = ({ searchResults, searchQuery }) => {
   const { t } = useTranslation('results');
   const [resultsCol, setResultsCol] = useState([]);
   const [results, setResults] = useState([]);
   const [isFlyoutOpen, setIsFlyoutOpen] = useState([false]);
-  const [searchQuery, setSearchQuery] = useState('');
+  const [searchQueryText, setSearchQueryText] = useState('');
 
   useEffect(() => {
     processData(searchResults);
-    search.length ? setSearchQuery(search) : setSearchQuery(basicSearch);
-  }, [searchResults, search, basicSearch]);
+    setSearchQueryText(searchQuery);
+  }, [searchResults]);
 
   const updateTableCell = (tableContent, value, colIndex, rowIndex) => {
     const updatedRow = updateArrayElement(tableContent[rowIndex], colIndex, value);
@@ -330,7 +330,7 @@ const Results = ({ searchResults, search, basicSearch }) => {
         <EuiSpacer size="s" />
         <EuiFlexItem grow={false}>
           <EuiTitle size="xs">
-            <h2>Your query : {searchQuery}</h2>
+            <h2>Your query : {searchQueryText}</h2>
           </EuiTitle>
         </EuiFlexItem>
         <EuiSpacer size="s" />
diff --git a/src/pages/search/Search.js b/src/pages/search/Search.js
index 32b18a2..61bd93e 100644
--- a/src/pages/search/Search.js
+++ b/src/pages/search/Search.js
@@ -169,8 +169,7 @@ const Search = () => {
           <EuiFlexItem>
             <Results
               searchResults={searchResults}
-              search={search}
-              basicSearch={basicSearch}
+              searchQuery={isAdvancedSearch ? search : basicSearch}
             />
           </EuiFlexItem>
         </EuiFlexGroup>
-- 
GitLab


From 1077ca8592712d78e1806a155f1d33b8f3cb66cc Mon Sep 17 00:00:00 2001
From: rbisson <remi.bisson@inrae.fr>
Date: Thu, 16 May 2024 11:49:09 +0200
Subject: [PATCH 16/17] [Results.js]: added full translations and cleaned
 component; improved results table columns names display

---
 public/locales/en/results.json |   5 ++
 public/locales/fr/results.json |   5 ++
 src/pages/results/Results.js   | 157 ++++-----------------------------
 3 files changed, 25 insertions(+), 142 deletions(-)

diff --git a/public/locales/en/results.json b/public/locales/en/results.json
index 742539e..61cf048 100644
--- a/public/locales/en/results.json
+++ b/public/locales/en/results.json
@@ -1,5 +1,10 @@
 {
+  "yourQuery": "Your query: {{query}}",
+  "clickOnRowTip": "Click on a resource row to display full metadata.",
   "downloadResultsButton": {
     "JSON": "Download as JSON"
+  },
+  "table": {
+    "title": "Search results"
   }
 }
diff --git a/public/locales/fr/results.json b/public/locales/fr/results.json
index 498fc27..e611299 100644
--- a/public/locales/fr/results.json
+++ b/public/locales/fr/results.json
@@ -1,5 +1,10 @@
 {
+  "yourQuery": "Votre requête : {{query}}",
+  "clickOnRowTip": "Clickez sur une ressource (ligne du tableau) pour afficher ses métadonnées.",
   "downloadResultsButton": {
     "JSON": "Télécharger en JSON"
+  },
+  "table": {
+    "title": "Résultats de la recherche"
   }
 }
diff --git a/src/pages/results/Results.js b/src/pages/results/Results.js
index 1d75887..b647f77 100644
--- a/src/pages/results/Results.js
+++ b/src/pages/results/Results.js
@@ -63,13 +63,20 @@ const Results = ({ searchResults, searchQuery }) => {
     return updatedResults;
   };
 
+  const buildColumnName = (name) => {
+    // Replace underscore with spaces
+    name = name.split('_').join(' ');
+    // Uppercase first character
+    name = name.charAt(0).toUpperCase() + name.slice(1);
+    return name;
+  };
+
   const processData = (metadata) => {
     if (metadata) {
       const columns = [];
       const rows = [];
-      // const metadataRecords = metadata.hits.hits
       columns.push({
-        name: 'currently open',
+        name: 'Currently open',
         options: {
           display: true,
           viewColumns: true,
@@ -88,7 +95,7 @@ const Results = ({ searchResults, searchQuery }) => {
           if (typeof displayedFields[fieldName] === 'string') {
             if (recordIndex === 0) {
               const column = {
-                name: fieldName,
+                name: buildColumnName(fieldName),
                 options: {
                   display: true,
                 },
@@ -156,13 +163,11 @@ const Results = ({ searchResults, searchQuery }) => {
     filterType: 'dropdown',
     responsive: 'standard',
     selectableRows: 'none',
-    selectableRowsOnClick: false,
+    selectableRowsOnClick: true,
     onRowSelectionChange: (rowsSelected, allRows) => {},
     onRowClick: (rowData, rowState) => {},
     onCellClick: (val, colMeta) => {
-      // if (searchResults.hits.hits && colMeta.colIndex !== 0) {
       if (searchResults && colMeta.colIndex !== 0) {
-        // const updatedTable = updateTableCell(closeAllFlyouts(results), recordFlyout(searchResults.hits.hits[colMeta.rowIndex]._source, colMeta.rowIndex, !isFlyoutOpen[colMeta.rowIndex]), 0, colMeta.rowIndex)
         const updatedTable = updateTableCell(
           closeAllFlyouts(results),
           recordFlyout(
@@ -185,134 +190,6 @@ const Results = ({ searchResults, searchQuery }) => {
     },
   };
 
-  /*  const displayRecord = (record) => {
-         let recordDisplay = []
-         if (!!record) {
-             const fields = Object.keys(record)
-             fields.forEach(field => {
-                 if (typeof record[field] != 'string') {
-                     // const rndId = Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
-                     if (isNaN(field)) {
-                         // const buttonContent = `"${field}"`
-                         let isStrArray = false
-                         if (Array.isArray(record[field])) {
-                             isStrArray = true
-                             record[field].forEach(item => {
-                                 if (typeof item != 'string')
-                                     isStrArray = false
-                             })
-                         }
-                         if (isStrArray) {
-                             recordDisplay.push(
-                                 <>
-                                     <h3>
-                                         &emsp;{field}
-                                     </h3>
-                                     {displayRecord(record[field])}
-                                 </>
-                             )
-                         } else {
-                             recordDisplay.push(
-                                 <>
-                                     <EuiSpacer size="s" />
-                                     <EuiPanel paddingSize="s">
-                                         <EuiAccordion id={Math.random().toString()} buttonContent={field}>
-                                             <EuiText size="s">
-                                                 {displayRecord(record[field])}
-                                             </EuiText>
-                                         </EuiAccordion>
-                                     </EuiPanel>
-                                 </>
-                             )
-                         }
-                     } else {
-                         recordDisplay.push(
-                             <>
-                                 {displayRecord(record[field])}
-                             </>
-                         )
-                         if (fields[fields.indexOf(field) + 1])
-                             recordDisplay.push(
-                                 <>
-                                     <EuiSpacer size="m" />
-                                     <hr />
-                                 </>
-                             )
-                     }
-                 } else {
-                     if (isNaN(field)) {
-                         recordDisplay.push(
-                             <>
-                                 <h3>
-                                     &emsp;{field}
-                                 </h3>
-                                 <EuiTextColor color="secondary">&emsp;&emsp;{record[field]}</EuiTextColor>
-                             </>
-                         )
-                     } else {
-                         recordDisplay.push(
-                             <>
-                                 <EuiSpacer size="s" />
-                                 <EuiTextColor color="secondary">&emsp;&emsp;{record[field]}</EuiTextColor>
-                             </>
-                         )
-                     }
-                 }
-             })
-             return recordDisplay
-         }
-     }
-
-     const recordFlyout = (record, recordIndex, isFlyoutOpen, setIsFlyoutOpen) => {
-         let flyout
-         if (isFlyoutOpen[recordIndex]) {
-             // const flyOutContent = ReactHtmlParser(displayRecord(record, 1))
-             const flyOutStr = displayRecord(record)
-             // const flyOutContent = parse(flyOutStr, { htmlparser2: { lowerCaseTags: false } })
-             const flyout = (
-                 <>
-                     <EuiFlyout
-                         onClose={() => {
-                             // setIsFlyoutOpen(updateArrayElement(isFlyoutOpen, recordIndex, false))
-                             // updateResultsCell(false, 0, recordIndex)
-                             const updatedArray = changeFlyoutState(isFlyoutOpen, recordIndex, !isFlyoutOpen[recordIndex], false)
-                             setIsFlyoutOpen(updatedArray)
-                         }}
-                         aria-labelledby={recordIndex}>
-                         <EuiFlyoutBody>
-                             <EuiText size="s">
-                                 <Fragment>
-                                     {flyOutStr}
-                                 </Fragment>
-                             </EuiText>
-                         </EuiFlyoutBody>
-                     </EuiFlyout>
-                     <EuiIcon type='eye' color='danger' />
-                 </>
-             );
-             return (flyout)
-         }
-     } */
-
-  /* const viewButton = (record, recordIndex, isFlyoutOpenIndex, isFlyoutOpen, setIsFlyoutOpen) => {
-        return (
-            <>
-                <EuiButtonIcon
-                    size="m"
-                    color="success"
-                    onClick={() => {
-                        const flyOutArray = updateArrayElement(isFlyoutOpen, recordIndex, !isFlyoutOpen[recordIndex])
-                        setIsFlyoutOpen(flyOutArray)
-                        updateResultsCell(!isFlyoutOpen[recordIndex], isFlyoutOpenIndex, recordIndex)
-                    }}
-                    iconType="eye"
-                    title="View record"
-                />
-                {recordFlyout(record, recordIndex, isFlyoutOpen, setIsFlyoutOpen)}
-            </>
-        )
-    } */
-
   const downloadResults = () => {
     if (searchResults) {
       download(
@@ -330,28 +207,24 @@ const Results = ({ searchResults, searchQuery }) => {
         <EuiSpacer size="s" />
         <EuiFlexItem grow={false}>
           <EuiTitle size="xs">
-            <h2>Your query : {searchQueryText}</h2>
+            <h2>{t('results:yourQuery', { query: searchQueryText })}</h2>
           </EuiTitle>
         </EuiFlexItem>
         <EuiSpacer size="s" />
       </EuiFlexGroup>
       <EuiFlexGroup>
         <EuiFlexItem>
-          <EuiCallOut
-            size="s"
-            title="Click on a line of the table to inspect resource metadata (except for the first column)."
-            iconType="search"
-          />
+          <EuiCallOut size="s" title={t('results:clickOnRowTip')} iconType="search" />
         </EuiFlexItem>
         <EuiFlexItem grow={false}>
           <EuiButton fill onClick={() => downloadResults()}>
-            {t('downloadResultsButton.JSON')}
+            {t('results:downloadResultsButton.JSON')}
           </EuiButton>
         </EuiFlexItem>
       </EuiFlexGroup>
       <MuiThemeProvider theme={getMuiTheme()}>
         <MUIDataTable
-          title={'Search results'}
+          title={t('results:table.title')}
           data={results}
           columns={resultsCol}
           options={resultsGridOptions}
-- 
GitLab


From 7d1c0632338a0b537bcec55abcbe1753ac2196aa Mon Sep 17 00:00:00 2001
From: rbisson <remi.bisson@inrae.fr>
Date: Fri, 17 May 2024 03:40:39 +0200
Subject: [PATCH 17/17] [SearchMaps]: added full translations ; added a tool
 column in legend table

---
 public/locales/en/maps.json | 14 +++++-
 public/locales/fr/maps.json | 16 +++++--
 src/pages/maps/SearchMap.js | 85 +++++++++++++++++++------------------
 src/pages/maps/styles.js    |  2 -
 4 files changed, 69 insertions(+), 48 deletions(-)

diff --git a/public/locales/en/maps.json b/public/locales/en/maps.json
index 08ae921..ca5269d 100644
--- a/public/locales/en/maps.json
+++ b/public/locales/en/maps.json
@@ -2,12 +2,22 @@
   "layersChoiceTitle": "Click on layers to toggle display.",
   "layersTableHeaders": {
     "cartography": "Cartography",
-    "data": "Data"
+    "filters": "Filters",
+    "tools": "Tools"
   },
   "layersTable": {
     "openStreetMap": "Open Street Map",
     "bingAerial": "Bing Aerial",
     "IGN": "IGN map",
-    "queryResults": "Query results"
+    "SylvoEcoRegions": "SylvoEcoRegions",
+    "queryResults": "Query results",
+    "regions": "Regions",
+    "departments": "Departments",
+    "selectFilterOption": "Select a single option",
+    "selectedPointsList": "Selected resources list",
+    "pointSelectionMode": {
+      "select": "Select",
+      "unselect": "Unselect"
+    }
   }
 }
diff --git a/public/locales/fr/maps.json b/public/locales/fr/maps.json
index a03e165..c113605 100644
--- a/public/locales/fr/maps.json
+++ b/public/locales/fr/maps.json
@@ -2,12 +2,22 @@
   "layersChoiceTitle": "Cliquez sur les couches pour modifier l'affichage.",
   "layersTableHeaders": {
     "cartography": "Cartographie",
-    "data": "Données"
+    "filters": "Filtres",
+    "tools": "Outils"
   },
   "layersTable": {
     "openStreetMap": "Open Street Map",
-    "bingAerial": "Bing carte",
+    "bingAerial": "Bing vue aérienne",
     "IGN": "Plan IGN",
-    "queryResults": "Résultats de la requête"
+    "SylvoEcoRegions": "SylvoEcoRégions",
+    "queryResults": "Résultats de la requête",
+    "regions": "Régions",
+    "departments": "Départements",
+    "selectFilterOption": "Sélectionnez une option",
+    "selectedPointsList": "Liste des resources sélectionnées",
+    "pointSelectionMode": {
+      "select": "Sélection",
+      "unselect": "Désélection"
+    }
   }
 }
diff --git a/src/pages/maps/SearchMap.js b/src/pages/maps/SearchMap.js
index 378e263..5ba404c 100644
--- a/src/pages/maps/SearchMap.js
+++ b/src/pages/maps/SearchMap.js
@@ -54,19 +54,18 @@ const initMatrixIds = () => {
   return matrixIds;
 };
 
-const layersOptions = [
-  { label: 'Résultats de la requête', value: 'ResRequete' },
-  { label: 'Régions', value: 'regions' },
-  { label: 'Départements', value: 'departements' },
-];
-
 const SearchMap = ({ searchResults }) => {
   const { t } = useTranslation('maps');
   const [center, setCenter] = useState(proj.fromLonLat([2.5, 46.5]));
   const [zoom, setZoom] = useState(6);
   const [isPopoverOpen, setIsPopoverOpen] = useState(false);
   const [deselectChecked, setDeselectChecked] = useState(false);
-  const [selectedOptions, setSelected] = useState([layersOptions[0]]);
+  const filterOptions = [
+    { label: t('maps:layersTable.queryResults'), value: 'ResRequete' },
+    { label: t('maps:layersTable.regions'), value: 'regions' },
+    { label: t('maps:layersTable.departments'), value: 'departements' },
+  ];
+  const [selectedOptions, setSelected] = useState([filterOptions[0]]);
   const resultPointsSource4BG = new VectorSource({});
   const resultPointsSource = new VectorSource({});
   let selectedPointsSource = new VectorSource({});
@@ -87,7 +86,7 @@ const SearchMap = ({ searchResults }) => {
     style.setStroke(stroke);
     return style;
   };
-  const maps4Filter = {
+  const mapFilters = {
     ResRequete: new VectorLayer({
       name: 'ResRequete',
       source: resultPointsSource,
@@ -181,7 +180,7 @@ const SearchMap = ({ searchResults }) => {
       name: 'queryResults',
       source: resultPointsSource4BG,
     }),
-    maps4Filter['selectedPointsLayer'],
+    mapFilters['selectedPointsLayer'],
   ]);
   const [mapLayersVisibility, setMapLayersVisibility] = useState(
     new Array(mapLayers.length).fill(false)
@@ -229,6 +228,7 @@ const SearchMap = ({ searchResults }) => {
       setZoom(map.getView().getZoom());
     });
     map.addInteraction(dragBox);
+    map.addInteraction(select);
     // clear selection when drawing a new box and when clicking on the map
     dragBox.on('boxstart', function () {
       selectedFeatures.clear();
@@ -252,7 +252,7 @@ const SearchMap = ({ searchResults }) => {
         if (selectedOptions[0].value === 'queryResults') {
           polygonSource = pointsSource;
         } else {
-          polygonSource = maps4Filter[selectedOptions[0].value].get('source');
+          polygonSource = mapFilters[selectedOptions[0].value].get('source');
         }
         const boxFeatures = polygonSource
           .getFeaturesInExtent(extent)
@@ -304,7 +304,6 @@ const SearchMap = ({ searchResults }) => {
         selectedFeatures.extend(boxFeatures);
       }
     });
-    map.addInteraction(select);
     map.getView().animate({ zoom: zoom }, { center: center }, { duration: 2000 });
     processData();
   }, [searchResults, map, deselectChecked]);
@@ -321,13 +320,13 @@ const SearchMap = ({ searchResults }) => {
     setIsPopoverOpen(false);
   };
 
-  const onLayersSelectChange = (newSelectedOptions) => {
+  const onFilterSelectChange = (newSelectedOptions) => {
     setSelected(newSelectedOptions);
     const allMyLayers = map.getLayers();
-    // remove previously selected layer
-    for (let i = 0; i < layersOptions.length; i++) {
+    // remove previously selected filter
+    for (let i = 0; i < filterOptions.length; i++) {
       for (let j = 0; j < allMyLayers.getLength(); j++) {
-        if (layersOptions[i].value === map.getLayers().item(j).get('name')) {
+        if (filterOptions[i].value === map.getLayers().item(j).get('name')) {
           map.removeLayer(map.getLayers().item(j));
           break;
         }
@@ -340,7 +339,7 @@ const SearchMap = ({ searchResults }) => {
     if (selectedMap === 'ResRequete') {
       toggleLayer('queryResults');
     } else {
-      map.addLayer(maps4Filter[selectedMap]);
+      map.addLayer(mapFilters[selectedMap]);
     }
   };
 
@@ -444,7 +443,8 @@ const SearchMap = ({ searchResults }) => {
           <thead>
             <tr>
               <th>{t('layersTableHeaders.cartography')}</th>
-              <th>{t('layersTableHeaders.data')}</th>
+              <th>{t('layersTableHeaders.filters')}</th>
+              <th>{t('layersTableHeaders.tools')}</th>
             </tr>
           </thead>
           <tbody>
@@ -485,31 +485,42 @@ const SearchMap = ({ searchResults }) => {
                   </li>
                 </ul>
               </td>
-              <td>
+              <td style={styles.layersTableCells}>
                 <ul>
-                  <input
-                    type="checkbox"
-                    checked={deselectChecked}
-                    onChange={handleDeselectCheckboxChange}
+                  <EuiCheckbox
+                    id={htmlIdGenerator()()}
+                    label={t('layersTable.queryResults')}
+                    checked={mapLayersVisibility[getLayerIndex('queryResults')]}
+                    onChange={() => toggleLayer('queryResults')}
                   />
-                  <i>Désélectionner</i>
+                  <br />
                   <EuiComboBox
-                    aria-label="Accessible screen reader label"
-                    placeholder="Select a single option"
+                    aria-label={t('maps:layersTable.selectFilterOption')}
+                    placeholder={t('maps:layersTable.selectFilterOption')}
                     singleSelection={{ asPlainText: true }}
-                    options={layersOptions}
+                    options={filterOptions}
                     selectedOptions={selectedOptions}
-                    onChange={onLayersSelectChange}
+                    onChange={onFilterSelectChange}
+                  />
+                </ul>
+              </td>
+              <td style={styles.layersTableCells}>
+                <ul>
+                  <input
+                    type="checkbox"
+                    checked={deselectChecked}
+                    onChange={handleDeselectCheckboxChange}
                   />
+                  <i>
+                    {deselectChecked
+                      ? t('maps:layersTable.pointSelectionMode.select')
+                      : t('maps:layersTable.pointSelectionMode.unselect')}
+                  </i>
                   <br />
                   <EuiPopover
                     button={
-                      <EuiButtonEmpty
-                        iconType="documentation"
-                        iconSide="right"
-                        onClick={onButtonClick}
-                      >
-                        Liste des résultats sélectionnés
+                      <EuiButtonEmpty onClick={onButtonClick}>
+                        {t('maps:layersTable.selectedPointsList')}
                       </EuiButtonEmpty>
                     }
                     isOpen={isPopoverOpen}
@@ -529,14 +540,6 @@ const SearchMap = ({ searchResults }) => {
                   </EuiPopover>
                 </ul>
               </td>
-              <td style={styles.layersTableCells}>
-                <EuiCheckbox
-                  id={htmlIdGenerator()()}
-                  label={t('layersTable.queryResults')}
-                  checked={mapLayersVisibility[getLayerIndex('queryResults')]}
-                  onChange={(e) => toggleLayer('queryResults')}
-                />
-              </td>
             </tr>
           </tbody>
         </table>
diff --git a/src/pages/maps/styles.js b/src/pages/maps/styles.js
index ac6cb6b..72257f8 100644
--- a/src/pages/maps/styles.js
+++ b/src/pages/maps/styles.js
@@ -35,11 +35,9 @@ const styles = {
   },
   layersTable: {
     margin: '20px',
-    border: '1px solid black',
   },
   layersTableCells: {
     padding: '10px',
-    verticalAlign: 'top',
   },
 };
 
-- 
GitLab