diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000000000000000000000000000000000000..7dc10ba260fe116876c0ea0da0aaf85ab3b37cfd
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,22 @@
+# Ignore node_modules directory
+node_modules/
+
+# Ignore npm debug log
+npm-debug.log
+
+# Ignore build artifacts
+dist/
+build/
+out/
+
+# Ignore development files
+*.log
+*.pid
+*.lock
+
+# Ignore version control files
+.git/
+.gitignore
+
+# Ignore certificates
+ssl/
\ No newline at end of file
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 0000000000000000000000000000000000000000..f0674db04d9dbb5b846a23974a0e0794f7172534
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,16 @@
+stages:
+  - build
+
+build:
+  stage: build
+  image: docker:latest
+  services:
+    - docker:dind
+  script:
+    - apk add jq
+    - version=$(jq -r .version package.json)
+    - cat $ENV_PRODUCTION > .env.production
+    - docker build -t registry.forgemia.inra.fr/in-sylva-development/in-sylva.search.app:$version .
+    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
+    - docker push registry.forgemia.inra.fr/in-sylva-development/in-sylva.search.app:$version
+  when: manual
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000000000000000000000000000000000000..d2c2bcb6bc408df47f9ec98baed359e1b284ba5d
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,25 @@
+FROM node:18.20.5 as builder
+
+WORKDIR /app/
+COPY package.json .
+RUN yarn install
+COPY . .
+RUN yarn build
+
+FROM nginx:1.24-bullseye
+COPY --from=builder /app/build /usr/share/nginx/html
+
+RUN rm /etc/nginx/conf.d/default.conf
+COPY nginx/nginx.conf /etc/nginx/conf.d/default.conf
+COPY nginx/gzip.conf /etc/nginx/conf.d/gzip.conf
+
+WORKDIR /usr/share/nginx/html
+RUN chown -R :www-data /usr/share/nginx/html
+
+COPY ./env.sh .
+RUN chmod +x env.sh
+
+COPY .env.production .env
+RUN ./env.sh -e .env -o ./
+
+CMD ["nginx", "-g", "daemon off;"]
diff --git a/env.sh b/env.sh
new file mode 100755
index 0000000000000000000000000000000000000000..5c94083cf6ba0fb86ef903310d3b9034474cdb83
--- /dev/null
+++ b/env.sh
@@ -0,0 +1,121 @@
+#!/bin/bash
+
+usage() {
+    BASENAME=$(basename "$0")
+    echo "Usage: $BASENAME -e <env-file> -o <output-file>"
+    echo "  -e, --env-file     Path to .env file"
+    echo "  -o, --output-file  Path to output '.env-config.js' file"
+    exit 1
+}
+
+while getopts "e:o:" opt; do
+    case $opt in
+    e)
+        ENV_FILE=$OPTARG
+        ;;
+    o)
+        OUT_DIRECTORY=$OPTARG
+        ;;
+    \?)
+        # Invalid option
+        echo "Invalid option: -$OPTARG"
+        usage
+        exit 1
+        ;;
+    :)
+        echo "Option -$OPTARG requires an argument."
+        usage
+        exit 1
+        ;;
+    esac
+done
+
+# Check if required options are provided
+if [ -z "$OUT_DIRECTORY" ]; then
+    echo "Error: -o (output directory) is required."
+    echo ""
+    usage
+    exit 1
+fi
+
+if [ -z "$ENV_FILE" ]; then
+    echo "Error: -e (environment file) is required."
+    echo ""
+    usage
+    exit 1
+fi
+
+ENV_FILE_PATH=$(realpath $ENV_FILE)
+
+# Check if the environment file exists
+if [[ ! -f $ENV_FILE_PATH ]]; then
+    echo "Environment file does not exist"
+    echo ""
+    usage
+    exit 1
+fi
+
+# Check if the environment file is readable
+if [[ ! -r $ENV_FILE_PATH ]]; then
+    echo "Environment file is not readable"
+    echo ""
+    usage
+    exit 1
+fi
+
+# Check if the environment file is empty
+if [[ ! -s $ENV_FILE_PATH ]]; then
+    echo "Environment file is empty"
+    echo ""
+    usage
+    exit 1
+fi
+
+# Check if the environment file has the correct format
+BAD_LINES=$(grep -v -P '^[^=\s]+=[^=\s]+$' "$ENV_FILE_PATH")
+if [ -n "$BAD_LINES" ]; then
+    echo "Environment file has incorrect format or contains empty values:"
+    echo "$BAD_LINES"
+    echo ""
+    usage
+    exit 1
+fi
+
+FULL_PATH_OUTPUT_DIRECTORY=$(realpath $OUT_DIRECTORY)
+
+# Check if the output directory is a directory, exists, and can be written
+if [[ ! -d $FULL_PATH_OUTPUT_DIRECTORY ]]; then
+    echo "Output directory does not exist or is not a directory"
+    echo ""
+    usage
+    exit 1
+fi
+
+if [[ ! -w $FULL_PATH_OUTPUT_DIRECTORY ]]; then
+    echo "Output directory is not writable"
+    echo ""
+    usage
+    exit 1
+fi
+
+OUTPUT_FILE="env-config.js"
+FULL_OUTPUT_PATH="$FULL_PATH_OUTPUT_DIRECTORY/$OUTPUT_FILE"
+
+# Remove the output file if it exists
+if [[ -f $FULL_OUTPUT_PATH ]]; then
+    rm $FULL_OUTPUT_PATH
+fi
+
+# Add assignment
+echo "window._env_ = {" >>$FULL_OUTPUT_PATH
+
+while read -r line || [[ -n "$line" ]]; do
+    # Split env variables by '='
+    varname=$(echo $line | cut -d'=' -f1)
+    value=$(echo $line | cut -d'=' -f2)
+
+    # Append configuration property to JS file
+    echo "  $varname: \"$value\"," >>$FULL_OUTPUT_PATH
+done <$ENV_FILE_PATH
+
+echo "}" >>$FULL_OUTPUT_PATH
diff --git a/nginx/gzip.conf b/nginx/gzip.conf
new file mode 100644
index 0000000000000000000000000000000000000000..2f54ae1e8e846ed57825b4597e24077efa3a6277
--- /dev/null
+++ b/nginx/gzip.conf
@@ -0,0 +1,44 @@
+# Enable Gzip compressed.
+# gzip on;
+
+  # Enable compression both for HTTP/1.0 and HTTP/1.1 (required for CloudFront).
+  gzip_http_version  1.0;
+
+  # Compression level (1-9).
+  # 5 is a perfect compromise between size and cpu usage, offering about
+  # 75% reduction for most ascii files (almost identical to level 9).
+  gzip_comp_level    5;
+
+  # Don't compress anything that's already small and unlikely to shrink much
+  # if at all (the default is 20 bytes, which is bad as that usually leads to
+  # larger files after gzipping).
+  gzip_min_length    256;
+
+  # Compress data even for clients that are connecting to us via proxies,
+  # identified by the "Via" header (required for CloudFront).
+  gzip_proxied       any;
+
+  # Tell proxies to cache both the gzipped and regular version of a resource
+  # whenever the client's Accept-Encoding capabilities header varies;
+  # Avoids the issue where a non-gzip capable client (which is extremely rare
+  # today) would display gibberish if their proxy gave them the gzipped version.
+  gzip_vary          on;
+
+  # Compress all output labeled with one of the following MIME-types.
+  gzip_types
+    application/atom+xml
+    application/javascript
+    application/json
+    application/rss+xml
+    application/vnd.ms-fontobject
+    application/x-font-ttf
+    application/x-web-app-manifest+json
+    application/xhtml+xml
+    application/xml
+    font/opentype
+    image/svg+xml
+    image/x-icon
+    text/css
+    text/plain
+    text/x-component;
+  # text/html is always compressed by HttpGzipModule
\ No newline at end of file
diff --git a/nginx/nginx.conf b/nginx/nginx.conf
new file mode 100644
index 0000000000000000000000000000000000000000..3ed620d20b90e99365f4078061fcf0619613e11d
--- /dev/null
+++ b/nginx/nginx.conf
@@ -0,0 +1,57 @@
+include /etc/nginx/mime.types;
+
+server {
+
+    listen 80;
+    server_name -;
+
+    proxy_set_header Host $host;
+    proxy_set_header X-Real-IP $remote_addr;
+    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+    proxy_set_header X-Forwarded-Host $host;
+    proxy_set_header X-Forwarded-Server $host;
+    proxy_set_header X-Forwarded-Port $server_port;
+    proxy_set_header X-Forwarded-Proto $scheme;
+
+    access_log /var/log/nginx/host.access.log;
+    error_log /var/log/nginx/host.error.log;
+
+    root /usr/share/nginx/html;
+    index index.html index.htm;
+
+    location /static/media/ {
+        try_files $uri /usr/share/nginx/html/static/media;
+    }
+
+    location / {
+
+        root /usr/share/nginx/html;
+        index index.html;
+        autoindex on;
+        set $fallback_file /index.html;
+        if ($http_accept !~ text/html) {
+            set $fallback_file /null;
+        }
+        if ($uri ~ /$) {
+            set $fallback_file /null;
+        }
+        try_files $uri $fallback_file;
+
+        proxy_http_version 1.1;
+        proxy_set_header Upgrade $http_upgrade;
+        proxy_set_header Connection 'upgrade';
+        proxy_set_header Host $host;
+        proxy_cache_bypass $http_upgrade;
+
+        add_header 'Access-Control-Allow-Origin' "$http_origin" always;
+        add_header 'Access-Control-Allow-Credentials' 'true' always;
+        add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, PATCH, DELETE, OPTIONS' always;
+        add_header 'Access-Control-Allow-Headers' 'Accept,Authorization,Cache-Control,Content-Type,DNT,If-Modified-Since,Keep-Alive,Origin,User-Agent,X-Requested-With' always;
+    }
+
+    error_page 500 502 503 504 /50x.html;
+
+    location = /50x.html {
+        root /usr/share/nginx/html;
+    }
+}
diff --git a/package.json b/package.json
index 893d7db724b5d118f6840ce440f7aaec4b1a9dec..9d5f3531bc2ee72ad1a787e181faca1da02b68c8 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "in-sylva.search",
-  "version": "1.0.0",
+  "version": "1.1.4",
   "private": true,
   "homepage": ".",
   "dependencies": {
@@ -17,6 +17,7 @@
     "i18next-http-backend": "^2.5.2",
     "moment": "^2.27.0",
     "mui-datatables": "^4.3.0",
+    "oidc-react": "^3.4.1",
     "ol": "^9.2.4",
     "proj4": "^2.11.0",
     "react": "^18.3.1",
@@ -29,8 +30,8 @@
     "react-use-storage": "^0.5.1"
   },
   "scripts": {
-    "start": "react-scripts start",
-    "build": "react-scripts build",
+    "start": "./env.sh -e .env.development -o ./public && BROWSER=none NODE_OPTIONS=--openssl-legacy-provider react-scripts start",
+    "build": "NODE_OPTIONS=--openssl-legacy-provider react-scripts build",
     "test": "react-scripts test",
     "eject": "react-scripts eject",
     "lint": "eslint ./src",
@@ -44,8 +45,6 @@
   ],
   "devDependencies": {
     "@babel/plugin-proposal-private-property-in-object": "^7.21.11",
-    "eslint-config-prettier": "^9.1.0",
-    "eslint-plugin-prettier": "^5.1.3",
     "husky": "8.0.3",
     "lint-staged": "14.0.1",
     "prettier": "^3.2.5"
@@ -65,26 +64,25 @@
       "es6": true
     },
     "extends": [
-      "plugin:react/recommended",
-      "plugin:prettier/recommended"
+      "eslint:recommended",
+      "plugin:react/recommended"
+    ],
+    "plugins": [
+      "react",
+      "react-hooks"
     ],
     "parserOptions": {
       "ecmaFeatures": {
         "jsx": true
       }
-    },
-    "plugins": [
-      "react"
-    ]
+    }
   },
   "lint-staged": {
     "*.+(js|jsx)": [
-      "eslint --fix",
-      "git add"
+      "eslint --fix"
     ],
     "*.+(json|css|md)": [
-      "prettier --write",
-      "git add"
+      "prettier --write"
     ]
   },
   "prettier": {
diff --git a/public/env-config.js b/public/env-config.js
new file mode 100644
index 0000000000000000000000000000000000000000..c0447a73cd3f794b0ab75405d4946f3ae771648e
--- /dev/null
+++ b/public/env-config.js
@@ -0,0 +1,9 @@
+window._env_ = {
+  REACT_APP_BASE_URL: "http://localhost:3000",
+  REACT_APP_KEYCLOAK_BASE_URL: "https://in-sylva.inrae.fr/keycloak/realms/in-sylva",
+  REACT_APP_KEYCLOAK_CLIENT_ID: "in-sylva.user.app",
+  REACT_APP_KEYCLOAK_CLIENT_SECRET: "O5igbRB7R2JLMKLv2meXDwBJ802Hd1MZ",
+  REACT_APP_IN_SYLVA_GATEKEEPER_BASE_URL: "http://localhost:3001",
+  REACT_APP_IN_SYLVA_PORTAL_BASE_URL: "http://localhost:3002",
+  NODE_TLS_REJECT_UNAUTHORIZED: "0",
+}
diff --git a/public/index.html b/public/index.html
index 5e6c56481c2b2e5db90e8168d6f8261aab9e9f72..d605a927d3bf768e6de1dec9b7cd7182d6851041 100644
--- a/public/index.html
+++ b/public/index.html
@@ -1,12 +1,13 @@
 <!DOCTYPE html>
 <html lang="en">
-<head>
-  <meta charset="utf-8" />
-  <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" />
-  <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
-  <!--
+  <head>
+    <meta charset="utf-8" />
+    <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.png" />
+    <meta name="viewport"
+      content="width=device-width, initial-scale=1, shrink-to-fit=no" />
+    <meta name="theme-color" content="#000000" />
+    <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.
       Only files inside the `public` folder can be referenced from the HTML.
@@ -15,11 +16,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>
-  <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>
-</body>
+    <title>IN-SYLVA Search</title>
+    <script src="%PUBLIC_URL%/env-config.js"></script>
+  </head>
+  <body style="font-family: 'Roboto', sans-serif;">
+    <noscript>You need to enable JavaScript to run this app.</noscript>
+    <div id="root"></div>
+  </body>
 </html>
diff --git a/public/locales/en/about.json b/public/locales/en/about.json
new file mode 100644
index 0000000000000000000000000000000000000000..7bcb0eb262a9c510e1f113b1cea7460e7940b0f4
--- /dev/null
+++ b/public/locales/en/about.json
@@ -0,0 +1,12 @@
+{
+  "pageTitle": "Welcome on In-Sylva search module.",
+  "abouts": [
+    "Metadata handled in the In-Sylva Information System describe the infrastructure's resources.",
+    "The resources are, to begin with, experimental systems linked to sylviculture, genetics or biogeochemical cycles.",
+    "Metadata are structured around a standard established by the In-Sylva community (<0>https://entrepot.recherche.data.gouv.fr/file.xhtml?persistentId=doi:10.15454/ELXRGY/NCVTVR&version=5.1</0>)",
+    "The query portal allows to search metadata sheets of interests according to different criteria and subject to access rights.",
+    "Your rights are those of the group(s) to which you belong. To find out more, check your profile <1>here</1> or on the right of the menu banner.",
+    "The ‘Search’ interface opens a ‘Basic search’ text box. Selecting ‘Advanced search’ mode allows you to build a more elaborate search.",
+    "Enjoy your research!"
+  ]
+}
diff --git a/public/locales/en/header.json b/public/locales/en/header.json
index 40943129698cb4bede32b102dc83bc507b6fa488..a9ee23dafc237e17f56e1435fb2c44799ecfe400 100644
--- a/public/locales/en/header.json
+++ b/public/locales/en/header.json
@@ -1,6 +1,11 @@
 {
+  "homepageRedirect": "Go to homepage",
+  "portalLink": {
+    "title":  "Go to Portal",
+    "tooltip": "Manage your sources with Portal"
+  },
   "tabs": {
-    "home": "Home",
+    "about": "About",
     "search": "Search"
   },
   "userMenu": {
diff --git a/public/locales/en/home.json b/public/locales/en/home.json
deleted file mode 100644
index 7442e938b671be31ae96ea4c7388eb58eaf4b3b7..0000000000000000000000000000000000000000
--- a/public/locales/en/home.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
-  "pageTitle": "Welcome on In-Sylva search module's homepage.",
-  "searchToolDescription": {
-    "standardLink": "Follow this link to find a complete documentation of the current INSYLVA standard: ",
-    "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": "This tool will help you 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/en/maps.json b/public/locales/en/maps.json
index c71540d2927d968996dd968daa8b1dd9482bc1f9..fb3a125d51428f5139acbd6b1c4689a760f40984 100644
--- a/public/locales/en/maps.json
+++ b/public/locales/en/maps.json
@@ -1,29 +1,37 @@
 {
-  "layersTableHeaders": {
-    "cartography": "Cartography",
-    "filters": "Filters",
-    "tools": "Tools"
+  "noResults": "You don't have any results. Search to display points on the map.",
+  "howTo": {
+    "zoomHelperText": "Use ctrl + scroll to zoom the map"
   },
-  "layersTable": {
-    "openStreetMap": "Open Street Map",
-    "bingAerial": "Bing Aerial",
-    "IGN": "IGN map",
-    "queryResults": "Query results",
-    "regions": "Regions",
-    "departments": "Departments",
-    "sylvoEcoRegions": "SylvoEcoRegions",
-    "selectFilterOption": "Select a single option",
-    "zoomHelperText": "Use ctrl + scroll to zoom the map",
-    "selectionTool": {
-      "title": "Point selection mode",
-      "select": "Add",
-      "unselect": "Remove",
-      "unselectAll": "Unselect all points"
+  "mapTools": {
+    "layers": {
+      "title": "Cartography",
+      "openStreetMap": "Open Street Map",
+      "bingAerial": "Bing Aerial",
+      "IGN": "IGN map"
+    },
+    "filters": {
+      "title": "Filters",
+      "queryResults": "Query results",
+      "regions": "Regions",
+      "departments": "Departments",
+      "sylvoEcoRegions": "SylvoEcoRegions",
+      "selectFilterOption": "Select a single option"
+    },
+    "tools": {
+      "title": "Tools",
+      "selectionTool": {
+        "title": "Point selection mode",
+        "select": "Add",
+        "unselect": "Remove",
+        "unselectAll": "Unselect all points"
+      }
     }
   },
   "selectedPointsList": {
     "title": "Selected resources",
-    "empty": "Select resources to display them here.",
+    "emptyTitle": "Select resources",
+    "empty": "Use CTRL + CLICK to select resources and display them here.",
     "actions": {
       "openResourceFlyout": "Open resource flyout",
       "unselectResource": "Unselect resource"
diff --git a/public/locales/en/profile.json b/public/locales/en/profile.json
index 4daa91ec77982e69c7177aa1bdfb4b126b54af89..6a1b87d39868ca384bc6e0dfa6bc40e5fc3e69ab 100644
--- a/public/locales/en/profile.json
+++ b/public/locales/en/profile.json
@@ -33,7 +33,7 @@
   },
   "fieldsDisplaySettings": {
     "selectedOptionsLabel": "User display options for fields in results page",
-    "selectedOptionsNumber": "<strong>{{count}}</strong> fields selected:",
+    "selectedOptionsNumber": "<strong>{{count}}</strong> fields selected",
     "deleteSettings": "Delete settings",
     "noSettings": "You don't have any settings.",
     "resetSelection": "Reset selection",
@@ -46,7 +46,8 @@
   "groups": {
     "groupsList": "Existing groups",
     "groupName": "Name",
-    "groupDescription": "Description"
+    "groupDescription": "Description",
+    "groupMember": "Member ?"
   },
   "requestsList": {
     "requestsList": "Requests list",
diff --git a/public/locales/en/results.json b/public/locales/en/results.json
index 66d1b63e5be83efd6ac21c1281a9684c998cd96b..7009a1a5ea62895f39c13f3a1119cf27cac1d0a8 100644
--- a/public/locales/en/results.json
+++ b/public/locales/en/results.json
@@ -1,4 +1,12 @@
 {
+  "noResult": {
+    "title": "No results match your search criteria",
+    "body": {
+      "title": "Adjust your query",
+      "description": "Try searching for a different combination of terms."
+    },
+    "action": "Back to search"
+  },
   "clickOnRowTip": "Click on a row to display metadata.",
   "downloadResultsButtons": {
     "download": "Download",
@@ -14,7 +22,7 @@
     }
   },
   "table": {
-    "title": "Search results from query: <strong>{{searchQuery}}</strong>",
+    "title": "<strong>{{count}}</strong> results for query: <strong>{{searchQuery}}</strong>",
     "search": "Search among results",
     "displaySelectedRowsButton": "Sort selection",
     "textLabels": {
@@ -50,6 +58,7 @@
   },
   "flyout": {
     "label": "Resource data sheet",
+    "loading": "Loading data sheet.",
     "JSON": {
       "title": "Resource JSON data"
     }
diff --git a/public/locales/en/search.json b/public/locales/en/search.json
index 67d35bcd73a63090940f4fb383b52da082dd9fc8..fa770790948301588f2b7016cc85406b78534444 100644
--- a/public/locales/en/search.json
+++ b/public/locales/en/search.json
@@ -5,10 +5,75 @@
     "map": "Map"
   },
   "sendSearchButton": "Search",
+  "queryError": "Your query is not formed correctly.",
   "basicSearch": {
     "switchSearchMode": "Switch to advanced search",
-    "searchInputPlaceholder": "Search..."
-  },
+    "searchInputPlaceholder": "Search...",
+    "howTo": {
+      "toggleAction": "How does basic search works ?",
+      "examplesTitle": "A few examples",
+      "examples": [
+        {
+          "query": "Cedrus",
+          "desc": "searches for Cedrus in any field of the standard."
+        },
+        {
+          "query": "biological_material.species:Cedrus",
+          "desc": "searches for Cedrus <strong>only</strong> in the species field of the standard."
+        },
+        {
+          "query": "Cedr?s atlanti*",
+          "desc": "searches for Cedrus atlantica, Cedris atlanticus, and all other possibilities."
+        },
+        {
+          "query": "\"Cedrus atlantica\" +(FCBA || INRAE)",
+          "desc": "searches \"Cedrus atlantica\" and <strong>necessarily</strong> FCBA or INRAE."
+        },
+        {
+          "query": "\"Pinus nigra\" +experimental_site.geo_point.altitude:>1200 +experimental_site.start_date:[1970-01-01 TO 1999-01-01]",
+          "desc": "searches for black pine on an experimental site above 1200m high that started between 1970 and 1999."
+        },
+        {
+          "query": "\"Abies alba\" -biological_material.genetic_level:clone",
+          "desc": "searches for Abies alba while excluding all clones."
+        }
+      ],
+      "sections": [
+        {
+          "title": "Term based functionnality",
+          "content": [
+            "Each term searches for an <strong>exact</strong> match among all standard fields.",
+            "Colon <strong>: </strong> allows to search in a <strong>specific standard field</strong>.",
+            "Quotes <strong>\" \"</strong> link terms <strong>in one and only</strong> character string."
+          ]
+        }, {
+          "title": "Wildcards",
+          "content": [
+            "<strong>?</strong> matches any unique character.",
+            "<strong>*</strong> matches zero or any multiple character including an empty one."
+          ]
+        }, {
+          "title": "Priorities operators",
+          "content": [
+            "Ampersand <strong>&&</strong> allows a <strong>logical and</strong> between two terms.",
+            "Double pipe <strong>||</strong> allows a <strong>logical or</strong> between two terms.",
+            "Parenthesis <strong>( )</strong> specify priority while using multiple operators.",
+            "<strong>+</strong> forces <strong>presence</strong> of the following terms.",
+            "<strong>-</strong> or <strong>!</strong> forces <strong>absence</strong> of the following terms."
+          ]
+        }, {
+          "title": "Comparison operators",
+          "content": [
+            "<, >, <=, => allows numeric and date comparison. Dates in YYYY-MM-DD format.",
+            "Brackets <strong>[ ]</strong> to search in a inclusive interval.",
+            "Curly brackets <strong>{ }</strong> to search in a exclusive interval.",
+            "Intervals two <strong>endpoints</strong> must be linked with <strong>TO</strong>.",
+            "You can combine two types of brackets in a same interval:  <strong>[ } or { ]</strong>."
+          ]
+        }
+      ]
+    }
+},
   "advancedSearch": {
     "switchSearchMode": "Switch to basic search",
     "textQueryPlaceholder": "Add fields...",
diff --git a/public/locales/fr/about.json b/public/locales/fr/about.json
new file mode 100644
index 0000000000000000000000000000000000000000..5634ad1cbbb841fb13c298ff1d4f60aabb1579ec
--- /dev/null
+++ b/public/locales/fr/about.json
@@ -0,0 +1,12 @@
+{
+  "pageTitle": "Bienvenue sur le module de recherche du Système d'Information In-Sylva",
+  "abouts": [
+    "Les métadonnées gérées dans le Système d'Information (SI) In-Sylva décrivent les ressources de l'Infrastructure.",
+    "Les ressources sont dans un premier temps des dispositifs expérimentaux relatifs à la sylviculture, la génétique ou les cycles biogéochimique.",
+    "Les métadonnées sont structurées selon un standard établi par la communauté In-Sylva (<0>https://entrepot.recherche.data.gouv.fr/file.xhtml?persistentId=doi:10.15454/ELXRGY/NCVTVR&version=5.1</0>)",
+    "Le portail d'interrogation permet la recherche des fiches de métadonnées d'intérêt, selon différents critères et conditionnellement aux droits d'accès.",
+    "Vos droits sont ceux du (des) groupes auxquels vous appartenez. Pour en savoir plus, consultez votre profil <1>ici</1> ou à droite du bandeau de menu.",
+    "L'interface \"Recherche\" ouvre une zone de texte de \"Recherche basique\". La sélection en mode \"Recherche avancée\" permet de construire une requete plus élaborée.",
+    "Bonne recherche !"
+  ]
+}
diff --git a/public/locales/fr/header.json b/public/locales/fr/header.json
index 23c3d26a36e7505c93101151aec1b4d0b34c1eeb..a1ab91c56b595787cc1072e12f256c01a7015fbe 100644
--- a/public/locales/fr/header.json
+++ b/public/locales/fr/header.json
@@ -1,6 +1,11 @@
 {
+  "portalLink": {
+    "homepageRedirect": "Accéder à la page d'acceuil",
+    "title":  "Accéder au Portal",
+    "tooltip": "Gérez vos sources avec le Portail"
+  },
   "tabs": {
-    "home": "Page d'accueil",
+    "about": "A propos",
     "search": "Recherche"
   },
   "userMenu": {
diff --git a/public/locales/fr/home.json b/public/locales/fr/home.json
deleted file mode 100644
index 61368490122745e23c183eaa0968da807178222f..0000000000000000000000000000000000000000
--- a/public/locales/fr/home.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
-  "pageTitle": "Bienvenue sur la page d'accueil du module de recherche du Système d'Information In-Sylva",
-  "searchToolDescription": {
-    "standardLink": "Une documentation complète de ce standard est disponible à cette adresse : ",
-    "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/public/locales/fr/maps.json b/public/locales/fr/maps.json
index 51f7f8d6099a93f9c3cc4733b165528a0031f195..765566210610a3055727f2161414f872ffc4085c 100644
--- a/public/locales/fr/maps.json
+++ b/public/locales/fr/maps.json
@@ -1,32 +1,40 @@
 {
-  "layersTableHeaders": {
-    "cartography": "Cartographie",
-    "filters": "Filtres",
-    "tools": "Outils"
+  "noResults": "Aucun résultat. Effectuez une nouvelle recherche pour apercevoir des points sur la carte.",
+  "howTo": {
+    "zoomHelperText": "Utilisez ctrl + scroll pour zoomer"
   },
-  "layersTable": {
-    "openStreetMap": "Open Street Map",
-    "bingAerial": "Bing vue aérienne",
-    "IGN": "Plan IGN",
-    "queryResults": "Résultats de la requête",
-    "regions": "Régions",
-    "departments": "Départements",
-    "sylvoEcoRegions": "SylvoEcoRégions",
-    "selectFilterOption": "Sélectionnez une option",
-    "zoomHelperText": "Utilisez ctrl + scroll pour zoomer",
-    "selectionTool": {
-      "title": "Mode de sélection de points",
-      "select": "Ajout",
-      "unselect": "Suppression",
-      "unselectAll": "Vider la sélection"
+  "mapTools": {
+    "layers": {
+      "title": "Cartographie",
+      "openStreetMap": "Open Street Map",
+      "bingAerial": "Bing vue aérienne",
+      "IGN": "Plan IGN"
+    },
+    "filters": {
+      "title": "Filtres",
+      "queryResults": "Résultats de la requête",
+      "regions": "Régions",
+      "departments": "Départements",
+      "sylvoEcoRegions": "SylvoEcoRégions",
+      "selectFilterOption": "Sélectionnez une option"
+    },
+    "tools": {
+      "title": "Outils",
+      "selectionTool": {
+        "title": "Mode de sélection de points",
+        "select": "Ajout",
+        "unselect": "Suppression",
+        "unselectAll": "Vider la sélection"
+      }
     }
   },
   "selectedPointsList": {
     "title": "Ressources sélectionnées",
-    "empty": "Sélectionnez des ressources pour les afficher ici",
+    "emptyTitle": "Aucune ressource sélectionnée",
+    "empty": "Utilisez CTRL + CLICK pour sélectionner des points de la carte.",
     "actions": {
-      "openResourceFlyout": "Ouvrir la fiche de la resource",
-      "unselectResource": "Désélectionner la resource"
+      "openResourceFlyout": "Ouvrir la fiche de la ressource",
+      "unselectResource": "Déselectionner la resource"
     }
   }
 }
diff --git a/public/locales/fr/profile.json b/public/locales/fr/profile.json
index 22d784c4eb8194135ab2eda90e6176b5f1e4cb0c..60412a9d24d08e49b7ed300e95e83445201118db 100644
--- a/public/locales/fr/profile.json
+++ b/public/locales/fr/profile.json
@@ -46,7 +46,8 @@
   "groups": {
     "groupsList": "Groupes existants",
     "groupName": "Nom",
-    "groupDescription": "Description"
+    "groupDescription": "Description",
+    "groupMember": "Member ?"
   },
   "requestsList": {
     "requestsList": "Liste des requêtes",
diff --git a/public/locales/fr/results.json b/public/locales/fr/results.json
index c8e2dd90522036090eab01276647ab2543bd3c7f..7bbe276b02fe9556f4ad355095ad2964d7eded06 100644
--- a/public/locales/fr/results.json
+++ b/public/locales/fr/results.json
@@ -1,4 +1,12 @@
 {
+  "noResult": {
+    "title": "Aucun résultat",
+    "body": {
+      "title": "Ajustez votre recherche",
+      "description": "Essayez d'utiliser une combinaison de termes différents."
+    },
+    "action": "Modifier ma requête"
+  },
   "clickOnRowTip": "Clickez sur une ligne du tableau pour afficher ses métadonnées.",
   "downloadResultsButtons": {
     "download": "Télécharger",
@@ -14,7 +22,7 @@
     }
   },
   "table": {
-    "title": "Résultats de la requête : <strong>{{searchQuery}}</strong>",
+    "title": "<strong>{{count}}</strong> résultats pour la requête : <strong>{{searchQuery}}</strong>",
     "search": "Chercher parmi les résultats",
     "displaySelectedRowsButton": "Trier la sélection",
     "textLabels": {
@@ -50,6 +58,7 @@
   },
   "flyout": {
     "label": "Fiche de données de la ressource",
+    "loading": "Chargement des données",
     "JSON": {
       "title": "Données JSON de la ressource"
     }
diff --git a/public/locales/fr/search.json b/public/locales/fr/search.json
index 101410a07c2b32d4c86c6f9c2a6ae67164d311f8..9c9f41b09d0f5894ffac229e6c1fc1821cc6e580 100644
--- a/public/locales/fr/search.json
+++ b/public/locales/fr/search.json
@@ -5,9 +5,74 @@
     "map": "Carte"
   },
   "sendSearchButton": "Lancer la recherche",
+  "queryError": "Votre requête n'est pas formée correctement.",
   "basicSearch": {
     "switchSearchMode": "Passer en recherche avancée",
-    "searchInputPlaceholder": "Chercher..."
+    "searchInputPlaceholder": "Chercher...",
+    "howTo": {
+      "toggleAction": "Comment fonctionne la recherche basique ?",
+      "examplesTitle": "Quelques exemples",
+      "examples": [
+        {
+          "query": "Cedrus",
+          "desc": "cherche Cedrus parmi l'ensemble des champs du standard."
+        },
+        {
+          "query": "biological_material.species:Cedrus",
+          "desc": "cherche Cedrus <strong>uniquement</strong> dans le champ espèce du standard."
+        },
+        {
+          "query": "Cedr?s atlanti*",
+          "desc": "cherche Cedrus atlantica, Cedris atlanticus, ainsi que toutes les autres possibilités."
+        },
+        {
+          "query": "\"Cedrus atlantica\" +(FCBA || INRAE)",
+          "desc": "cherche \"Cedrus atlantica\" et <strong>obligatoirement</strong> un de FCBA ou INRAE."
+        },
+        {
+          "query": "\"Pinus nigra\" +experimental_site.geo_point.altitude:>1200 +experimental_site.start_date:[1970-01-01 TO 1999-01-01]",
+          "desc": "cherche du pin noir sur un site exp. situé à plus de 1200m d'altitude, ayant débuté entre 1970 et 1999."
+        },
+        {
+          "query": "\"Abies alba\" -biological_material.genetic_level:clone",
+          "desc": "cherche du Abies alba en excluant tous les clônes."
+        }
+      ],
+      "sections": [
+        {
+          "title": "Fonctionnement par mots clés",
+          "content": [
+            "Chaque terme cherche une correspondance <strong>exacte</strong> parmi tous les champs du standard.",
+            "Le <strong>deux-points : </strong>permet de chercher dans un <strong>champ spécifique du standard</strong>.",
+            "Les guillemets <strong>\" \"</strong> lient des termes en <strong>une seule et même</strong> chaîne de caractère."
+          ]
+        }, {
+          "title": "Caractères de remplacement",
+          "content": [
+            "<strong>?</strong> qui correspond à n'importe quel caractère unique.",
+            "<strong>*</strong> qui peut correspondre à zéro ou plusieurs caractères, y compris un caractère vide."
+          ]
+        }, {
+          "title": "Opérateurs de priorités",
+          "content": [
+            "Le et commercial <strong>&&</strong> permet un <strong>et logique</strong> entre deux termes.",
+            "La barre verticale <strong>||</strong> permet un <strong>ou logique</strong> entre deux termes.",
+            "Les parenthèses <strong>( )</strong> précisent la priorité dès que vous utilisez plusieurs opérateurs.",
+            "<strong>+</strong> oblige <strong>la présence</strong> des termes suivants.",
+            "<strong>-</strong> ou <strong>!</strong> obligent <strong>l'absence</strong> des termes suivants."
+          ]
+        }, {
+          "title": "Opérateurs de comparaison",
+          "content": [
+            "<, >, <=, => permettent des comparaisons avec des valeurs numériques ou des dates (au format AAAA-MM-JJ).",
+            "Les crochets <strong>[ ]</strong> pour chercher dans un intervalle inclusif.",
+            "Les brackets <strong>{ }</strong> pour chercher dans un intervalle exclusif.",
+            "Les deux <strong>bornes</strong> d'un intervalle doivent être liés par le terme <strong>TO</strong>.",
+            "Vous pouvez combiner un crochet et un bracket dans le même intervalle: <strong>[ } ou { ]</strong>."
+          ]
+        }
+      ]
+    }
   },
   "advancedSearch": {
     "switchSearchMode": "Passer en recherche basique",
@@ -20,7 +85,7 @@
     "fields": {
       "title": "Recherche de champ",
       "loadingFields": "Chargement des champs...",
-      "removeFieldButton": "Supprimer le champ",
+      "removeFieldButton": "Retirer le champ",
       "clearValues": "Vider les valeurs",
       "addFieldPopover": {
         "title": "Ajouter un champ",
diff --git a/src/App.js b/src/App.js
index 181dc8686a114cb0b47057ed5b87ea1b081d5ffe..af68f32bf4a166f5e05b57e1c09e27fa15df4a4b 100644
--- a/src/App.js
+++ b/src/App.js
@@ -1,26 +1,26 @@
 import React from 'react';
 import {
-  RouterProvider,
   createHashRouter,
-  Route,
   createRoutesFromElements,
-  Navigate,
+  Route,
+  RouterProvider,
 } from 'react-router-dom';
-import Home from './pages/home';
 import Search from './pages/search';
 import Profile from './pages/profile';
 import Layout from './components/Layout';
 import ErrorBoundary from './pages/error/ErrorBoundary';
+import About from './pages/about';
 
 const App = () => {
   const router = createHashRouter(
     createRoutesFromElements(
       <>
-        <Route path="/" element={<Navigate to="/home" />} />
         <Route errorElement={<ErrorBoundary />} element={<Layout />}>
-          <Route index path="/home" element={<Home />} />
+          <Route index path="/" element={<Search />} />
           <Route path="/search" element={<Search />} />
           <Route path="/profile" element={<Profile />} />
+          <Route path="/about" element={<About />} />
+          <Route element={<Search />} />
         </Route>
       </>
     )
diff --git a/src/Utils.js b/src/Utils.js
index 7042c6f617d75d0a7399417233982f6ab7a26011..f3f6d528255101474bee5203d8afee2a7c136817 100644
--- a/src/Utils.js
+++ b/src/Utils.js
@@ -8,12 +8,6 @@ export class SearchField {
   }
 }
 
-export const getLoginUrl = () => {
-  return process.env.REACT_APP_IN_SYLVA_LOGIN_PORT
-    ? `${process.env.REACT_APP_IN_SYLVA_LOGIN_HOST}:${process.env.REACT_APP_IN_SYLVA_LOGIN_PORT}`
-    : `${window._env_.REACT_APP_IN_SYLVA_LOGIN_HOST}`;
-};
-
 export const removeNullFields = (standardFields) => {
   let standardFieldsNoNull = standardFields;
   let nullIndex;
@@ -47,28 +41,32 @@ export const getSections = (fields) => {
 };
 
 export const getFieldsBySection = (fields, fieldSection) => {
+  if (!fieldSection) {
+    return [];
+  }
+
   let filteredFields = [];
   fields.forEach((field) => {
     if (
       field.field_name &&
       field.field_name.split('.', 1)[0].replace(/_|\./g, ' ') === fieldSection.label
     ) {
-      if (field.sources.length) {
-        filteredFields.push({
-          label: field.field_name
-            .substring(field.field_name.indexOf('.') + 1)
-            .replace(/_|\./g, ' '),
-          color: 'danger',
-        });
-      } else {
-        filteredFields.push({
-          label: field.field_name
-            .substring(field.field_name.indexOf('.') + 1)
-            .replace(/_|\./g, ' '),
-          color: 'primary',
-        });
-      }
+      // if (field.sources.length) {
+      //   filteredFields.push({
+      //     label: field.field_name
+      //       .substring(field.field_name.indexOf('.') + 1)
+      //       .replace(/_|\./g, ' '),
+      //     color: 'danger',
+      //   });
+      // } else {
+      filteredFields.push({
+        label: field.field_name
+          .substring(field.field_name.indexOf('.') + 1)
+          .replace(/_|\./g, ' '),
+        color: 'primary',
+      });
     }
+    //}
   });
   return filteredFields;
 };
@@ -163,7 +161,6 @@ const splitString = (str, bracketOpenRegex, bracketCloseRegex, separatorRegex) =
   let startSplitIndex = 0;
   let splitStr = [];
   for (let i = 0; i < requestTokens.length; i++) {
-    requestTokens.forEach((token, index) => {});
     switch (true) {
       case bracketOpenRegex.test(requestTokens[i]):
         openBracketCount = openBracketCount + 1;
@@ -237,7 +234,7 @@ const buildDslClause = (clause, fields) => {
           .trim();
         tmpQuery = `${tmpQuery} { "${firstValue}": { "gte": "${secondValue}" } } }, `;
         break;
-      default:
+      default: {
         let fieldName = splitClause[i]
           .substring(0, splitClause[i].indexOf('=') - 1)
           .trim();
@@ -248,15 +245,17 @@ const buildDslClause = (clause, fields) => {
             case 'Numeric':
               tmpQuery = `${tmpQuery} { "term": { "${fieldName}": "${splitClause[i].substring(splitClause[i].indexOf('=') + 1, splitClause[i].length).trim()}" } }, `;
               break;
-            default:
+            default: {
               let values = splitClause[i]
                 .substring(splitClause[i].indexOf('=') + 1, splitClause[i].length)
                 .split(',');
               values.forEach((value) => {
                 tmpQuery = `${tmpQuery} { "match": { "${fieldName}": { "query": "${value.trim()}", "operator": "AND" } } }, `;
               });
+            }
           }
         }
+      }
     }
   }
   if (tmpQuery.endsWith(', ')) {
@@ -268,7 +267,7 @@ const buildDslClause = (clause, fields) => {
 
 // const buildDslQuery = (request, query, fields) => {
 //     let tmpQuery = query
-const buildDslQuery = (request, fields) => {
+export const buildDslQuery = (request, fields) => {
   let requestStr = request.replace(/ OR /g, ' | ').replace(/ AND /g, ' & ').trim();
   let splitRequest = splitString(requestStr, /\(/, /\)/, /&|\|/);
   let tmpQuery = '';
@@ -361,70 +360,6 @@ export const createAdvancedQueriesBySource = (
   const indicesLists = [];
   const publicFieldnames = [];
   const privateFields = [];
-  /* fields.forEach(field => {
-        if (field.ispublic) {
-            publicFieldnames.push(field.field_name)
-        } else {
-            privateFields.push(field)
-        }
-    })
-
-    if (selectedSources.length) {
-        selectedSources.forEach(source => {
-            queriedSourcesId.push(source.id)
-        })
-    } else {
-        sources.forEach(source => {
-            queriedSourcesId.push(source.id)
-        })
-    }
-
-    noPolicySourcesId = queriedSourcesId
-
-    //browse fields to create a map containing the fields available by source
-    //ie : [field1, field2] - [source1], [field1, field3, field4] - [source2], [field1] - []
-    //empty brackets means "the sources not affected by policies" at that point - this list is being compiled at the same time
-    if (privateFields.length) {
-        privateFields.forEach(field => {
-            field.sources.forEach(sourceId => {
-                if (queriedSourcesId.includes(sourceId)) {
-                    const index = findArray(sourcesLists, [sourceId])
-                    if (index >= 0) {
-                        fieldsLists[index].push(field.field_name)
-                    } else {
-                        fieldsLists.push([field.field_name])
-                        sourcesLists.push([sourceId])
-                    }
-                }
-                //filter no policy sources
-                if (noPolicySourcesId.includes(sourceId))
-                    noPolicySourcesId = removeArrayElement(noPolicySourcesId, noPolicySourcesId.indexOf(sourceId))
-            })
-        })
-        //add the public fields for every source
-        fieldsLists.forEach(fieldList => {
-            fieldList.push(...publicFieldnames)
-        })
-    }
-
-    const indexOfAllSources = findArray(sourcesLists, [])
-    //if there isn't any source with no policy, remove corresponding items in sourcesLists and fieldsLists
-    if (!noPolicySourcesId.length) {
-        sourcesLists = removeArrayElement(sourcesLists, indexOfAllSources)
-        fieldsLists = removeArrayElement(fieldsLists, indexOfAllSources)
-    } else {
-        sourcesLists = updateArrayElement(sourcesLists, indexOfAllSources, noPolicySourcesId)
-    }
-
-    //get elastic indices from sources id
-    sourcesLists.forEach(sourceList => {
-        const indicesList = []
-        sourceList.forEach(sourceId => {
-            const source = sources.find(src => src.id === sourceId)
-            indicesList.push(source.index_id)
-        })
-        indicesLists.push(indicesList)
-    }) */
 
   fields.forEach((field) => {
     if (field.ispublic) {
@@ -517,104 +452,3 @@ export const createAdvancedQueriesBySource = (
   });
   return queries;
 };
-
-export const createBasicQueriesBySource = (
-  fields,
-  searchRequest,
-  selectedSources,
-  sources
-) => {
-  const queries = [];
-  let fieldsLists = [[]];
-  let sourcesLists = [[]];
-  let queriedSourcesId = [];
-  let noPolicySourcesId = [];
-  const indicesLists = [];
-  const publicFieldnames = [];
-  const privateFields = [];
-
-  fields.forEach((field) => {
-    if (field.ispublic) {
-      publicFieldnames.push(field.field_name);
-    } else {
-      privateFields.push(field);
-    }
-  });
-
-  if (selectedSources.length) {
-    selectedSources.forEach((source) => {
-      queriedSourcesId.push(source.id);
-    });
-  } else {
-    if (sources.length) {
-      sources.forEach((source) => {
-        queriedSourcesId.push(source.id);
-      });
-    }
-  }
-
-  noPolicySourcesId = queriedSourcesId;
-
-  //browse fields to create a map containing the fields available by source
-  //ie : [field1, field2] - [source1], [field1, field3, field4] - [source2], [field1] - []
-  //empty brackets means "the sources not affected by policies" at that point
-  if (privateFields.length) {
-    privateFields.forEach((field) => {
-      field.sources.forEach((sourceId) => {
-        if (queriedSourcesId.includes(sourceId)) {
-          const index = findArray(sourcesLists, [sourceId]);
-          if (index >= 0) {
-            fieldsLists[index].push(field.field_name);
-          } else {
-            fieldsLists.push([field.field_name]);
-            sourcesLists.push([sourceId]);
-          }
-        }
-        //filter no policy sources
-        if (noPolicySourcesId.includes(sourceId))
-          noPolicySourcesId = removeArrayElement(
-            noPolicySourcesId,
-            noPolicySourcesId.indexOf(sourceId)
-          );
-      });
-    });
-    //add the public fields for every source
-    fieldsLists.forEach((fieldList) => {
-      fieldList.push(...publicFieldnames);
-    });
-  }
-
-  const indexOfAllSources = findArray(sourcesLists, []);
-  //if there is no source with no policy, remove corresponding items in sourcesLists and fieldsLists
-  if (!noPolicySourcesId.length) {
-    sourcesLists = removeArrayElement(sourcesLists, indexOfAllSources);
-    fieldsLists = removeArrayElement(fieldsLists, indexOfAllSources);
-  } else {
-    sourcesLists = updateArrayElement(sourcesLists, indexOfAllSources, noPolicySourcesId);
-    fieldsLists = updateArrayElement(fieldsLists, indexOfAllSources, publicFieldnames);
-  }
-
-  //get elastic indices from sources id
-  sourcesLists.forEach((sourceList) => {
-    const indicesList = [];
-    sourceList.forEach((sourceId) => {
-      const source = sources.find((src) => src.id === sourceId);
-      indicesList.push(source.index_id);
-    });
-    indicesLists.push(indicesList);
-  });
-
-  sourcesLists.forEach((sourcesArray, index) => {
-    let sourceParam = `"_source": [`;
-    fieldsLists[index].forEach((fieldName) => {
-      sourceParam = `${sourceParam} "${fieldName}", `;
-    });
-    if (sourceParam.endsWith(', ')) {
-      sourceParam = sourceParam.substring(0, sourceParam.length - 2);
-    }
-    sourceParam = `${sourceParam}],`;
-    let query = `{ ${sourceParam} "query": { "multi_match": { "query": "${searchRequest}", "operator": "AND", "type": "cross_fields" } } }`;
-    queries.push({ indicesId: indicesLists[index], query: JSON.parse(query) });
-  });
-  return queries;
-};
diff --git a/src/actions/index.js b/src/actions/index.js
deleted file mode 100644
index 245f568201757c06ee57c2d7c5ba4c3850037dab..0000000000000000000000000000000000000000
--- a/src/actions/index.js
+++ /dev/null
@@ -1,2 +0,0 @@
-import * as user from './user';
-export { user };
diff --git a/src/actions/source.js b/src/actions/source.js
deleted file mode 100644
index cd208f5527c2288ee9efe5417e86370af3434bdd..0000000000000000000000000000000000000000
--- a/src/actions/source.js
+++ /dev/null
@@ -1,61 +0,0 @@
-import { InSylvaSourceManagerClient } from '../context/InSylvaSourceManagerClient';
-import { InSylvaSearchClient } from '../context/InSylvaSearchClient';
-import { refreshToken } from '../context/UserContext';
-import { tokenTimedOut } from '../Utils';
-
-const ismClient = new InSylvaSourceManagerClient();
-ismClient.baseUrl = process.env.REACT_APP_IN_SYLVA_SOURCE_MANAGER_PORT
-  ? `${process.env.REACT_APP_IN_SYLVA_SOURCE_MANAGER_HOST}:${process.env.REACT_APP_IN_SYLVA_SOURCE_MANAGER_PORT}`
-  : `${window._env_.REACT_APP_IN_SYLVA_SOURCE_MANAGER_HOST}`;
-const isClient = new InSylvaSearchClient();
-isClient.baseUrl = process.env.REACT_APP_IN_SYLVA_SEARCH_PORT
-  ? `${process.env.REACT_APP_IN_SYLVA_SEARCH_HOST}:${process.env.REACT_APP_IN_SYLVA_SEARCH_PORT}`
-  : `${window._env_.REACT_APP_IN_SYLVA_SEARCH_HOST}`;
-
-export {
-  fetchPublicFields,
-  fetchUserPolicyFields,
-  fetchSources,
-  searchQuery,
-  getQueryCount,
-};
-
-async function fetchPublicFields() {
-  if (tokenTimedOut(process.env.REACT_KEYCLOAK_TOKEN_VALIDITY)) {
-    refreshToken();
-  }
-  ismClient.token = sessionStorage.getItem('access_token');
-  return await ismClient.publicFields();
-}
-
-async function fetchUserPolicyFields(kcId) {
-  if (tokenTimedOut(process.env.REACT_KEYCLOAK_TOKEN_VALIDITY)) {
-    refreshToken();
-  }
-  ismClient.token = sessionStorage.getItem('access_token');
-  return await ismClient.userPolicyFields(kcId);
-}
-
-async function fetchSources(kcId) {
-  if (tokenTimedOut(process.env.REACT_KEYCLOAK_TOKEN_VALIDITY)) {
-    refreshToken();
-  }
-  ismClient.token = sessionStorage.getItem('access_token');
-  return await ismClient.sourcesWithIndexes(kcId);
-}
-
-async function searchQuery(query) {
-  if (tokenTimedOut(process.env.REACT_KEYCLOAK_TOKEN_VALIDITY)) {
-    refreshToken();
-  }
-  isClient.token = sessionStorage.getItem('access_token');
-  return await isClient.search(query);
-}
-
-async function getQueryCount(queries) {
-  if (tokenTimedOut(process.env.REACT_KEYCLOAK_TOKEN_VALIDITY)) {
-    refreshToken();
-  }
-  isClient.token = sessionStorage.getItem('access_token');
-  return await isClient.count(queries);
-}
diff --git a/src/actions/user.js b/src/actions/user.js
deleted file mode 100644
index d5cc589c313b89b63005dedefc395326a0b95d25..0000000000000000000000000000000000000000
--- a/src/actions/user.js
+++ /dev/null
@@ -1,216 +0,0 @@
-import { InSylvaGatekeeperClient } from '../context/InSylvaGatekeeperClient';
-import { refreshToken } from '../context/UserContext';
-import { tokenTimedOut } from '../Utils';
-
-const igClient = new InSylvaGatekeeperClient();
-igClient.baseUrl = process.env.REACT_APP_IN_SYLVA_GATEKEEPER_PORT
-  ? `${process.env.REACT_APP_IN_SYLVA_GATEKEEPER_HOST}:${process.env.REACT_APP_IN_SYLVA_GATEKEEPER_PORT}`
-  : `${window._env_.REACT_APP_IN_SYLVA_GATEKEEPER_HOST}`;
-
-export const findOneUser = async (id, request = igClient) => {
-  if (tokenTimedOut(process.env.REACT_KEYCLOAK_TOKEN_VALIDITY)) {
-    await refreshToken();
-  }
-  igClient.token = sessionStorage.getItem('access_token');
-  try {
-    const user = await request.findOneUser(id);
-    if (user) {
-      return user;
-    }
-  } catch (error) {
-    console.error(error);
-  }
-};
-
-export const findOneUserWithGroupAndRole = async (kcId) => {
-  if (tokenTimedOut(process.env.REACT_KEYCLOAK_TOKEN_VALIDITY)) {
-    await refreshToken();
-  }
-  igClient.token = sessionStorage.getItem('access_token');
-  try {
-    const user = await igClient.findOneUserWithGroupAndRole(kcId);
-    if (user) {
-      return user;
-    }
-  } catch (error) {
-    console.error(error);
-  }
-};
-
-export const getGroups = async () => {
-  if (tokenTimedOut(process.env.REACT_KEYCLOAK_TOKEN_VALIDITY)) {
-    await refreshToken();
-  }
-  igClient.token = sessionStorage.getItem('access_token');
-  try {
-    const groups = await igClient.getGroups();
-    if (groups) {
-      return groups;
-    }
-  } catch (error) {
-    console.error(error);
-  }
-};
-
-export const getRoles = async () => {
-  if (tokenTimedOut(process.env.REACT_KEYCLOAK_TOKEN_VALIDITY)) {
-    await refreshToken();
-  }
-  igClient.token = sessionStorage.getItem('access_token');
-  try {
-    const roles = await igClient.findRole();
-    if (roles) {
-      return roles;
-    }
-  } catch (error) {
-    console.error(error);
-  }
-};
-
-export const sendMail = async (subject, message, request = igClient) => {
-  if (tokenTimedOut(process.env.REACT_KEYCLOAK_TOKEN_VALIDITY)) {
-    await refreshToken();
-  }
-  igClient.token = sessionStorage.getItem('access_token');
-  try {
-    await request.sendMail(subject, message);
-  } catch (error) {
-    console.error(error);
-  }
-};
-
-export const fetchUserDetails = async (kcId) => {
-  try {
-    return await igClient.getUserDetails(kcId);
-  } catch (error) {
-    console.error(error);
-  }
-};
-
-export const fetchUserRequests = async (kcId) => {
-  if (tokenTimedOut(process.env.REACT_KEYCLOAK_TOKEN_VALIDITY)) {
-    await refreshToken();
-  }
-  igClient.token = sessionStorage.getItem('access_token');
-  try {
-    const requests = await igClient.getUserRequests(kcId);
-    if (requests) {
-      return requests;
-    }
-  } catch (error) {
-    console.error(error);
-  }
-};
-
-export const createUserRequest = async (kcId, message) => {
-  if (tokenTimedOut(process.env.REACT_KEYCLOAK_TOKEN_VALIDITY)) {
-    await refreshToken();
-  }
-  igClient.token = sessionStorage.getItem('access_token');
-  try {
-    return await igClient.createUserRequest(kcId, message);
-  } catch (error) {
-    console.error(error);
-  }
-};
-
-export const deleteUserRequest = async (requestId) => {
-  if (tokenTimedOut(process.env.REACT_KEYCLOAK_TOKEN_VALIDITY)) {
-    await refreshToken();
-  }
-  igClient.token = sessionStorage.getItem('access_token');
-  try {
-    return await igClient.deleteUserRequest(requestId);
-  } catch (error) {
-    console.error(error);
-  }
-};
-
-export const addUserHistory = async (kcId, query, name, uiStructure, description) => {
-  if (tokenTimedOut(process.env.REACT_KEYCLOAK_TOKEN_VALIDITY)) {
-    await refreshToken();
-  }
-  igClient.token = sessionStorage.getItem('access_token');
-  try {
-    const jsonUIStructure = JSON.stringify(uiStructure);
-    return await igClient.addUserHistory(kcId, query, name, jsonUIStructure, description);
-  } catch (error) {
-    console.error(error);
-  }
-};
-
-export const fetchUserHistory = async (kcId) => {
-  if (tokenTimedOut(process.env.REACT_KEYCLOAK_TOKEN_VALIDITY)) {
-    await refreshToken();
-  }
-  igClient.token = sessionStorage.getItem('access_token');
-  try {
-    const history = await igClient.userHistory(kcId);
-    if (history) {
-      return history;
-    }
-  } catch (error) {
-    console.error(error);
-  }
-};
-
-export const deleteUserHistory = async (id) => {
-  if (tokenTimedOut(process.env.REACT_KEYCLOAK_TOKEN_VALIDITY)) {
-    await refreshToken();
-  }
-  igClient.token = sessionStorage.getItem('access_token');
-  try {
-    await igClient.deleteUserHistory(id);
-  } catch (error) {
-    console.error(error);
-  }
-};
-
-export const fetchUserFieldsDisplaySettings = async (userId) => {
-  if (tokenTimedOut(process.env.REACT_KEYCLOAK_TOKEN_VALIDITY)) {
-    await refreshToken();
-  }
-  igClient.token = sessionStorage.getItem('access_token');
-  try {
-    const row = await igClient.fetchUserFieldsDisplaySettings(userId);
-    return row.std_fields_ids;
-  } catch (error) {
-    console.error(error);
-  }
-};
-
-export const createUserFieldsDisplaySettings = async (userId, stdFieldsIds) => {
-  if (tokenTimedOut(process.env.REACT_KEYCLOAK_TOKEN_VALIDITY)) {
-    await refreshToken();
-  }
-  igClient.token = sessionStorage.getItem('access_token');
-  try {
-    return await igClient.createUserFieldsDisplaySettings(userId, stdFieldsIds);
-  } catch (error) {
-    console.error(error);
-  }
-};
-
-export const updateUserFieldsDisplaySettings = async (userId, stdFieldsIds) => {
-  if (tokenTimedOut(process.env.REACT_KEYCLOAK_TOKEN_VALIDITY)) {
-    await refreshToken();
-  }
-  igClient.token = sessionStorage.getItem('access_token');
-  try {
-    return await igClient.updateUserFieldsDisplaySettings(userId, stdFieldsIds);
-  } catch (error) {
-    console.error(error);
-  }
-};
-
-export const deleteUserFieldsDisplaySettings = async (userId) => {
-  if (tokenTimedOut(process.env.REACT_KEYCLOAK_TOKEN_VALIDITY)) {
-    await refreshToken();
-  }
-  igClient.token = sessionStorage.getItem('access_token');
-  try {
-    return await igClient.deleteUserFieldsDisplaySettings(userId);
-  } catch (error) {
-    console.error(error);
-  }
-};
diff --git a/src/components/Header/Header.js b/src/components/Header/Header.js
index 2e653c1e436f4ce3b471ba68ea66ffbd21789552..700f8fae776eb6f226c22737a3ff84f37cb60bdb 100644
--- a/src/components/Header/Header.js
+++ b/src/components/Header/Header.js
@@ -1,49 +1,108 @@
-import React from 'react';
-import { NavLink } from 'react-router-dom';
+import React, { useEffect, useState } from 'react';
 import {
   EuiHeader,
+  EuiHeaderLink,
+  EuiHeaderLinks,
+  EuiHeaderLogo,
   EuiHeaderSection,
   EuiHeaderSectionItem,
-  EuiHeaderLinks,
-  EuiHeaderLink,
+  EuiLink,
+  EuiSpacer,
+  EuiToolTip,
 } from '@elastic/eui';
 import HeaderUserMenu from './HeaderUserMenu';
 import style from './styles';
 import logoInSylva from '../../assets/favicon.svg';
 import { useTranslation } from 'react-i18next';
 import LanguageSwitcher from '../LanguageSwitcher/LanguageSwitcher';
+import { useGatekeeper } from '../../contexts/GatekeeperContext';
+import { useUserInfo } from '../../contexts/TokenContext';
+import { NavLink } from 'react-router-dom';
 
 const routes = [
   {
     id: 0,
-    label: 'home',
-    href: '/home',
+    label: 'search',
+    href: '/search',
   },
   {
     id: 1,
-    label: 'search',
-    href: '/search',
+    label: 'about',
+    href: '/about',
   },
 ];
 
 const Header = () => {
   const { t } = useTranslation(['header', 'common']);
+  const client = useGatekeeper();
+  const getUserInfo = useUserInfo();
+  const [userRoleId, setUserRoleId] = useState(0);
+
+  useEffect(() => {
+    const fetchUserRoleID = async () => {
+      const { sub } = await getUserInfo();
+      const user = await client.findUserBySub(sub);
+      if (!user.id) {
+        return;
+      }
+      const { roles } = user;
+      return roles[0]?.role?.id;
+    };
+
+    fetchUserRoleID().then((userRoleID) => {
+      setUserRoleId(userRoleID);
+    });
+  }, []);
+
+  const PortalLink = () => {
+    if (userRoleId >= 1) {
+      return (
+        <EuiHeaderSectionItem>
+          <EuiHeaderLink>
+            <EuiToolTip position="bottom" content={t('header:portalLink.tooltip')}>
+              <EuiLink
+                href={process.env.REACT_APP_IN_SYLVA_PORTAL_BASE_URL}
+                target="_blank"
+                rel="noopener noreferrer"
+              >
+                {t(`header:portalLink.title`)}
+              </EuiLink>
+            </EuiToolTip>
+          </EuiHeaderLink>
+          <EuiSpacer size={'s'} />
+        </EuiHeaderSectionItem>
+      );
+    }
+  };
 
   return (
     <EuiHeader>
       <EuiHeaderSection grow={true}>
-        <EuiHeaderSectionItem>
-          <img style={style.logo} src={logoInSylva} alt={t('common:inSylvaLogoAlt')} />
+        <EuiHeaderSectionItem border={'none'}>
+          <EuiHeaderLogo
+            iconType={() => {
+              return (
+                <img
+                  style={style.logo}
+                  src={logoInSylva}
+                  alt={t('common:inSylvaLogoAlt')}
+                />
+              );
+            }}
+            href="#/"
+            aria-label={t('header:homepageRedirect')}
+          />
         </EuiHeaderSectionItem>
         <EuiHeaderLinks>
           {routes.map((link) => (
-            <EuiHeaderLink key={link.id}>
-              <NavLink to={link.href}>{t(`tabs.${link.label}`)}</NavLink>
+            <EuiHeaderLink key={link.id} size={'m'}>
+              <NavLink to={link.href}>{t(`header:tabs.${link.label}`)}</NavLink>
             </EuiHeaderLink>
           ))}
         </EuiHeaderLinks>
       </EuiHeaderSection>
       <EuiHeaderSection side="right">
+        <PortalLink />
         <EuiHeaderSectionItem style={style.languageSwitcherItem} border={'none'}>
           <LanguageSwitcher />
         </EuiHeaderSectionItem>
diff --git a/src/components/Header/HeaderUserMenu.js b/src/components/Header/HeaderUserMenu.js
index 0b4a4a96d621d1af9ecf2d2ff92b5afb946864ec..cbf3f374c30f4cd29b4e201b320adc4c822b9ea2 100644
--- a/src/components/Header/HeaderUserMenu.js
+++ b/src/components/Header/HeaderUserMenu.js
@@ -1,4 +1,5 @@
 import React, { useEffect, useState } from 'react';
+import { useAuth } from 'oidc-react';
 import {
   EuiAvatar,
   EuiFlexGroup,
@@ -8,11 +9,11 @@ import {
   EuiPopover,
   EuiButtonIcon,
 } from '@elastic/eui';
-import { signOut } from '../../context/UserContext';
 import { useTranslation } from 'react-i18next';
 import { NavLink } from 'react-router-dom';
 
 const HeaderUserMenu = () => {
+  const auth = useAuth();
   const { t } = useTranslation('header');
   const [isOpen, setIsOpen] = useState(false);
   const [username, setUsername] = useState('');
@@ -26,7 +27,7 @@ const HeaderUserMenu = () => {
   };
 
   useEffect(() => {
-    setUsername(sessionStorage.getItem('username'));
+    setUsername(auth.userData?.profile?.preferred_username || '');
   }, []);
 
   const HeaderUserButton = (
@@ -63,7 +64,7 @@ const HeaderUserMenu = () => {
                   </NavLink>
                 </EuiFlexItem>
                 <EuiFlexItem grow={false}>
-                  <NavLink to={'/'} onClick={() => signOut()}>
+                  <NavLink to={'/'} onClick={() => auth.signOutRedirect()}>
                     {t('header:userMenu.logOutButton')}
                   </NavLink>
                 </EuiFlexItem>
diff --git a/src/components/Header/styles.js b/src/components/Header/styles.js
index 94f8ecf22cfa183f26e33b3f3c7ae6086c4730ee..1ccde11a8f7ec3bc8b52a54e1585dd3eb997e9a5 100644
--- a/src/components/Header/styles.js
+++ b/src/components/Header/styles.js
@@ -2,7 +2,7 @@ const headerStyle = {
   logo: {
     width: '75px',
     height: '75px',
-    padding: '10px',
+    padding: '12px',
   },
   languageSwitcherItem: {
     margin: '10px',
diff --git a/src/components/Layout/Layout.js b/src/components/Layout/Layout.js
index 087c5ce116b09f9ca0c8e5bfe70c16f0a5825fa2..743a15b477ba72cee8cd3df4cb45ae7290c1619c 100644
--- a/src/components/Layout/Layout.js
+++ b/src/components/Layout/Layout.js
@@ -1,15 +1,14 @@
 import React from 'react';
 import { Outlet } from 'react-router-dom';
 import Header from '../../components/Header';
-import { EuiPage, EuiPageBody, EuiPageSection } from '@elastic/eui';
-import styles from './styles.js';
+import { EuiFlexGroup, EuiPage, EuiPageBody, EuiPageSection } from '@elastic/eui';
 import { Slide, ToastContainer } from 'react-toastify';
 import 'react-toastify/dist/ReactToastify.css';
 
 const Layout = () => {
   return (
-    <EuiPage style={styles.page} restrictWidth={false}>
-      <EuiPageBody>
+    <EuiFlexGroup style={{ minHeight: '100vh' }}>
+      <EuiPage direction={'column'}>
         <ToastContainer
           position="bottom-right"
           autoClose={5000}
@@ -23,12 +22,14 @@ const Layout = () => {
           theme="light"
           transition={Slide}
         />
-        <Header />
-        <EuiPageSection style={styles.pageContent} grow={true}>
-          <Outlet />
-        </EuiPageSection>
-      </EuiPageBody>
-    </EuiPage>
+        <EuiPageBody>
+          <Header />
+          <EuiPageSection grow={true} color="plain">
+            <Outlet />
+          </EuiPageSection>
+        </EuiPageBody>
+      </EuiPage>
+    </EuiFlexGroup>
   );
 };
 
diff --git a/src/components/Layout/styles.js b/src/components/Layout/styles.js
deleted file mode 100644
index fd435cba3022f538dbb0d55ee19b69aee634fe41..0000000000000000000000000000000000000000
--- a/src/components/Layout/styles.js
+++ /dev/null
@@ -1,10 +0,0 @@
-const styles = {
-  page: {
-    padding: '0px',
-  },
-  pageContent: {
-    backgroundColor: '#ffffff',
-  },
-};
-
-export default styles;
diff --git a/src/components/NoResultEmptyPrompt/NoResultEmptyPrompt.js b/src/components/NoResultEmptyPrompt/NoResultEmptyPrompt.js
new file mode 100644
index 0000000000000000000000000000000000000000..1685cca1c021f6f8e40b7b71632d2dfc9e0119ac
--- /dev/null
+++ b/src/components/NoResultEmptyPrompt/NoResultEmptyPrompt.js
@@ -0,0 +1,54 @@
+import React from 'react';
+import {
+  EuiButton,
+  EuiDescriptionList,
+  EuiDescriptionListDescription,
+  EuiDescriptionListTitle,
+  EuiEmptyPrompt,
+  EuiFlexGroup,
+} from '@elastic/eui';
+import { useTranslation } from 'react-i18next';
+
+const NoResultEmptyPrompt = ({ setSelectedTabNumber }) => {
+  const { t } = useTranslation('results');
+
+  const body = (
+    <EuiDescriptionList compressed>
+      <EuiDescriptionListTitle>
+        {t('results:noResult.body.title')}
+      </EuiDescriptionListTitle>
+      <EuiDescriptionListDescription>
+        {t('results:noResult.body.description')}
+      </EuiDescriptionListDescription>
+    </EuiDescriptionList>
+  );
+
+  const actions = [
+    <EuiButton
+      onClick={() => {
+        setSelectedTabNumber(0);
+      }}
+      color="primary"
+      fill
+      key={0}
+      iconType={'pencil'}
+    >
+      {t('results:noResult.action')}
+    </EuiButton>,
+  ];
+
+  return (
+    <EuiFlexGroup style={{ minHeight: '75vh' }}>
+      <EuiEmptyPrompt
+        title={<h2>{t('results:noResult.title')}</h2>}
+        titleSize={'l'}
+        iconType={'magnifyWithExclamation'}
+        color={'plain'}
+        body={body}
+        actions={actions}
+      />
+    </EuiFlexGroup>
+  );
+};
+
+export default NoResultEmptyPrompt;
diff --git a/src/components/ToastMessage/ToastMessage.js b/src/components/ToastMessage/ToastMessage.js
index 72e024d07d7a6a01f54f44ec2877ee48880afc61..0be043919bf7bad0d33e98000b2cf1589d48ded9 100644
--- a/src/components/ToastMessage/ToastMessage.js
+++ b/src/components/ToastMessage/ToastMessage.js
@@ -1,5 +1,5 @@
 import React from 'react';
-import { EuiTitle, EuiSpacer } from '@elastic/eui';
+import { EuiSpacer, EuiText, EuiTitle } from '@elastic/eui';
 
 const ToastMessage = ({ title, message }) => {
   return (
@@ -8,7 +8,9 @@ const ToastMessage = ({ title, message }) => {
         <p>{title}</p>
       </EuiTitle>
       <EuiSpacer size={'m'} />
-      <p>{message}</p>
+      <EuiText>
+        <p>{message}</p>
+      </EuiText>
     </>
   );
 };
diff --git a/src/context/InSylvaGatekeeperClient.js b/src/context/InSylvaGatekeeperClient.js
deleted file mode 100644
index 26f10787d4e09845938d541d741098a20cbafea1..0000000000000000000000000000000000000000
--- a/src/context/InSylvaGatekeeperClient.js
+++ /dev/null
@@ -1,138 +0,0 @@
-class InSylvaGatekeeperClient {
-  async post(method, path, requestContent) {
-    const headers = {
-      'Content-Type': 'application/json',
-      Authorization: `Bearer ${this.token}`,
-    };
-    const response = await fetch(`${this.baseUrl}${path}`, {
-      method,
-      headers,
-      body: JSON.stringify(requestContent),
-      mode: 'cors',
-    });
-    return await response.json();
-  }
-
-  async getGroups() {
-    const path = `/user/groups`;
-    return await this.post('POST', `${path}`, {});
-  }
-
-  async getUserRequests(kcId) {
-    const path = `/user/list-requests-by-user`;
-    return await this.post('POST', `${path}`, {
-      kcId,
-    });
-  }
-
-  async createUserRequest(kcId, message) {
-    const path = `/user/create-request`;
-    return await this.post('POST', `${path}`, {
-      kcId,
-      message,
-    });
-  }
-
-  async deleteUserRequest(id) {
-    const path = `/user/delete-request`;
-    return await this.post('DELETE', `${path}`, { id });
-  }
-
-  async findRole() {
-    const path = `/role/find`;
-    return await this.post('GET', `${path}`);
-  }
-
-  async kcId({ email }) {
-    const path = `/user/kcid`;
-    return await this.post('POST', `${path}`, {
-      email,
-    });
-  }
-
-  async sendMail(subject, message) {
-    const path = `/user/send-mail`;
-
-    await this.post('POST', `${path}`, {
-      subject,
-      message,
-    });
-  }
-
-  async findOneUser(id) {
-    const path = `/user/findOne`;
-    return await this.post('POST', `${path}`, {
-      id,
-    });
-  }
-
-  // Returns an array containing objects for each user group.
-  async findOneUserWithGroupAndRole(kcId) {
-    const path = `/user/one-with-groups-and-roles`;
-    return await this.post('POST', `${path}`, {
-      id: kcId,
-    });
-  }
-
-  async getUserDetails(kcId) {
-    const path = `/user/detail`;
-    return await this.post('POST', `${path}`, {
-      kcId,
-    });
-  }
-
-  async addUserHistory(kcId, query, name, uiStructure, description) {
-    const path = `/user/add-history`;
-    return await this.post('POST', `${path}`, {
-      kcId,
-      query,
-      name,
-      uiStructure,
-      description,
-    });
-  }
-
-  async userHistory(kcId) {
-    const path = `/user/fetch-history`;
-    return await this.post('POST', `${path}`, {
-      kcId,
-    });
-  }
-
-  async deleteUserHistory(id) {
-    const path = `/user/delete-history`;
-    await this.post('POST', `${path}`, {
-      id,
-    });
-  }
-
-  // GET Fetches user fields display settings
-  async fetchUserFieldsDisplaySettings(userId) {
-    return await this.post('GET', `/user/fields-display-settings/${userId}`);
-  }
-
-  // POST Creates user fields display settings
-  async createUserFieldsDisplaySettings(userId, stdFieldsIds) {
-    return await this.post('POST', `/user/fields-display-settings/`, {
-      userId,
-      stdFieldsIds,
-    });
-  }
-
-  // PUT Updates user fields display settings
-  async updateUserFieldsDisplaySettings(userId, stdFieldsIds) {
-    return await this.post('PUT', `/user/fields-display-settings/`, {
-      userId,
-      stdFieldsIds,
-    });
-  }
-
-  // DELETE Remove user fields display settings
-  async deleteUserFieldsDisplaySettings(userId) {
-    return await this.post('DELETE', `/user/fields-display-settings/${userId}`);
-  }
-}
-
-InSylvaGatekeeperClient.prototype.baseUrl = null;
-InSylvaGatekeeperClient.prototype.token = null;
-export { InSylvaGatekeeperClient };
diff --git a/src/context/InSylvaKeycloakClient.js b/src/context/InSylvaKeycloakClient.js
deleted file mode 100644
index 092f27ca9a1c0e195a51c390533a08fd802107f1..0000000000000000000000000000000000000000
--- a/src/context/InSylvaKeycloakClient.js
+++ /dev/null
@@ -1,66 +0,0 @@
-import { getLoginUrl } from '../Utils';
-
-class InSylvaKeycloakClient {
-  async post(path, requestContent) {
-    const headers = {
-      'Content-Type': 'application/x-www-form-urlencoded',
-      // "Access-Control-Allow-Methods": "GET,HEAD,PUT,PATCH,POST,DELETE",
-      // "Access-Control-Allow-Headers": "Origin, X-Requested-With, Content-Type, Accept, Authorization"
-    };
-    let formBody = [];
-    for (const property in requestContent) {
-      const encodedKey = encodeURIComponent(property);
-      const encodedValue = encodeURIComponent(requestContent[property]);
-      formBody.push(encodedKey + '=' + encodedValue);
-    }
-    formBody = formBody.join('&');
-    const response = await fetch(`${this.baseUrl}${path}`, {
-      method: 'POST',
-      headers,
-      body: formBody,
-      mode: 'cors',
-    });
-    if (!response.ok) {
-      await this.logout();
-      sessionStorage.removeItem('kcId');
-      sessionStorage.removeItem('access_token');
-      sessionStorage.removeItem('refresh_token');
-      window.location.replace(getLoginUrl() + '?requestType=search');
-    }
-    if (response.statusText !== 'No Content') {
-      return await response.json();
-    }
-  }
-
-  async refreshToken({
-    realm = this.realm,
-    client_id = this.client_id,
-    grant_type = 'refresh_token',
-    refresh_token,
-  }) {
-    const path = `/auth/realms/${realm}/protocol/openid-connect/token`;
-    const token = await this.post(`${path}`, {
-      client_id,
-      grant_type,
-      refresh_token,
-    });
-    return { token };
-  }
-
-  async logout() {
-    const refresh_token = sessionStorage.getItem('refresh_token');
-    if (refresh_token) {
-      const client_id = this.client_id;
-      await this.post(`/auth/realms/${this.realm}/protocol/openid-connect/logout`, {
-        client_id,
-        refresh_token,
-      });
-    }
-  }
-}
-
-InSylvaKeycloakClient.prototype.baseUrl = null;
-InSylvaKeycloakClient.prototype.client_id = null;
-InSylvaKeycloakClient.prototype.grant_type = null;
-InSylvaKeycloakClient.prototype.realm = null;
-export { InSylvaKeycloakClient };
diff --git a/src/context/InSylvaSearchClient.js b/src/context/InSylvaSearchClient.js
deleted file mode 100644
index ffdc831b72ef715a652caadace18b8e3f2c09730..0000000000000000000000000000000000000000
--- a/src/context/InSylvaSearchClient.js
+++ /dev/null
@@ -1,67 +0,0 @@
-class InSylvaSearchClient {
-  async post(method, path, requestContent) {
-    const headers = {
-      'Content-Type': 'application/json',
-      'Access-Control-Allow-Origin': '*',
-      Authorization: `Bearer ${this.token}`,
-      'Access-Control-Max-Age': 86400,
-      // 'Access-Control-Allow-Methods': 'GET, POST, PUT, PATCH',
-    };
-    const response = await fetch(`${this.baseUrl}${path}`, {
-      method,
-      headers,
-      body: JSON.stringify(requestContent),
-      mode: 'cors',
-    });
-    return await response.json();
-  }
-
-  /* async search(query, indices) {
-        const indicesId = []
-        indices.forEach(index => {
-            indicesId.push(index.index_id)
-        })
-        const path = `/search`;
-        const result = await this.post('POST', `${path}`, {
-            indicesId,
-            query
-        });
-        return result;
-    } */
-
-  async search(queries) {
-    let finalResult = [];
-
-    for (let i = 0; i < queries.length; i++) {
-      const indicesId = queries[i].indicesId;
-      const query = queries[i].query;
-      const result = await this.post('POST', '/scroll-search', {
-        indicesId,
-        query,
-      });
-      if (!result.statusCode) {
-        finalResult.push(...result);
-      }
-    }
-    return finalResult;
-  }
-
-  async count(queries) {
-    let finalResult = 0;
-    for (let i = 0; i < queries.length; i++) {
-      const indicesId = queries[i].indicesId;
-      const query = queries[i].query;
-      const path = `/count`;
-      const result = await this.post('POST', `${path}`, {
-        indicesId,
-        query,
-      });
-      finalResult = finalResult + result.count;
-    }
-    return finalResult;
-  }
-}
-
-InSylvaSearchClient.prototype.baseUrl = null;
-InSylvaSearchClient.prototype.token = null;
-export { InSylvaSearchClient };
diff --git a/src/context/InSylvaSourceManagerClient.js b/src/context/InSylvaSourceManagerClient.js
deleted file mode 100644
index ceffbd624da8af86e2555a72ab9afdea255a7985..0000000000000000000000000000000000000000
--- a/src/context/InSylvaSourceManagerClient.js
+++ /dev/null
@@ -1,34 +0,0 @@
-class InSylvaSourceManagerClient {
-  async post(method, path, requestContent) {
-    const headers = {
-      'Content-Type': 'application/json',
-      Authorization: `Bearer ${this.token}`,
-    };
-    const response = await fetch(`${this.baseUrl}${path}`, {
-      method,
-      headers,
-      body: JSON.stringify(requestContent),
-      mode: 'cors',
-    });
-    return await response.json();
-  }
-
-  async publicFields() {
-    const path = `/publicFieldList`;
-    return this.post('GET', `${path}`);
-  }
-
-  async userPolicyFields(userId) {
-    const path = `/policy-stdfields`;
-    return this.post('POST', `${path}`, { userId });
-  }
-
-  async sourcesWithIndexes(kc_id) {
-    const path = `/source_indexes`;
-    return this.post('POST', `${path}`, { kc_id });
-  }
-}
-
-InSylvaSourceManagerClient.prototype.baseUrl = null;
-InSylvaSourceManagerClient.prototype.token = null;
-export { InSylvaSourceManagerClient };
diff --git a/src/context/UserContext.js b/src/context/UserContext.js
deleted file mode 100644
index 1b6d8b7946bbdc57a1abc0f3cee6a1115674ed4b..0000000000000000000000000000000000000000
--- a/src/context/UserContext.js
+++ /dev/null
@@ -1,141 +0,0 @@
-import React, { createContext, useContext, useReducer } from 'react';
-import { InSylvaGatekeeperClient } from './InSylvaGatekeeperClient';
-import { InSylvaKeycloakClient } from './InSylvaKeycloakClient';
-import { getLoginUrl } from '../Utils';
-import { fetchUserDetails, findOneUser } from '../actions/user';
-
-const UserStateContext = createContext(null);
-const UserDispatchContext = createContext(null);
-
-const igClient = new InSylvaGatekeeperClient();
-igClient.baseUrl = process.env.REACT_APP_IN_SYLVA_GATEKEEPER_PORT
-  ? `${process.env.REACT_APP_IN_SYLVA_GATEKEEPER_HOST}:${process.env.REACT_APP_IN_SYLVA_GATEKEEPER_PORT}`
-  : `${window._env_.REACT_APP_IN_SYLVA_GATEKEEPER_HOST}`;
-
-const ikcClient = new InSylvaKeycloakClient();
-ikcClient.baseUrl = process.env.REACT_APP_IN_SYLVA_KEYCLOAK_PORT
-  ? `${process.env.REACT_APP_IN_SYLVA_KEYCLOAK_HOST}:${process.env.REACT_APP_IN_SYLVA_KEYCLOAK_PORT}`
-  : `${window._env_.REACT_APP_IN_SYLVA_KEYCLOAK_HOST}`;
-ikcClient.realm = process.env.REACT_APP_IN_SYLVA_KEYCLOAK_PORT
-  ? `${process.env.REACT_APP_IN_SYLVA_REALM}`
-  : `${window._env_.REACT_APP_IN_SYLVA_REALM}`;
-ikcClient.client_id = process.env.REACT_APP_IN_SYLVA_KEYCLOAK_PORT
-  ? `${process.env.REACT_APP_IN_SYLVA_CLIENT_ID}`
-  : `${window._env_.REACT_APP_IN_SYLVA_CLIENT_ID}`;
-ikcClient.grant_type = process.env.REACT_APP_IN_SYLVA_KEYCLOAK_PORT
-  ? `${process.env.REACT_APP_IN_SYLVA_GRANT_TYPE}`
-  : `${window._env_.REACT_APP_IN_SYLVA_GRANT_TYPE}`;
-
-function userReducer(state, action) {
-  switch (action.type) {
-    case 'USER_LOGGED_IN':
-      return { ...state, isAuthenticated: true };
-    case 'SIGN_OUT_SUCCESS':
-      return { ...state, isAuthenticated: false };
-    case 'EXPIRED_SESSION':
-      return { ...state, isAuthenticated: false };
-    case 'USER_NOT_LOGGED_IN':
-      return { ...state, isAuthenticated: false };
-    case 'STD_FIELDS_SUCCESS':
-      return { ...state, isAuthenticated: true };
-    case 'STD_FIELDS_FAILURE':
-      return { ...state, isAuthenticated: true };
-    default: {
-      throw new Error(`Unhandled action type: ${action.type}`);
-    }
-  }
-}
-
-function UserProvider({ children }) {
-  const [state, dispatch] = useReducer(userReducer, {
-    isAuthenticated: !!sessionStorage.getItem('access_token'),
-  });
-
-  return (
-    <UserStateContext.Provider value={state}>
-      <UserDispatchContext.Provider value={dispatch}>
-        {children}
-      </UserDispatchContext.Provider>
-    </UserStateContext.Provider>
-  );
-}
-
-function useUserState() {
-  const context = useContext(UserStateContext);
-  if (context === undefined) {
-    throw new Error('useUserState must be used within a UserProvider');
-  }
-  return context;
-}
-
-function useUserDispatch() {
-  const context = useContext(UserDispatchContext);
-  if (context === undefined) {
-    throw new Error('useUserDispatch must be used within a UserProvider');
-  }
-  return context;
-}
-
-const getUserIdFromKcId = async (kcId) => {
-  if (!kcId) {
-    return;
-  }
-  const userDetails = await fetchUserDetails(kcId);
-  if (!userDetails) {
-    return;
-  }
-  return userDetails.id;
-};
-
-const checkUserLogin = async (kcId, accessToken, refreshToken) => {
-  if (!kcId || !accessToken || !refreshToken) {
-    return;
-  }
-  const userId = await getUserIdFromKcId(kcId);
-  if (userId) {
-    sessionStorage.setItem('userId', userId.toString());
-  }
-  const user = await findOneUser(kcId);
-  if (user) {
-    sessionStorage.setItem('username', user.username);
-    sessionStorage.setItem('email', user.email);
-  }
-  sessionStorage.setItem('kcId', kcId);
-  sessionStorage.setItem('access_token', accessToken);
-  sessionStorage.setItem('refresh_token', refreshToken);
-  if (!sessionStorage.getItem('token_refresh_time')) {
-    sessionStorage.setItem('token_refresh_time', Date.now().toString());
-  }
-};
-
-async function refreshToken() {
-  if (!sessionStorage.getItem('kcId')) {
-    return;
-  }
-  setTimeout(async () => {
-    const result = await ikcClient.refreshToken({
-      refresh_token: sessionStorage.getItem('refresh_token'),
-    });
-    if (result) {
-      sessionStorage.setItem('access_token', result.token.access_token);
-      sessionStorage.setItem('token_refresh_time', Date.now().toString());
-    }
-  }, 3000);
-}
-
-async function signOut() {
-  await ikcClient.logout();
-  sessionStorage.removeItem('kcId');
-  sessionStorage.removeItem('access_token');
-  sessionStorage.removeItem('refresh_token');
-  window.location.replace(getLoginUrl() + '?requestType=search');
-}
-
-export {
-  UserProvider,
-  useUserState,
-  useUserDispatch,
-  checkUserLogin,
-  refreshToken,
-  signOut,
-};
diff --git a/src/contexts/GatekeeperContext.js b/src/contexts/GatekeeperContext.js
new file mode 100644
index 0000000000000000000000000000000000000000..9604361803d89c99d70c49c2d09856efeff51203
--- /dev/null
+++ b/src/contexts/GatekeeperContext.js
@@ -0,0 +1,19 @@
+import React, { createContext, useContext } from 'react';
+import { InSylvaGatekeeperClient } from '../services/GatekeeperService';
+import { useUserInfo } from './TokenContext';
+
+const GatekeeperContext = createContext(null);
+
+export const useGatekeeper = () => {
+  const context = useContext(GatekeeperContext);
+  return context;
+};
+
+export const GatekeeperProvider = ({ children }) => {
+  const getUserInfo = useUserInfo();
+  const client = new InSylvaGatekeeperClient(getUserInfo);
+
+  return (
+    <GatekeeperContext.Provider value={client}>{children}</GatekeeperContext.Provider>
+  );
+};
diff --git a/src/contexts/TokenContext.js b/src/contexts/TokenContext.js
new file mode 100644
index 0000000000000000000000000000000000000000..dfbd78feb1229fc4e3ac5a0cfd52d02dfcd9f999
--- /dev/null
+++ b/src/contexts/TokenContext.js
@@ -0,0 +1,52 @@
+import React, { createContext, useContext } from 'react';
+import { useAuth } from 'oidc-react';
+import TokenService from '../services/TokenService';
+
+const TokenContext = createContext(null);
+
+export const useUserInfo = () => {
+  const context = useContext(TokenContext);
+  return context;
+};
+
+export const TokenProvider = ({ children }) => {
+  const auth = useAuth();
+
+  const getUserInfo = async () => {
+    //console.log('Getting access token');
+    if (!auth) {
+      //console.log('Auth not found');
+      return null;
+    }
+
+    if (auth.isLoading) {
+      //console.log('Auth is loading');
+      return null;
+    }
+
+    if (!auth.userData || !auth.userData.access_token) {
+      //console.log('User data not found');
+      await auth.signOutRedirect();
+    }
+
+    const access_token = auth.userData.access_token;
+    //console.log(access_token);
+
+    let userInfo = await new TokenService().getUserInfo(access_token);
+    if (!userInfo) {
+      //console.log('User info not found');
+      const refreshed = await new TokenService().refreshToken(
+        auth.userData.refresh_token
+      );
+      if (!refreshed) {
+        //console.log('Error refreshing token');
+        await auth.signOutRedirect();
+      }
+      userInfo = await new TokenService().getUserInfo(refreshed.access_token);
+    }
+    //console.log('User info:', userInfo);
+    return { ...userInfo, ...auth.userData };
+  };
+
+  return <TokenContext.Provider value={getUserInfo}>{children}</TokenContext.Provider>;
+};
diff --git a/src/i18n.js b/src/i18n.js
index d422da216a0d038d5978aba593300f8ac89a0ec8..5f49f6ef06094da4867af6babcc095607b26ca35 100644
--- a/src/i18n.js
+++ b/src/i18n.js
@@ -10,7 +10,7 @@ i18n
     fallbackLng: 'fr',
     ns: 'common',
     defaultNS: 'common',
-    debug: true,
+    debug: false,
     load: 'languageOnly',
     interpolation: {
       // not needed for react as it escapes by default
diff --git a/src/index.js b/src/index.js
index 3944da2e6f150426cf6528521524585007933714..1e27e35b7b3dce71fb3b49e23f611229417f51df 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,32 +1,38 @@
 import React, { Suspense } from 'react';
-import '@elastic/eui/dist/eui_theme_light.css';
-import { UserProvider, checkUserLogin } from './context/UserContext';
+import { createRoot } from 'react-dom/client';
+import { AuthProvider } from 'oidc-react';
 import App from './App';
-import { getLoginUrl, getUrlParam, redirect } from './Utils.js';
 import './i18n';
 import Loading from './components/Loading';
-import { createRoot } from 'react-dom/client';
 import { EuiProvider } from '@elastic/eui';
+import '@elastic/eui/dist/eui_theme_light.css';
+import { GatekeeperProvider } from './contexts/GatekeeperContext';
+import { TokenProvider } from './contexts/TokenContext';
 
-const userId = getUrlParam('kcId', '');
-const accessToken = getUrlParam('accessToken', '');
-let refreshToken = getUrlParam('refreshToken', '');
-if (refreshToken.includes('#/search')) {
-  refreshToken = refreshToken.substring(0, refreshToken.indexOf('#'));
-}
-checkUserLogin(userId, accessToken, refreshToken);
-
-if (sessionStorage.getItem('access_token')) {
-  const root = createRoot(document.getElementById('root'));
-  root.render(
-    <UserProvider>
-      <Suspense fallback={<Loading />}>
-        <EuiProvider colorMode={'light'}>
-          <App />
-        </EuiProvider>
-      </Suspense>
-    </UserProvider>
-  );
-} else {
-  redirect(getLoginUrl() + '?requestType=search');
-}
+const root = createRoot(document.getElementById('root'));
+root.render(
+  <AuthProvider
+    authority={`${process.env.REACT_APP_KEYCLOAK_BASE_URL}`}
+    clientId={`${process.env.REACT_APP_KEYCLOAK_CLIENT_ID}`}
+    clientSecret={`${process.env.REACT_APP_KEYCLOAK_CLIENT_SECRET}`}
+    redirectUri={`${process.env.REACT_APP_BASE_URL}`}
+    onBeforeSignIn={() => {
+      const redirectUri = window.location.hash;
+      return { postLoginRedirect: redirectUri };
+    }}
+    onSignIn={async (user) => {
+      const postLoginRedirect = user.state?.postLoginRedirect;
+      window.location.href = process.env.REACT_APP_BASE_URL + '/' + postLoginRedirect;
+    }}
+  >
+    <TokenProvider>
+      <GatekeeperProvider>
+        <Suspense fallback={<Loading />}>
+          <EuiProvider colorMode={'light'}>
+            <App />
+          </EuiProvider>
+        </Suspense>
+      </GatekeeperProvider>
+    </TokenProvider>
+  </AuthProvider>
+);
diff --git a/src/pages/about/About.js b/src/pages/about/About.js
new file mode 100644
index 0000000000000000000000000000000000000000..15a6b8f50f1a92fed15861f6075e25ae076f8fcb
--- /dev/null
+++ b/src/pages/about/About.js
@@ -0,0 +1,40 @@
+import React from 'react';
+import { Trans, useTranslation } from 'react-i18next';
+import { EuiLink, EuiSpacer, EuiText, EuiTitle } from '@elastic/eui';
+import { NavLink } from 'react-router-dom';
+
+const About = () => {
+  const { t } = useTranslation('about');
+
+  const abouts = t('about:abouts', { returnObjects: true });
+
+  return (
+    <>
+      <EuiTitle size="m">
+        <h4>{t('about:pageTitle')}</h4>
+      </EuiTitle>
+      <EuiSpacer size={'l'} />
+      <EuiText>
+        <Trans
+          components={[
+            <EuiLink
+              key={0}
+              href={
+                'https://entrepot.recherche.data.gouv.fr/file.xhtml?persistentId=doi:10.15454/ELXRGY/NCVTVR&version=5.1'
+              }
+              target="_blank"
+              rel="noopener noreferrer"
+            />,
+            <NavLink key={1} to={'/profile'} />,
+          ]}
+        >
+          {abouts.map((about, index) => (
+            <p key={index}>{about}</p>
+          ))}
+        </Trans>
+      </EuiText>
+    </>
+  );
+};
+
+export default About;
diff --git a/src/pages/home/package.json b/src/pages/about/package.json
similarity index 52%
rename from src/pages/home/package.json
rename to src/pages/about/package.json
index ba3c514f8b3222be3c05312714553240fe234506..2c0afee0efe598fcea668f461df49788016fb45e 100644
--- a/src/pages/home/package.json
+++ b/src/pages/about/package.json
@@ -1,6 +1,6 @@
 {
-  "name": "Home",
+  "name": "About",
   "version": "1.0.0",
   "private": true,
-  "main": "Home.js"
+  "main": "About.js"
 }
diff --git a/src/pages/home/Home.js b/src/pages/home/Home.js
deleted file mode 100644
index 43316d15cdf2bac74aebe8b145184359e4c4e6b7..0000000000000000000000000000000000000000
--- a/src/pages/home/Home.js
+++ /dev/null
@@ -1,42 +0,0 @@
-import React from 'react';
-import { useTranslation } from 'react-i18next';
-import { EuiTitle, EuiSpacer } from '@elastic/eui';
-
-const Home = () => {
-  const { t } = useTranslation('home');
-
-  return (
-    <>
-      <EuiTitle size="m">
-        <h4>{t('home:pageTitle')}</h4>
-      </EuiTitle>
-      <EuiSpacer size={'l'} />
-      <p>{t('home:searchToolDescription.part1')}</p>
-      <br />
-      <p>{t('home:searchToolDescription.part2')}</p>
-      <br />
-      <p>
-        {t('home:searchToolDescription.standardLink')}
-        <a
-          href={
-            'https://entrepot.recherche.data.gouv.fr/file.xhtml?persistentId=doi:10.15454/ELXRGY/NCVTVR&version=5.1'
-          }
-          target="_blank"
-          rel="noopener noreferrer"
-        >
-          https://entrepot.recherche.data.gouv.fr/file.xhtml?persistentId=doi:10.15454/ELXRGY/NCVTVR&version=5.1
-        </a>
-      </p>
-      <br />
-      <p>{t('home:searchToolDescription.part3')}</p>
-      <br />
-      <p>{t('home:searchToolDescription.part4')}</p>
-      <br />
-      <p>{t('home:searchToolDescription.part5')}</p>
-      <br />
-      <p>{t('home:searchToolDescription.part6')}</p>
-    </>
-  );
-};
-
-export default Home;
diff --git a/src/pages/maps/SearchMap.js b/src/pages/maps/SearchMap.js
index 709cbd3d37dd1f2bad294144c5b37aadcfa2f817..4226d8b66d637778420552ebcc6ee992ee96cf24 100644
--- a/src/pages/maps/SearchMap.js
+++ b/src/pages/maps/SearchMap.js
@@ -24,15 +24,19 @@ import 'ol/ol.css';
 import {
   EuiButton,
   EuiButtonGroup,
+  EuiButtonIcon,
+  EuiCallOut,
   EuiCheckbox,
   EuiComboBox,
+  EuiEmptyPrompt,
+  EuiFlexGrid,
+  EuiFlexGroup,
+  EuiFlexItem,
+  EuiPanel,
   EuiProgress,
   EuiSpacer,
+  EuiText,
   EuiTitle,
-  EuiPanel,
-  EuiFlexGroup,
-  EuiFlexItem,
-  EuiButtonIcon,
   EuiToolTip,
   htmlIdGenerator,
 } from '@elastic/eui';
@@ -98,6 +102,7 @@ const SearchMap = ({
   setSelectedPointsIds,
   setResourceFlyoutDataFromId,
   setIsResourceFlyoutOpen,
+  setSelectedTabNumber,
 }) => {
   const { t } = useTranslation('maps');
   // ref for handling zoomHelperText display
@@ -108,10 +113,10 @@ const SearchMap = ({
   // Selection tool mode: false = select points ; true = unselect points.
   const [selectionToolMode, setSelectionToolMode] = useState(false);
   const filterOptions = [
-    { label: t('maps:layersTable.queryResults'), value: 'ResRequete' },
-    { label: t('maps:layersTable.regions'), value: 'regions' },
-    { label: t('maps:layersTable.departments'), value: 'departments' },
-    { label: t('maps:layersTable.sylvoEcoRegions'), value: 'sylvoEcoRegions' },
+    { label: t('maps:mapTools.filters.queryResults'), value: 'ResRequete' },
+    { label: t('maps:mapTools.filters.regions'), value: 'regions' },
+    { label: t('maps:mapTools.filters.departments'), value: 'departments' },
+    { label: t('maps:mapTools.filters.sylvoEcoRegions'), value: 'sylvoEcoRegions' },
   ];
   const [selectedFilterOptions, setSelectedFilterOptions] = useState([filterOptions[0]]);
   const [zoomHelperTextFeature, setZoomHelperTextFeature] = useState(
@@ -282,7 +287,7 @@ const SearchMap = ({
     const zoomHelperTextStyle = new Style({
       text: new Text({
         font: '24px Arial',
-        text: t('maps:layersTable.zoomHelperText'),
+        text: t('maps:howTo.zoomHelperText'),
         fill: new Fill({
           color: 'white',
         }),
@@ -519,7 +524,7 @@ const SearchMap = ({
         const pointFeature = createPointFeature(
           {
             id: result.id,
-            name: result?.resource?.identifier,
+            name: result?.resource?.title,
           },
           proj.fromLonLat([geoPoint.longitude, geoPoint.latitude]),
           3500,
@@ -534,7 +539,7 @@ const SearchMap = ({
       const point = searchResults.find((result) => result.id === id);
       if (point) {
         const geoPoint = point.experimental_site.geo_point;
-        const pointName = point.resource.identifier;
+        const pointName = point.resource.title;
         if (geoPoint && geoPoint.longitude && geoPoint.latitude) {
           if (!getSelectedPointsNames(selectedPointsSource).includes(pointName)) {
             const pointFeature = createPointFeature(
@@ -677,23 +682,29 @@ const SearchMap = ({
     setSelectedPointsIds(newSelectedPointsIds);
   };
 
-  // Display selected points names
-  const SelectedPointsList = () => {
-    const SelectedPointItem = ({ id, name }) => {
-      const onUncheckButtonClick = () => {
-        unselectPoint(id);
-      };
-
-      const onOpenFlyoutClick = () => {
-        setResourceFlyoutDataFromId(id);
-        setIsResourceFlyoutOpen(true);
-      };
-
-      return (
-        <EuiPanel paddingSize="s" hasShadow={false} hasBorder={true}>
+  const SelectedPointItem = ({ id, name }) => {
+    const onUncheckButtonClick = () => {
+      unselectPoint(id);
+    };
+
+    const onOpenFlyoutClick = () => {
+      setResourceFlyoutDataFromId(id);
+      setIsResourceFlyoutOpen(true);
+    };
+
+    return (
+      <EuiFlexGroup alignItems={'center'}>
+        <EuiPanel
+          paddingSize="s"
+          hasShadow={false}
+          hasBorder
+          onClick={() => onOpenFlyoutClick()}
+        >
           <EuiFlexGroup alignItems={'center'}>
             <EuiFlexGroup>
-              <p>{name}</p>
+              <EuiText>
+                <p>{name}</p>
+              </EuiText>
             </EuiFlexGroup>
             <EuiToolTip
               position="top"
@@ -704,70 +715,87 @@ const SearchMap = ({
                 aria-label={t('maps:selectedPointsList.actions.openResourceFlyout')}
                 onClick={() => onOpenFlyoutClick()}
                 color={'primary'}
-                display={'base'}
-                size={'s'}
-              />
-            </EuiToolTip>
-            <EuiToolTip
-              position="top"
-              content={<p>{t('maps:selectedPointsList.actions.unselectResource')}</p>}
-            >
-              <EuiButtonIcon
-                iconType="cross"
-                aria-label={t('maps:selectedPointsList.actions.unselectResource')}
-                onClick={() => onUncheckButtonClick()}
-                color={'danger'}
                 size={'s'}
               />
             </EuiToolTip>
-            <EuiSpacer />
           </EuiFlexGroup>
         </EuiPanel>
-      );
-    };
-
-    const buildSelectedPointList = () => {
-      return selectedPoints.map((point, index) => {
-        return (
-          <SelectedPointItem key={index} name={point.nom} id={point.linkedPointId} />
-        );
-      });
-    };
+        <EuiToolTip
+          position="top"
+          content={<p>{t('maps:selectedPointsList.actions.unselectResource')}</p>}
+        >
+          <EuiButtonIcon
+            iconType="minusInCircle"
+            aria-label={t('maps:selectedPointsList.actions.unselectResource')}
+            onClick={() => onUncheckButtonClick()}
+            color={'danger'}
+            size={'s'}
+          />
+        </EuiToolTip>
+      </EuiFlexGroup>
+    );
+  };
 
+  // Display selected points names
+  const SelectedPointsList = () => {
     let selectedPointsList;
     let totalPoints = getPoints();
     let selectedPoints = getSelectedPoints();
     if (selectedPoints.length === 0) {
-      selectedPointsList = <p>{t('maps:selectedPointsList.empty')}</p>;
+      selectedPointsList = (
+        <EuiEmptyPrompt
+          iconType={'pagesSelect'}
+          title={<h2>{t('maps:selectedPointsList.emptyTitle')}</h2>}
+          titleSize={'s'}
+          body={<p>{t('maps:selectedPointsList.empty')}</p>}
+          color={'plain'}
+        />
+      );
     } else {
-      selectedPointsList = buildSelectedPointList();
+      selectedPointsList = (
+        <EuiPanel hasShadow={false} hasBorder={true}>
+          <EuiText textAlign={'center'}>
+            <EuiTitle size="s">
+              <p>
+                {t('maps:selectedPointsList.title')} ({selectedPoints.length} /{' '}
+                {totalPoints.length})
+              </p>
+            </EuiTitle>
+          </EuiText>
+          <EuiSpacer size={'l'} />
+          <EuiFlexItem>
+            <EuiFlexGrid
+              columns={1}
+              gutterSize={'m'}
+              direction={'column'}
+              style={styles.selectedPointsList}
+            >
+              {selectedPoints.map((point, index) => {
+                return (
+                  <SelectedPointItem
+                    key={index}
+                    name={point.nom}
+                    id={point.linkedPointId}
+                  />
+                );
+              })}
+            </EuiFlexGrid>
+          </EuiFlexItem>
+        </EuiPanel>
+      );
     }
 
-    return (
-      <EuiPanel hasShadow={false} hasBorder={true}>
-        <EuiFlexGroup direction={'column'}>
-          <EuiTitle size="s">
-            <p>
-              {t('maps:selectedPointsList.title')} ({selectedPoints.length} /{' '}
-              {totalPoints.length})
-            </p>
-          </EuiTitle>
-          <EuiFlexGroup direction={'column'} style={styles.selectedPointsList}>
-            {selectedPointsList}
-          </EuiFlexGroup>
-        </EuiFlexGroup>
-      </EuiPanel>
-    );
+    return <EuiFlexGroup direction={'column'}>{selectedPointsList}</EuiFlexGroup>;
   };
 
   const selectionToolOptions = [
     {
       id: 'selectionToolButton__0',
-      label: t('maps:layersTable.selectionTool.select'),
+      label: t('maps:mapTools.tools.selectionTool.select'),
     },
     {
       id: 'selectionToolButton__1',
-      label: t('maps:layersTable.selectionTool.unselect'),
+      label: t('maps:mapTools.tools.selectionTool.unselect'),
     },
   ];
 
@@ -779,97 +807,142 @@ const SearchMap = ({
     setSelectedPointsIds([]);
   };
 
-  const MapTools = () => {
+  const MapToolItem = ({ title, children }) => {
     return (
-      <EuiPanel paddingSize="l" hasShadow={false} hasBorder={true}>
-        <EuiFlexGroup>
-          <table style={styles.layersTable}>
-            <thead>
-              <tr>
-                <th>{t('maps:layersTableHeaders.cartography')}</th>
-                <th>{t('maps:layersTableHeaders.filters')}</th>
-                <th>{t('maps:layersTableHeaders.tools')}</th>
-              </tr>
-            </thead>
-            <tbody>
-              <tr>
-                <td style={styles.layersTableCells}>
-                  <EuiCheckbox
-                    id={htmlIdGenerator()()}
-                    label={t('maps:layersTable.openStreetMap')}
-                    checked={mapLayersVisibility[getLayerIndex('osm-layer')]}
-                    onChange={(e) => setLayerDisplay('osm-layer', e.target.checked)}
-                  />
-                  <EuiCheckbox
-                    id={htmlIdGenerator()()}
-                    label={t('maps:layersTable.bingAerial')}
-                    checked={mapLayersVisibility[getLayerIndex('Bing Aerial')]}
-                    onChange={(e) => setLayerDisplay('Bing Aerial', e.target.checked)}
-                  />
-                  <EuiCheckbox
-                    id={htmlIdGenerator()()}
-                    label={t('maps:layersTable.IGN')}
-                    checked={mapLayersVisibility[getLayerIndex('IGN')]}
-                    onChange={(e) => setLayerDisplay('IGN', e.target.checked)}
-                  />
-                </td>
-                <td style={styles.layersTableCells}>
-                  <EuiCheckbox
-                    id={htmlIdGenerator()()}
-                    label={t('maps:layersTable.queryResults')}
-                    checked={mapLayersVisibility[getLayerIndex('queryResults')]}
-                    onChange={(e) => setLayerDisplay('queryResults', e.target.checked)}
-                  />
-                  <br />
-                  <EuiComboBox
-                    aria-label={t('maps:layersTable.selectFilterOption')}
-                    placeholder={t('maps:layersTable.selectFilterOption')}
-                    singleSelection={{ asPlainText: true }}
-                    options={filterOptions}
-                    selectedOptions={selectedFilterOptions}
-                    onChange={onFilterSelectChange}
-                    styles={styles.filtersSelect}
-                  />
-                </td>
-                <td style={styles.layersTableCells}>
-                  <EuiTitle size="xxs">
-                    <h6>{t('maps:layersTable.selectionTool.title')}</h6>
-                  </EuiTitle>
-                  <EuiButtonGroup
-                    legend={t('maps:layersTable.selectionTool.title')}
-                    options={selectionToolOptions}
-                    onChange={toggleSelectionToolMode}
-                    idSelected={
-                      selectionToolMode
-                        ? 'selectionToolButton__1'
-                        : 'selectionToolButton__0'
-                    }
-                    color={'primary'}
-                    isFullWidth
-                  />
-                  <EuiSpacer size="s" />
-                  <EuiButton
-                    onClick={() => unselectPoints()}
-                    style={styles.unselectAllButton}
-                    color={'accent'}
-                    disabled={selectedPointsIds.length === 0}
-                  >
-                    {t('maps:layersTable.selectionTool.unselectAll')}
-                  </EuiButton>
-                </td>
-              </tr>
-            </tbody>
-          </table>
-        </EuiFlexGroup>
+      <EuiPanel paddingSize={'l'} hasShadow={false} hasBorder={true}>
+        <EuiText textAlign={'center'}>
+          <EuiTitle size={'xs'}>
+            <p>{title}</p>
+          </EuiTitle>
+        </EuiText>
+        <EuiSpacer size={'m'} />
+        <EuiFlexItem>{children}</EuiFlexItem>
       </EuiPanel>
     );
   };
 
+  const MapLayersHandler = () => {
+    return (
+      <MapToolItem title={t('maps:mapTools.layers.title')}>
+        <EuiCheckbox
+          id={htmlIdGenerator()()}
+          label={t('maps:mapTools.layers.openStreetMap')}
+          checked={mapLayersVisibility[getLayerIndex('osm-layer')]}
+          onChange={(e) => setLayerDisplay('osm-layer', e.target.checked)}
+        />
+        <EuiCheckbox
+          id={htmlIdGenerator()()}
+          label={t('maps:mapTools.layers.bingAerial')}
+          checked={mapLayersVisibility[getLayerIndex('Bing Aerial')]}
+          onChange={(e) => setLayerDisplay('Bing Aerial', e.target.checked)}
+        />
+        <EuiCheckbox
+          id={htmlIdGenerator()()}
+          label={t('maps:mapTools.layers.IGN')}
+          checked={mapLayersVisibility[getLayerIndex('IGN')]}
+          onChange={(e) => setLayerDisplay('IGN', e.target.checked)}
+        />
+      </MapToolItem>
+    );
+  };
+
+  const MapFiltersHandler = () => {
+    return (
+      <MapToolItem title={t('maps:mapTools.filters.title')}>
+        <EuiCheckbox
+          id={htmlIdGenerator()()}
+          label={t('maps:mapTools.filters.queryResults')}
+          checked={mapLayersVisibility[getLayerIndex('queryResults')]}
+          onChange={(e) => setLayerDisplay('queryResults', e.target.checked)}
+        />
+        <br />
+        <EuiComboBox
+          aria-label={t('maps:mapTools.filters.selectFilterOption')}
+          placeholder={t('maps:mapTools.filters.selectFilterOption')}
+          singleSelection={{ asPlainText: true }}
+          options={filterOptions}
+          selectedOptions={selectedFilterOptions}
+          onChange={onFilterSelectChange}
+          styles={styles.filtersSelect}
+        />
+      </MapToolItem>
+    );
+  };
+
+  const MapToolsHandler = () => {
+    return (
+      <MapToolItem title={t('maps:mapTools.tools.title')}>
+        <EuiText>
+          <EuiTitle size="xxs">
+            <h6>{t('maps:mapTools.tools.selectionTool.title')}</h6>
+          </EuiTitle>
+        </EuiText>
+        <EuiButtonGroup
+          legend={t('maps:mapTools.tools.selectionTool.title')}
+          options={selectionToolOptions}
+          onChange={toggleSelectionToolMode}
+          idSelected={
+            selectionToolMode ? 'selectionToolButton__1' : 'selectionToolButton__0'
+          }
+          color={'primary'}
+          isFullWidth
+        />
+        <EuiSpacer size="s" />
+        <EuiButton
+          onClick={() => unselectPoints()}
+          style={styles.unselectAllButton}
+          color={'danger'}
+          disabled={selectedPointsIds.length === 0}
+        >
+          {t('maps:mapTools.tools.selectionTool.unselectAll')}
+        </EuiButton>
+      </MapToolItem>
+    );
+  };
+
+  const MapTools = () => {
+    return (
+      <EuiFlexGrid columns={3}>
+        <MapLayersHandler />
+        <MapFiltersHandler />
+        <MapToolsHandler />
+      </EuiFlexGrid>
+    );
+  };
+
   return (
     <EuiFlexGroup>
       <EuiFlexGroup direction={'column'} style={styles.container}>
+        {(!searchResults || searchResults.length === 0) && (
+          <EuiFlexGroup alignItems={'center'}>
+            <EuiFlexItem>
+              <EuiCallOut
+                title={t('maps:noResults')}
+                color="warning"
+                iconType="warning"
+              />
+            </EuiFlexItem>
+            <EuiButton
+              onClick={() => {
+                setSelectedTabNumber(0);
+              }}
+              color="primary"
+              fill
+              key={0}
+              iconType={'pencil'}
+            >
+              {t('results:noResult.action')}
+            </EuiButton>
+          </EuiFlexGroup>
+        )}
         <EuiFlexItem>
-          <div id="map" style={styles.mapContainer}></div>
+          <EuiPanel
+            id="map"
+            style={styles.mapContainer}
+            paddingSize={'none'}
+            hasBorder
+            hasShadow={false}
+          />
           {isLoading && <EuiProgress size="l" color="accent" />}
         </EuiFlexItem>
         <MapTools />
diff --git a/src/pages/maps/styles.js b/src/pages/maps/styles.js
index edf2829c2238055e59ec7661723c04e08836f470..ae61fb15b8857d0516fb366a856367597e2ab5c8 100644
--- a/src/pages/maps/styles.js
+++ b/src/pages/maps/styles.js
@@ -32,14 +32,7 @@ const styles = {
   selectedPointsList: {
     overflow: 'auto',
     maxHeight: '72vh',
-  },
-  layersTable: {
-    width: '70vw',
-    cursor: 'pointer',
-    marginTop: '10px',
-  },
-  layersTableCells: {
-    padding: '10px',
+    paddingRight: '15px',
   },
   filtersSelect: {
     maxWidth: '50%',
diff --git a/src/pages/profile/GroupSettings.js b/src/pages/profile/GroupSettings.js
index 1eeb85bcb0ad9bc56ccd770bd0098fd504fafc04..90e2fe9e06cc274796587de436ed0e127ecd94f1 100644
--- a/src/pages/profile/GroupSettings.js
+++ b/src/pages/profile/GroupSettings.js
@@ -10,56 +10,50 @@ import {
   EuiFlexGroup,
   EuiSpacer,
 } from '@elastic/eui';
-import { getGroups, sendMail, createUserRequest } from '../../actions/user';
 import { useTranslation } from 'react-i18next';
-import styles from './styles';
 import { toast } from 'react-toastify';
 import ToastMessage from '../../components/ToastMessage/ToastMessage';
+import { useGatekeeper } from '../../contexts/GatekeeperContext';
+import { useUserInfo } from '../../contexts/TokenContext';
 
 const GroupSettings = ({ userGroups }) => {
+  const getUserInfo = useUserInfo();
+  const client = useGatekeeper();
   const { t } = useTranslation(['profile', 'common', 'validation']);
   const [groups, setGroups] = useState([]);
   const [selectedUserGroups, setSelectedUserGroups] = useState([]);
   const [valueError, setValueError] = useState(undefined);
 
   useEffect(() => {
-    setSelectedUserGroups(userGroups);
+    const getUserGroups = async () => {
+      if (!client) {
+        return;
+      }
+      const groupResults = await client.getGroups();
+      const mappedGroups = groupResults.map((group) => {
+        return {
+          id: group.id,
+          label: group.name,
+          description: group.description,
+          member: userGroups.some((userGroup) => userGroup.id === group.id) ? 'X' : '',
+        };
+      });
+      setGroups(mappedGroups);
+    };
     getUserGroups();
   }, []);
 
   const groupColumns = [
     { field: 'label', name: t('profile:groups.groupName'), width: '30%' },
     { field: 'description', name: t('profile:groups.groupDescription') },
+    {
+      field: 'member',
+      name: t('profile:groups.groupMember'),
+      width: '10%',
+      align: 'center',
+    },
   ];
 
-  const getUserGroups = () => {
-    getGroups().then((groupsResult) => {
-      const groupsArray = [];
-      groupsResult.forEach((group) => {
-        groupsArray.push({
-          id: group.id,
-          label: group.name,
-          description: group.description,
-        });
-      });
-      setGroups(groupsArray);
-    });
-  };
-
-  const getUserGroupLabels = (groups) => {
-    let labelList = '';
-    if (!groups || groups.length === 0) {
-      return labelList;
-    }
-    groups.forEach((group) => {
-      labelList = `${labelList} ${group.label},`;
-    });
-    if (labelList.endsWith(',')) {
-      labelList = labelList.substring(0, labelList.length - 1);
-    }
-    return labelList;
-  };
-
   const onValueSearchChange = (value, hasMatchingOptions) => {
     if (value.length !== 0 && !hasMatchingOptions) {
       setValueError(t('profile:groupRequests.invalidOption'));
@@ -67,13 +61,14 @@ const GroupSettings = ({ userGroups }) => {
   };
 
   const onSendGroupRequest = async () => {
+    const userInfo = await getUserInfo();
     if (!selectedUserGroups || selectedUserGroups.length === 0) {
       return;
     }
-    const groupList = getUserGroupLabels(selectedUserGroups);
-    const message = `The user ${sessionStorage.getItem('username')} (${sessionStorage.getItem('email')}) has made a request to be part of these groups : ${groupList}.`;
-    const result = await createUserRequest(sessionStorage.getItem('kcId'), message);
-    await sendMail('User group request', message);
+    const message = `The user ${userInfo.email} has made a request to be part of these groups : ${selectedUserGroups
+      .map((group) => group.label)
+      .join(', ')}.`;
+    const result = await client.createUserRequest(message, userInfo.sub);
     if (result.error) {
       toast.error(<ToastMessage title={t('validation:error')} message={result.error} />);
     } else {
@@ -82,61 +77,56 @@ const GroupSettings = ({ userGroups }) => {
   };
 
   return (
-    <EuiFlexGroup>
-      <EuiFlexItem>
-        <EuiPanel paddingSize="l" hasShadow={false} hasBorder={true}>
-          <EuiTitle size="s">
-            <h3>{t('profile:groups.groupsList')}</h3>
-          </EuiTitle>
-          <EuiSpacer size={'l'} />
-          <EuiBasicTable items={groups} columns={groupColumns} />
-        </EuiPanel>
-      </EuiFlexItem>
+    userGroups &&
+    userGroups.length > 0 && (
+      <EuiFlexGroup>
+        <EuiFlexItem>
+          <EuiPanel paddingSize="l" hasShadow={false} hasBorder={true}>
+            <EuiTitle size="s">
+              <h3>{t('profile:groups.groupsList')}</h3>
+            </EuiTitle>
+            <EuiSpacer size={'l'} />
+            <EuiBasicTable items={groups} columns={groupColumns} />
+          </EuiPanel>
+        </EuiFlexItem>
 
-      <EuiFlexItem>
-        <EuiPanel paddingSize="l" hasShadow={false} hasBorder={true}>
-          <EuiTitle size="s">
-            <h3>{t('profile:groupRequests.requestGroupAssignment')}</h3>
-          </EuiTitle>
-          <EuiSpacer size={'l'} />
-          {userGroups ? (
-            <p
-              style={styles.currentRoleOrGroupText}
-            >{`${t('profile:groupRequests.currentGroups')} ${getUserGroupLabels(userGroups)}`}</p>
-          ) : (
-            <p>{t('profile:groupRequests.noGroup')}</p>
-          )}
-          <EuiSpacer size={'l'} />
-          <EuiFormRow error={valueError} isInvalid={valueError !== undefined}>
-            <EuiComboBox
-              placeholder={t('profile:groupRequests.selectGroup')}
-              options={groups}
-              selectedOptions={selectedUserGroups}
-              onChange={(selectedOptions) => {
-                setValueError(undefined);
-                setSelectedUserGroups(selectedOptions);
-              }}
-              onSearchChange={onValueSearchChange}
-            />
-          </EuiFormRow>
-          <EuiSpacer size={'l'} />
-
-          <EuiFlexItem>
-            <div>
-              <EuiButton
-                disabled={selectedUserGroups.length === 0}
-                onClick={() => {
-                  onSendGroupRequest();
+        <EuiFlexItem>
+          <EuiPanel paddingSize="l" hasShadow={false} hasBorder={true}>
+            <EuiTitle size="s">
+              <h3>{t('profile:groupRequests.requestGroupAssignment')}</h3>
+            </EuiTitle>
+            <EuiSpacer size={'l'} />
+            <EuiFormRow error={valueError} isInvalid={valueError !== undefined}>
+              <EuiComboBox
+                placeholder={t('profile:groupRequests.selectGroup')}
+                options={groups.filter((group) => group.member != 'X')}
+                selectedOptions={selectedUserGroups}
+                onChange={(selectedOptions) => {
+                  setValueError(undefined);
+                  setSelectedUserGroups(selectedOptions);
                 }}
-                fill
-              >
-                {t('common:validationActions.send')}
-              </EuiButton>
-            </div>
-          </EuiFlexItem>
-        </EuiPanel>
-      </EuiFlexItem>
-    </EuiFlexGroup>
+                onSearchChange={onValueSearchChange}
+              />
+            </EuiFormRow>
+            <EuiSpacer size={'l'} />
+
+            <EuiFlexItem>
+              <div>
+                <EuiButton
+                  disabled={selectedUserGroups.length === 0}
+                  onClick={() => {
+                    onSendGroupRequest();
+                  }}
+                  fill
+                >
+                  {t('common:validationActions.send')}
+                </EuiButton>
+              </div>
+            </EuiFlexItem>
+          </EuiPanel>
+        </EuiFlexItem>
+      </EuiFlexGroup>
+    )
   );
 };
 
diff --git a/src/pages/profile/MyProfile.js b/src/pages/profile/MyProfile.js
index 57bf89a0ed383b4764682ab1a4a3e1d65d1a0b98..67bffe375d2ac3dc2f230f97d1fef12c41584c50 100644
--- a/src/pages/profile/MyProfile.js
+++ b/src/pages/profile/MyProfile.js
@@ -5,16 +5,18 @@ import {
   EuiButtonEmpty,
   EuiFlexGroup,
   EuiFlexItem,
+  EuiIconTip,
   EuiPanel,
   EuiSpacer,
+  EuiText,
   EuiTitle,
-  EuiIconTip,
 } from '@elastic/eui';
-import { deleteUserRequest } from '../../actions/user';
+import { useGatekeeper } from '../../contexts/GatekeeperContext';
 import { buildFieldName } from '../../Utils';
 import BulletPointList from '../../components/BulletPointList/BulletPointList';
 import { toast } from 'react-toastify';
 import ToastMessage from '../../components/ToastMessage/ToastMessage';
+import { useAuth } from 'oidc-react';
 
 const MyProfile = ({
   setSelectedTabNumber,
@@ -25,6 +27,8 @@ const MyProfile = ({
   publicFields,
   getUserRequests,
 }) => {
+  const client = useGatekeeper();
+  const auth = useAuth();
   const { t } = useTranslation(['profile']);
 
   const MyProfileCustomPanel = ({
@@ -70,9 +74,9 @@ const MyProfile = ({
   };
 
   const onDeleteRequest = async (request) => {
-    const result = await deleteUserRequest(request.id);
-    if (result.error) {
-      toast.error(<ToastMessage title={t('validation:error')} message={result.error} />);
+    const { success, message } = await client.deleteUserRequest(request.id);
+    if (!success) {
+      toast.error(<ToastMessage title={t('validation:error')} message={message} />);
     } else {
       toast.success(t('profile:requestsList.requestCanceled'));
       // Refresh requests table
@@ -114,17 +118,27 @@ const MyProfile = ({
 
   const GroupList = () => {
     if (!userGroups || userGroups.length === 0) {
-      return <p>{t('profile:groupRequests.noGroup')}</p>;
+      return (
+        <EuiText>
+          <p>{t('profile:groupRequests.noGroup')}</p>
+        </EuiText>
+      );
     }
-    const listItems = userGroups.map((group, index) => (
-      <li key={index}>{group.label}</li>
-    ));
-    return <BulletPointList>{listItems}</BulletPointList>;
+    const listItems = userGroups.map((group, index) => <li key={index}>{group.name}</li>);
+    return (
+      <EuiText>
+        <BulletPointList>{listItems}</BulletPointList>
+      </EuiText>
+    );
   };
 
   const FieldsDisplaySettings = () => {
     if (!fieldsDisplaySettingsIds || fieldsDisplaySettingsIds.length === 0) {
-      return <p>{t('profile:fieldsDisplaySettings.noSettings')}</p>;
+      return (
+        <EuiText>
+          <p>{t('profile:fieldsDisplaySettings.noSettings')}</p>
+        </EuiText>
+      );
     }
     const fieldsDisplaySettings = [];
     publicFields.forEach((field) => {
@@ -136,13 +150,17 @@ const MyProfile = ({
       <li key={index}>{fieldName}</li>
     ));
     // Have to style list manually because of display flex container
-    return <BulletPointList>{listItems}</BulletPointList>;
+    return (
+      <EuiText>
+        <BulletPointList>{listItems}</BulletPointList>
+      </EuiText>
+    );
   };
 
   return (
     <>
       <EuiTitle>
-        <h3>{sessionStorage.getItem('username')}</h3>
+        <h3>{auth.userData?.profile?.preferred_username || ''}</h3>
       </EuiTitle>
       <EuiFlexGroup>
         <MyProfileCustomPanel
@@ -159,7 +177,9 @@ const MyProfile = ({
           linkedTabButton={t('profile:myProfile.rolePanel.edit')}
           linkedTabNumber={2}
         >
-          <p>{userRole}</p>
+          <EuiText>
+            <p>{userRole}</p>
+          </EuiText>
         </MyProfileCustomPanel>
       </EuiFlexGroup>
 
@@ -171,7 +191,9 @@ const MyProfile = ({
           <EuiBasicTable items={userRequests} columns={requestsColumns} />
         ) : (
           <EuiFlexGroup justifyContent={'center'}>
-            <p>{t('profile:requestsList.noCurrentRequest')}</p>
+            <EuiText>
+              <p>{t('profile:requestsList.noCurrentRequest')}</p>
+            </EuiText>
           </EuiFlexGroup>
         )}
       </MyProfileCustomPanel>
@@ -190,7 +212,9 @@ const MyProfile = ({
           linkedTabButton={t('profile:myProfile.fieldsDownloadSettingsPanel.edit')}
           linkedTabNumber={3}
         >
-          <p>Fonctionnalité à venir prochainement.</p>
+          <EuiText>
+            <p>Fonctionnalité à venir prochainement.</p>
+          </EuiText>
         </MyProfileCustomPanel>
       </EuiFlexGroup>
     </>
diff --git a/src/pages/profile/Profile.js b/src/pages/profile/Profile.js
index 81c0d26781230fa0e930dee0143a956a9c7608f5..111b6c37b5cb3bd6a9f3e1a39ace881e06cabeca 100644
--- a/src/pages/profile/Profile.js
+++ b/src/pages/profile/Profile.js
@@ -5,14 +5,12 @@ import UserFieldsDisplaySettings from './UserFieldsDisplaySettings';
 import GroupSettings from './GroupSettings';
 import RoleSettings from './RoleSettings';
 import MyProfile from './MyProfile';
-import {
-  fetchUserFieldsDisplaySettings,
-  fetchUserRequests,
-  findOneUserWithGroupAndRole,
-} from '../../actions/user';
-import { fetchPublicFields } from '../../actions/source';
+import { useGatekeeper } from '../../contexts/GatekeeperContext';
+import { useUserInfo } from '../../contexts/TokenContext';
 
 const Profile = () => {
+  const getUserInfo = useUserInfo();
+  const client = useGatekeeper();
   const { t } = useTranslation(['profile', 'common', 'validation']);
   const [selectedTabNumber, setSelectedTabNumber] = useState(0);
   const [userGroups, setUserGroups] = useState([]);
@@ -21,56 +19,38 @@ const Profile = () => {
   const [fieldsDisplaySettings, setFieldsDisplaySettings] = useState(null);
   const [publicFields, setPublicFields] = useState([]);
 
-  useEffect(() => {
-    findOneUserWithGroupAndRole(sessionStorage.getItem('kcId')).then((result) => {
-      if (result) {
-        if (result[0]) {
-          setUserRole(result[0].rolename);
-        }
-        const userGroupList = [];
-        result.forEach((userGroup) => {
-          if (userGroup.groupname) {
-            userGroupList.push({
-              id: userGroup.groupid,
-              label: userGroup.groupname,
-              description: userGroup.groupdescription,
-            });
-          }
-        });
-        setUserGroups(userGroupList);
-      }
-    });
-    getUserRequests();
-    getUserFieldsDisplaySettings();
-    getPublicFields();
-  }, []);
-
-  const getUserRequests = () => {
-    fetchUserRequests(sessionStorage.getItem('kcId')).then((userRequests) => {
-      if (userRequests) {
-        setUserRequests([...userRequests]);
-      }
-    });
-  };
-
-  const getUserFieldsDisplaySettings = () => {
-    fetchUserFieldsDisplaySettings(sessionStorage.getItem('userId')).then(
-      (userSettings) => {
-        if (userSettings) {
-          setFieldsDisplaySettings(userSettings);
-        }
-      }
+  const fetchUser = async () => {
+    const { sub } = await getUserInfo();
+    const user = await client.findUserBySub(sub);
+    if (!user.id) {
+      return;
+    }
+    const { groups, roles, requests, display_fields } = user;
+    setUserGroups(
+      groups.map((group) => {
+        return {
+          id: group.group.id,
+          name: group.group.name,
+        };
+      })
     );
+    setUserRole(roles[0]?.role?.name);
+    setUserRequests(requests);
+    setFieldsDisplaySettings(display_fields.map((field) => field.std_field_id));
   };
 
-  const getPublicFields = () => {
-    fetchPublicFields().then((publicFieldsResults) => {
-      if (publicFieldsResults) {
-        setPublicFields(publicFieldsResults);
-      }
-    });
+  const fetchFields = async () => {
+    const fields = await client.getPublicFields();
+    setPublicFields(fields);
   };
 
+  useEffect(() => {
+    if (client && selectedTabNumber === 0) {
+      fetchUser();
+      fetchFields();
+    }
+  }, [selectedTabNumber]);
+
   const Tab = ({ children, description }) => {
     const [showCallOut, setShowCallOut] = useState(true);
 
@@ -113,7 +93,7 @@ const Profile = () => {
             userRequests={userRequests}
             fieldsDisplaySettingsIds={fieldsDisplaySettings}
             publicFields={publicFields}
-            getUserRequests={() => getUserRequests()}
+            getUserRequests={() => fetchUser()}
           />
         </Tab>
       ),
diff --git a/src/pages/profile/RoleSettings.js b/src/pages/profile/RoleSettings.js
index dc91ad226cf26c7556e27f8f798cfd28c33da83d..885b41547c65356a6834f49a0c5f225f0da83329 100644
--- a/src/pages/profile/RoleSettings.js
+++ b/src/pages/profile/RoleSettings.js
@@ -1,44 +1,45 @@
-import React, { useState, useEffect } from 'react';
+import React, { useEffect, useState } from 'react';
 import {
-  EuiTitle,
-  EuiSelect,
   EuiButton,
-  EuiFormRow,
+  EuiFlexGroup,
   EuiFlexItem,
+  EuiFormRow,
   EuiPanel,
+  EuiSelect,
   EuiSpacer,
-  EuiFlexGroup,
+  EuiText,
+  EuiTitle,
 } from '@elastic/eui';
-import { getRoles, sendMail, createUserRequest } from '../../actions/user';
 import { useTranslation } from 'react-i18next';
 import styles from './styles';
 import { toast } from 'react-toastify';
 import ToastMessage from '../../components/ToastMessage/ToastMessage';
+import { useGatekeeper } from '../../contexts/GatekeeperContext';
+import { useUserInfo } from '../../contexts/TokenContext';
 
 const RoleSettings = ({ userRole }) => {
+  const client = useGatekeeper();
+  const getUserInfo = useUserInfo();
   const { t } = useTranslation(['profile', 'common', 'validation']);
   const [roles, setRoles] = useState([]);
   const [selectedRole, setSelectedRole] = useState(undefined);
 
   useEffect(() => {
-    getUserRoles();
-  }, []);
-
-  const getUserRoles = () => {
-    getRoles().then((rolesResult) => {
-      const rolesArray = [];
-      rolesResult.forEach((role) => {
-        rolesArray.push({ id: role.id, text: role.name });
-      });
-      setRoles(rolesArray);
-    });
-  };
+    const fetchRoles = async () => {
+      const roles = await client.getRoles();
+      const filteredRoles = roles
+        .filter((role) => role.name !== userRole)
+        .map((role) => ({ value: role.name, text: role.name }));
+      setRoles(filteredRoles);
+    };
+    fetchRoles();
+  }, [client]);
 
   const onSendRoleRequest = async () => {
+    const { email, sub } = await getUserInfo();
     if (selectedRole) {
-      const message = `The user ${sessionStorage.getItem('username')} (${sessionStorage.getItem('email')}) has made a request to get the role : ${selectedRole}.`;
-      const result = await createUserRequest(sessionStorage.getItem('kcId'), message);
-      await sendMail('User role request', message);
+      const message = `The user ${email} has made a request to get the role : ${selectedRole}.`;
+      const result = await client.createUserRequest(message, sub);
       if (result.error) {
         toast.error(
           <ToastMessage title={t('validation:error')} message={result.error} />
@@ -58,9 +59,11 @@ const RoleSettings = ({ userRole }) => {
           </EuiTitle>
           <EuiSpacer size={'l'} />
           {userRole && (
-            <p style={styles.currentRoleOrGroupText}>
-              {`${t('profile:roleRequests.currentRole')} ${userRole}`}
-            </p>
+            <EuiText>
+              <p style={styles.currentRoleOrGroupText}>
+                {`${t('profile:roleRequests.currentRole')} ${userRole}`}
+              </p>
+            </EuiText>
           )}
           <EuiSpacer size={'l'} />
           <EuiFormRow>
@@ -74,7 +77,7 @@ const RoleSettings = ({ userRole }) => {
             />
           </EuiFormRow>
           <EuiSpacer size={'l'} />
-          <EuiButton onClick={() => onSendRoleRequest()} fill>
+          <EuiButton onClick={() => onSendRoleRequest()} fill disabled={!selectedRole}>
             {t('common:validationActions.send')}
           </EuiButton>
         </EuiPanel>
diff --git a/src/pages/profile/UserFieldsDisplaySettings.js b/src/pages/profile/UserFieldsDisplaySettings.js
index 2984427619b8b2f00ea6c6664cfa872543663f32..8859a3e1cc7b660984795a25e370872a5e1c0e94 100644
--- a/src/pages/profile/UserFieldsDisplaySettings.js
+++ b/src/pages/profile/UserFieldsDisplaySettings.js
@@ -1,29 +1,29 @@
 import React, { useEffect, useState } from 'react';
 import {
-  EuiSpacer,
-  EuiSelectable,
   EuiButton,
-  EuiFlexItem,
   EuiFlexGroup,
+  EuiFlexItem,
   EuiPanel,
+  EuiSelectable,
+  EuiSpacer,
+  EuiText,
   EuiTitle,
 } from '@elastic/eui';
-import {
-  createUserFieldsDisplaySettings,
-  deleteUserFieldsDisplaySettings,
-  updateUserFieldsDisplaySettings,
-} from '../../actions/user';
 import { Trans, useTranslation } from 'react-i18next';
 import { buildFieldName } from '../../Utils';
 import BulletPointList from '../../components/BulletPointList/BulletPointList';
 import { toast } from 'react-toastify';
 import ToastMessage from '../../components/ToastMessage/ToastMessage';
+import { useGatekeeper } from '../../contexts/GatekeeperContext';
+import { useUserInfo } from '../../contexts/TokenContext';
 
 /*
   User fields display settings are used to choose which fields are displayed in results table after a search.
   If the user has no settings, the default are used. Default settings are the same for all users, chosen by admin at standard setup.
  */
 const UserFieldsDisplaySettings = ({ userSettings, setUserSettings, publicFields }) => {
+  const client = useGatekeeper();
+  const getUserInfo = useUserInfo();
   const { t } = useTranslation(['profile', 'common', 'validation']);
   const [settingsOptions, setSettingsOptions] = useState([]);
   const [selectedOptionsIds, setSelectedOptionsIds] = useState([]);
@@ -87,22 +87,14 @@ const UserFieldsDisplaySettings = ({ userSettings, setUserSettings, publicFields
     if (!selectedOptionsIds || selectedOptionsIds.length === 0) {
       return;
     }
-    let result;
-    if (userSettings) {
-      result = await updateUserFieldsDisplaySettings(
-        sessionStorage.getItem('userId'),
-        selectedOptionsIds
+    const { sub } = await getUserInfo();
+    const response = await client.setUserFieldsDisplaySettings(sub, selectedOptionsIds);
+    if (response && response.length === 0) {
+      toast.error(
+        <ToastMessage title={t('validation:error')} message={response.error} />
       );
     } else {
-      result = await createUserFieldsDisplaySettings(
-        sessionStorage.getItem('userId'),
-        selectedOptionsIds
-      );
-    }
-    setUserSettings(selectedOptionsIds);
-    if (result.error) {
-      toast.error(<ToastMessage title={t('validation:error')} message={result.error} />);
-    } else {
+      setUserSettings(selectedOptionsIds);
       toast.success(t('profile:fieldsDisplaySettings.updatedSettingsSuccess'));
     }
   };
@@ -126,45 +118,38 @@ const UserFieldsDisplaySettings = ({ userSettings, setUserSettings, publicFields
   // With current logic, no user settings means it should use default.
   // So in this case 'reset' means delete current user settings.
   const onSettingsReset = async () => {
-    // TODO add a confirmation modal ?
-    const result = await deleteUserFieldsDisplaySettings(
-      sessionStorage.getItem('userId')
-    );
-    if (result.error) {
-      toast.error(<ToastMessage title={t('validation:error')} message={result.error} />);
-    } else {
-      setUserSettings(null);
-      toast.success(
-        <ToastMessage
-          title={t('profile:fieldsDisplaySettings.deleteSettingsSuccess')}
-          message={t('profile:fieldsDisplaySettings.deleteSettingsSuccessDefault')}
-        />
-      );
+    if (!settingsOptions) {
+      toast.error(t('profile:fieldsDisplaySettings.selectionResetFailure'));
+      return;
     }
+    const newSettingsOptions = [];
+    settingsOptions.forEach((option) => {
+      option.checked = false;
+      newSettingsOptions.push(option);
+    });
+    setSettingsOptions(newSettingsOptions);
   };
 
   const SelectableSettingsPanel = () => {
     return (
       <EuiFlexItem>
-        <EuiPanel paddingSize="l" hasShadow={false} hasBorder={true}>
-          <EuiSelectable
-            aria-label={t('profile:fieldsDisplaySettings.selectedOptionsLabel')}
-            options={settingsOptions}
-            onChange={(newOptions) => setSettingsOptions(newOptions)}
-            searchable
-            listProps={{ bordered: true }}
-            style={{ minHeight: '65vh' }}
-            height={'full'}
-          >
-            {(list, search) => (
-              <>
-                {search}
-                <EuiSpacer size={'xs'} />
-                {list}
-              </>
-            )}
-          </EuiSelectable>
-        </EuiPanel>
+        <EuiSelectable
+          aria-label={t('profile:fieldsDisplaySettings.selectedOptionsLabel')}
+          options={settingsOptions}
+          onChange={(newOptions) => setSettingsOptions(newOptions)}
+          searchable
+          listProps={{ bordered: true }}
+          style={{ minHeight: '65vh' }}
+          height={'full'}
+        >
+          {(list, search) => (
+            <>
+              {search}
+              <EuiSpacer size={'xs'} />
+              {list}
+            </>
+          )}
+        </EuiSelectable>
       </EuiFlexItem>
     );
   };
@@ -172,11 +157,13 @@ const UserFieldsDisplaySettings = ({ userSettings, setUserSettings, publicFields
   const SelectedSettingsPanel = () => {
     const SelectedSettingsList = () => {
       return (
-        <BulletPointList>
-          {selectedOptions.map((option, index) => {
-            return <li key={index}>{option}</li>;
-          })}
-        </BulletPointList>
+        <EuiText>
+          <BulletPointList>
+            {selectedOptions.map((option, index) => {
+              return <li key={index}>{option}</li>;
+            })}
+          </BulletPointList>
+        </EuiText>
       );
     };
 
diff --git a/src/pages/results/ResourceFlyout/JSON2MD.js b/src/pages/results/ResourceFlyout/JSON2MD.js
index 1c25fdb2b04a3e3827b8e56d8b9efb4cadb6581a..00c85ff1511989fae3f65f85498debf6f90bc1cd 100644
--- a/src/pages/results/ResourceFlyout/JSON2MD.js
+++ b/src/pages/results/ResourceFlyout/JSON2MD.js
@@ -2,16 +2,7 @@ import standard_markdown_styles from './standard_markdown_styles_FR_EN.json';
 
 const DEFAULT_ARRAY_SEPARATOR = ',';
 
-const handleString = (
-  key,
-  value,
-  styles,
-  lang,
-  indent = '',
-  level,
-  labelType,
-  rejectNull
-) => {
+const handleString = (key, value, styles, lang, level, labelType, rejectNull) => {
   const indentLocal = ' '.repeat((level - 1) * 2);
   const style = styles[key]
     ? styles[key][lang]
@@ -29,16 +20,7 @@ const handleString = (
   }
 };
 
-const handleObject = (
-  key,
-  value,
-  styles,
-  lang,
-  indent = '',
-  level,
-  labelType,
-  rejectNull
-) => {
+const handleObject = (key, value, styles, lang, level, labelType, rejectNull) => {
   const style = styles[key]
     ? styles[key][lang]
     : { value_style: '**', title_style: '', label: key, definition: '' };
@@ -224,7 +206,7 @@ const processKeyValue = (
     case 'bigint':
       break;
     case 'string':
-      return handleString(key, value, styles, lang, indent, level, labelType, rejectNull);
+      return handleString(key, value, styles, lang, level, labelType, rejectNull);
     case 'object':
       if (Array.isArray(value)) {
         if (value.length > 0 && typeof value[0] === 'object') {
@@ -251,16 +233,7 @@ const processKeyValue = (
           );
         }
       } else if (value !== null) {
-        return handleObject(
-          key,
-          value,
-          styles,
-          lang,
-          indent,
-          level,
-          labelType,
-          rejectNull
-        );
+        return handleObject(key, value, styles, lang, level, labelType, rejectNull);
       } else if ((typeof value !== 'undefined' && false && value !== '') || !rejectNull) {
         return `\n${indentLocal}- ${localLabel} = `;
       } else {
diff --git a/src/pages/results/ResourceFlyout/ResourceJSON.js b/src/pages/results/ResourceFlyout/ResourceJSON.js
index bd12c0bae9ea2b66ddf046f0b17eee2a947cc717..a6e7b4ad6b3c00730ca2b80eea1ecc159111070f 100644
--- a/src/pages/results/ResourceFlyout/ResourceJSON.js
+++ b/src/pages/results/ResourceFlyout/ResourceJSON.js
@@ -16,7 +16,7 @@ const ResourceJSON = ({ resourceData }) => {
         displayArrayKey={false}
         displayDataTypes={false}
         displayObjectSize={false}
-        collapsed={false}
+        collapsed={true}
       />
     </EuiText>
   );
diff --git a/src/pages/results/ResourceFlyout/ResourceMarkdown.js b/src/pages/results/ResourceFlyout/ResourceMarkdown.js
index b7e2fb9670adfa47438fbeb4b08113c86ec6256f..4a8558d93d361f79f8653c521fb9ca4764867c54 100644
--- a/src/pages/results/ResourceFlyout/ResourceMarkdown.js
+++ b/src/pages/results/ResourceFlyout/ResourceMarkdown.js
@@ -1,19 +1,34 @@
-import React from 'react';
-import { EuiText } from '@elastic/eui';
+import React, { useEffect, useState } from 'react';
+import { EuiEmptyPrompt, EuiLoadingSpinner, EuiText } from '@elastic/eui';
 import { JSON2MD } from './JSON2MD';
 import { useTranslation } from 'react-i18next';
 import Markdown from 'react-markdown';
 
 const ResourceMarkdown = ({ resourceData }) => {
-  const { i18n } = useTranslation();
+  const { t, i18n } = useTranslation('results');
+  const [isLoading, setIsLoading] = useState(true);
+  const [markdownString, setMarkdownString] = useState('');
 
-  const buildMarkdown = () => {
-    return JSON2MD(resourceData, i18n.language, 'HT', true);
-  };
+  useEffect(() => {
+    const buildMarkdown = () => {
+      return JSON2MD(resourceData, i18n.language, 'HT', true);
+    };
+    setMarkdownString(buildMarkdown());
+  }, []);
 
-  const markdownString = buildMarkdown();
+  useEffect(() => {
+    if (markdownString !== '') {
+      setIsLoading(false);
+    }
+  }, [markdownString]);
 
-  return (
+  return isLoading ? (
+    <EuiEmptyPrompt
+      title={<h2>{t('results:flyout.loading')}</h2>}
+      icon={<EuiLoadingSpinner size="xxl" />}
+      titleSize={'s'}
+    />
+  ) : (
     <EuiText size="s">
       <Markdown>{markdownString}</Markdown>
     </EuiText>
diff --git a/src/pages/results/Results.js b/src/pages/results/Results.js
index 5ebb6f4023a11f59e78d17148276f5be8dc5935a..5171e2160f1a6a0d7b98293e6384ef27815373fc 100644
--- a/src/pages/results/Results.js
+++ b/src/pages/results/Results.js
@@ -1,8 +1,9 @@
 import React from 'react';
-import { EuiCallOut, EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui';
+import { EuiCallOut, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
 import { useTranslation } from 'react-i18next';
 import ResultsTableMUI from './ResultsTableMUI';
 import ResultsDownload from './ResultsDownload';
+import NoResultEmptyPrompt from '../../components/NoResultEmptyPrompt/NoResultEmptyPrompt';
 
 const Results = ({
   searchResults,
@@ -11,9 +12,14 @@ const Results = ({
   setSelectedRowsIds,
   setResourceFlyoutDataFromId,
   setIsResourceFlyoutOpen,
+  setSelectedTabNumber,
 }) => {
   const { t } = useTranslation('results');
 
+  if (!searchResults || searchResults.length === 0) {
+    return <NoResultEmptyPrompt setSelectedTabNumber={setSelectedTabNumber} />;
+  }
+
   return (
     <>
       <EuiFlexGroup>
@@ -28,7 +34,6 @@ const Results = ({
           />
         </EuiFlexItem>
       </EuiFlexGroup>
-      <EuiSpacer size={'m'} />
       <ResultsTableMUI
         searchResults={searchResults}
         searchQuery={searchQuery}
diff --git a/src/pages/results/ResultsTableMUI.js b/src/pages/results/ResultsTableMUI.js
index 1a7b22b8d70ea9331a31eaa71a8c32210272873d..fcba67efefdc40c23c93ee75ebe95076c954c6c5 100644
--- a/src/pages/results/ResultsTableMUI.js
+++ b/src/pages/results/ResultsTableMUI.js
@@ -1,25 +1,11 @@
-import React, { useEffect, useMemo, useState } from 'react';
+import React, { useEffect, useState } from 'react';
 import { Trans, useTranslation } from 'react-i18next';
 import MUIDataTable from 'mui-datatables';
 import { createTheme, ThemeProvider } from '@mui/material';
-import { fetchPublicFields } from '../../actions/source';
-import { fetchUserFieldsDisplaySettings } from '../../actions/user';
 import { buildFieldName } from '../../Utils';
-
-const getMuiTheme = () =>
-  createTheme({
-    components: {
-      MuiTableRow: {
-        styleOverrides: {
-          root: {
-            '&:hover': {
-              cursor: 'pointer',
-            },
-          },
-        },
-      },
-    },
-  });
+import { useGatekeeper } from '../../contexts/GatekeeperContext';
+import { useUserInfo } from '../../contexts/TokenContext';
+import { EuiText, EuiTitle, transparentize, useEuiTheme } from '@elastic/eui';
 
 const ResultsTableMUI = ({
   searchResults,
@@ -30,65 +16,126 @@ const ResultsTableMUI = ({
   setResourceFlyoutDataFromId,
 }) => {
   const { t } = useTranslation('results');
+  const client = useGatekeeper();
+  const getUserInfo = useUserInfo();
+  const { euiTheme } = useEuiTheme();
   const [publicFields, setPublicFields] = useState([]);
-  const [userFieldsIds, setUserFieldsIds] = useState([]);
   const [isLoading, setIsLoading] = useState(true);
   const [rowsPerPage, setRowsPerPage] = useState(15);
+  const [rows, setRows] = useState([]);
+  const [columns, setColumns] = useState([]);
 
-  // Fetch public fields and user display settings
   useEffect(() => {
-    const fetchData = async () => {
-      setIsLoading(true);
-      const publicFieldsResults = await fetchPublicFields();
-      setPublicFields(publicFieldsResults);
-      const userStdFieldsIds = await fetchUserFieldsDisplaySettings(
-        sessionStorage.getItem('userId')
-      );
-      const defaultStdFieldsIds = [1, 20, 36, 9, 31, 13];
-      // TODO replace hard-coded array by gatekeeper fetch on default settings
-      // If no userStdFields, use system default ones.
-      setUserFieldsIds(userStdFieldsIds || defaultStdFieldsIds);
+    const fetchFields = async () => {
+      const publicFields = await client.getPublicFields();
+      setPublicFields(publicFields);
     };
-    fetchData();
+    if (!searchResults || searchResults.length === 0 || searchResults.error) {
+      return;
+    }
+    fetchFields();
   }, [searchResults]);
 
-  // Memoize columns
-  const columns = useMemo(() => {
-    if (publicFields.length === 0) {
-      return [];
+  useEffect(() => {
+    const getUserFieldsDisplaySettings = async () => {
+      // TODO: get also private fields that user has access to
+      const userInfo = await getUserInfo();
+      const userFields = await client.getUserFieldsDisplaySettings(userInfo.sub);
+      const defaultFields = await client.getDefaultFieldsDisplaySettings();
+      if (userFields && userFields.length > 0) {
+        const newColumns = await buildColumns(userFields);
+        setColumns(newColumns);
+        const newRows = buildRows(searchResults, newColumns);
+        setRows(newRows);
+        setIsLoading(false);
+      } else {
+        const newColumns = await buildColumns(defaultFields);
+        setColumns(newColumns);
+        const newRows = buildRows(searchResults, newColumns);
+        setRows(newRows);
+        setIsLoading(false);
+      }
+    };
+    if (!searchResults || searchResults.length === 0 || searchResults.error) {
+      return;
     }
-    let dataColumns = [
+    getUserFieldsDisplaySettings();
+  }, [publicFields]);
+
+  const createFieldColumn = (fieldName, isDisplayed) => {
+    return {
+      name: fieldName,
+      label: buildFieldName(fieldName),
+      options: {
+        display: isDisplayed,
+        // Apply styling on columns headers
+        customHeadLabelRender: (columnMeta) => {
+          // Apply styling on columns headers text
+          return (
+            <EuiTitle size={'xxs'}>
+              <p>{columnMeta.label}</p>
+            </EuiTitle>
+          );
+        },
+        customBodyRender: (value) => {
+          return (
+            <span
+              style={{
+                display: 'inline-block',
+                overflow: 'hidden',
+                textOverflow: 'ellipsis',
+                whiteSpace: 'nowrap',
+                maxWidth: '100%',
+              }}
+            >
+              {value}
+            </span>
+          );
+        },
+        setCellProps: () => ({
+          style: {
+            maxWidth: '250px',
+          },
+        }),
+      },
+    };
+  };
+
+  const buildColumns = async (userFields) => {
+    let columns = [
       {
         name: 'id',
         label: 'ID',
         options: { display: 'excluded' },
       },
     ];
-    publicFields.forEach((publicField) => {
-      dataColumns.push({
-        name: publicField.field_name,
-        label: buildFieldName(publicField.field_name),
-        options: {
-          display: userFieldsIds.includes(publicField.id),
-          customBodyRenderLite: (dataIndex, rowIndex) => {
-            let value = rows[rowIndex][publicField.field_name];
-            if (value && value.length >= 150) {
-              value = value.substring(0, 150) + ' ...';
-            }
-            return value;
-          },
-          setCellProps: () => ({
-            style: {
-              maxWidth: '350px',
-            },
-          }),
-        },
+    publicFields.forEach((field) => {
+      // Create columns for all public fields with display set to false.
+      // This way they can be used in table options (checkbox)
+      columns.push(createFieldColumn(field.field_name, false));
+    });
+    userFields.forEach((field) => {
+      // Set columns display to true according to user choices (or default ones).
+      const index = columns.findIndex(
+        (columnItem) => columnItem.name === field.field_name
+      );
+      if (index !== -1) {
+        columns[index].options.display = true;
+      }
+    });
+    return columns;
+  };
+
+  const buildRows = (results, columns) => {
+    return results.map((result) => {
+      let row = { id: result.id };
+      columns.forEach((column) => {
+        const value = getValueByPath(result, column.name);
+        row[column.name] = typeof value === 'string' ? value : value?.toString();
       });
+      return row;
     });
-    // sort columns alphabetically for clarity
-    dataColumns.sort((a, b) => (a.name > b.name ? 1 : -1));
-    return dataColumns;
-  }, [publicFields, userFieldsIds]);
+  };
 
   // Returns value from JSON obj associated to key string.
   const getValueByPath = (obj, path) => {
@@ -102,49 +149,9 @@ const ResultsTableMUI = ({
     }, obj);
   };
 
-  const rows = useMemo(() => {
-    const buildRows = (results, columns) => {
-      if (results.length === 0) {
-        return [];
-      }
-      return results.map((result) => {
-        let row = { id: result.id };
-        columns.forEach((column) => {
-          const value = getValueByPath(result, column.name);
-          row[column.name] = typeof value === 'string' ? value : value?.toString();
-        });
-        return row;
-      });
-    };
-    const rows = buildRows(searchResults, columns);
-    setIsLoading(false);
-    return searchResults && columns.length > 0 ? rows : [];
-  }, [searchResults, columns]);
-
-  const getRowIdFromResourceData = (id) => {
-    if (!rows || rows.length === 0) {
-      return -1;
-    }
-    for (let index = 0; index < rows.length; index++) {
-      if (rows[index].id === id) {
-        return index;
-      }
-    }
-    return -1;
-  };
-
-  // On page load, check table rows from selected resources from map
-  const selectedRows = useMemo(() => {
-    return selectedRowsIds.map((id) => getRowIdFromResourceData(id));
-  }, [rows, selectedRowsIds]);
-
   // Add row to list of selected on checkbox click
-  const onRowSelectionCallback = (selectedRow, allSelectedRows) => {
-    setSelectedRowsIds(
-      allSelectedRows.map((row) => {
-        return rows[row.dataIndex].id;
-      })
-    );
+  const onRowSelectionCallback = (currentRowsSelected, allRowsSelected, rowsSelected) => {
+    setSelectedRowsIds(rowsSelected.map((index) => rows[index].id));
   };
 
   // Open resource flyout on row click (any cell)
@@ -193,6 +200,30 @@ const ResultsTableMUI = ({
     },
   };
 
+  const getMuiTheme = () =>
+    createTheme({
+      components: {
+        MUIDataTableBodyRow: {
+          styleOverrides: {
+            root: {
+              '&:nth-of-type(odd)': {
+                backgroundColor: transparentize(euiTheme.colors.lightShade, 0.5),
+              },
+            },
+          },
+        },
+        MuiTableRow: {
+          styleOverrides: {
+            root: {
+              '&:hover': {
+                cursor: 'pointer',
+              },
+            },
+          },
+        },
+      },
+    });
+
   const tableOptions = {
     print: false,
     download: false,
@@ -205,7 +236,9 @@ const ResultsTableMUI = ({
     },
     selectableRows: 'multiple',
     selectableRowsOnClick: false,
-    rowsSelected: selectedRows,
+    rowsSelected: rows
+      .filter((row) => selectedRowsIds.includes(row.id))
+      .map((row) => rows.indexOf(row)),
     rowsPerPage: rowsPerPage,
     onChangeRowsPerPage: (newRowsPerPage) => {
       setRowsPerPage(newRowsPerPage);
@@ -213,7 +246,7 @@ const ResultsTableMUI = ({
     rowsPerPageOptions: [15, 30, 50, 100, 250],
     jumpToPage: true,
     searchPlaceholder: t('results:table.search'),
-    elevation: 0, // remove the boxShadow style
+    elevation: 0,
     customToolbarSelect: () => <CustomSelectToolbar />,
     selectToolbarPlacement: 'above',
     onRowSelectionChange: onRowSelectionCallback,
@@ -222,12 +255,21 @@ const ResultsTableMUI = ({
   };
 
   const isTableReady = !isLoading && columns.length > 0 && rows.length > 0;
+  const count = searchResults.length;
 
   return (
     <ThemeProvider theme={getMuiTheme()}>
       {isTableReady && (
         <MUIDataTable
-          title={<Trans i18nKey={'results:table.title'} components={{ searchQuery }} />}
+          title={
+            <EuiText>
+              <Trans
+                i18nKey={'results:table.title'}
+                components={{ searchQuery }}
+                count={count}
+              />
+            </EuiText>
+          }
           data={rows}
           columns={columns}
           options={tableOptions}
diff --git a/src/pages/search/AdvancedSearch/AdvancedSearch.js b/src/pages/search/AdvancedSearch/AdvancedSearch.js
index 53f5ad4fe2db2bf1753770e92b0778dbe031618a..025873b89095fd65750fecd26783b53c94f128a7 100644
--- a/src/pages/search/AdvancedSearch/AdvancedSearch.js
+++ b/src/pages/search/AdvancedSearch/AdvancedSearch.js
@@ -3,6 +3,7 @@ import {
   EuiButtonEmpty,
   EuiButtonIcon,
   EuiComboBox,
+  EuiDatePicker,
   EuiFieldText,
   EuiFlexGroup,
   EuiFlexItem,
@@ -24,15 +25,15 @@ import {
   EuiSelect,
   EuiSpacer,
   EuiSwitch,
+  EuiText,
   EuiTextArea,
   EuiTextColor,
   EuiTitle,
-  EuiDatePicker,
 } from '@elastic/eui';
 import React, { Fragment, useEffect, useState } from 'react';
 import {
+  buildDslQuery,
   changeNameToLabel,
-  createAdvancedQueriesBySource,
   getFieldsBySection,
   getSections,
   removeArrayElement,
@@ -40,15 +41,15 @@ import {
   updateArrayElement,
   updateSearchFieldValues,
 } from '../../../Utils';
-import { getQueryCount, searchQuery } from '../../../actions/source';
 import { DateOptions, NumericOptions, Operators } from '../Data';
-import { addUserHistory, fetchUserHistory } from '../../../actions/user';
 import { useTranslation } from 'react-i18next';
 import styles from './styles.js';
 import moment from 'moment';
 import SearchModeSwitcher from '../SearchModeSwitcher';
 import { toast } from 'react-toastify';
 import ToastMessage from '../../../components/ToastMessage/ToastMessage';
+import { useGatekeeper } from '../../../contexts/GatekeeperContext';
+import { useAuth } from 'oidc-react';
 
 const updateSources = (
   searchFields,
@@ -69,7 +70,7 @@ const updateSources = (
         availableSources = updatedSources;
       }
       updatedSources = [];
-      field.sources.forEach((sourceId) => {
+      field.sources?.forEach((sourceId) => {
         noPrivateField = false;
         const source = availableSources.find((src) => src.id === sourceId);
         if (source && !updatedSources.includes(source)) {
@@ -130,18 +131,6 @@ const fieldValuesToString = (field) => {
   return strValues;
 };
 
-const fetchHistory = (setUserHistory) => {
-  fetchUserHistory(sessionStorage.getItem('kcId')).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) => {
@@ -172,12 +161,27 @@ const HistorySelect = ({
   setUserHistory,
 }) => {
   const { t } = useTranslation('search');
+  const auth = useAuth();
+  const client = useGatekeeper();
   const [historySelectError, setHistorySelectError] = useState(undefined);
   const [selectedSavedSearch, setSelectedSavedSearch] = useState(undefined);
 
   useEffect(() => {
-    fetchHistory(setUserHistory);
-  }, [setUserHistory]);
+    const fetchHistory = async (sub) => {
+      const userHistory = await client.getUserHistory(sub);
+      if (userHistory[0] && userHistory[0].ui_structure) {
+        userHistory.forEach((item) => {
+          item.ui_structure = JSON.parse(item.ui_structure);
+          item.label = `${item.name} - ${new Date(item.createdat).toLocaleString()}`;
+        });
+        setUserHistory(userHistory);
+      }
+    };
+    const sub = auth.userData?.profile?.sub;
+    if (sub) {
+      fetchHistory(sub);
+    }
+  }, [auth.userData]);
 
   const onHistoryChange = (selectedSavedSearch) => {
     setHistorySelectError(undefined);
@@ -194,13 +198,13 @@ const HistorySelect = ({
       setSearchFields([]);
       return;
     }
-    if (!!selectedSavedSearch[0].query) {
+    if (selectedSavedSearch[0].query) {
       setSelectedSavedSearch(selectedSavedSearch);
       setSearch(selectedSavedSearch[0].query);
       setSearchCount();
       setFieldCount([]);
     }
-    if (!!selectedSavedSearch[0].ui_structure) {
+    if (selectedSavedSearch[0].ui_structure) {
       updateSources(
         selectedSavedSearch[0].ui_structure,
         sources,
@@ -246,7 +250,6 @@ const SearchBar = ({
   setSearchResults,
   searchFields,
   setSearchFields,
-  selectedSources,
   setSelectedSources,
   availableSources,
   setAvailableSources,
@@ -262,69 +265,82 @@ const SearchBar = ({
   const [userHistory, setUserHistory] = useState({});
   const [isSaveSearchModalOpen, setIsSaveSearchModalOpen] = useState(false);
   const [readOnlyQuery, setReadOnlyQuery] = useState(true);
+  const auth = useAuth();
+  const client = useGatekeeper();
 
   const closeSaveSearchModal = () => {
     setIsSaveSearchModalOpen(false);
   };
 
-  const onClickAdvancedSearch = () => {
+  const onClickAdvancedSearch = async () => {
     if (search.trim()) {
       setIsLoading(true);
-      const queriesWithIndices = createAdvancedQueriesBySource(
-        standardFields,
-        search,
-        selectedSources,
-        availableSources
-      );
-      searchQuery(queriesWithIndices).then((result) => {
-        setSearchResults(result);
-        setSelectedTabNumber(1);
-        if (isLoading) {
-          setIsLoading(false);
-        }
-      });
+      const advancedQuery = buildDslQuery(search, standardFields);
+      const params = {
+        query: advancedQuery,
+        sourcesId: availableSources.map((source) => source.id),
+        fieldsId: standardFields,
+        advancedQuery: true,
+      };
+      const result = await client.searchQuery(params);
+      setSearchResults(result);
+      setSelectedTabNumber(1);
+      if (isLoading) {
+        setIsLoading(false);
+      }
     }
   };
 
-  const onClickCountResults = () => {
-    if (!!search) {
-      const queriesWithIndices = createAdvancedQueriesBySource(
-        standardFields,
-        search,
-        selectedSources,
-        availableSources
-      );
-      getQueryCount(queriesWithIndices).then((result) => {
-        if (result || result === 0) {
-          setSearchCount(result);
-        }
-      });
+  const onClickCountResults = async () => {
+    if (search) {
+      const query = buildDslQuery(search, standardFields);
+      const params = {
+        query,
+        sourcesId: availableSources.map((source) => source.id),
+        fieldsId: standardFields,
+        advancedQuery: true,
+      };
+      const result = await client.searchQuery(params);
+      if (!result || result.error) {
+        toast.error(
+          <ToastMessage title={t('validation:error')} message={result.error} />
+        );
+        setSearchCount(0);
+      }
+      setSearchCount(result.length);
     }
   };
 
-  const addHistory = (
+  const addHistory = async (
     search,
     searchName,
     searchFields,
     searchDescription,
     setUserHistory
   ) => {
-    addUserHistory(
-      sessionStorage.getItem('kcId'),
-      search,
-      searchName,
-      searchFields,
-      searchDescription
-    ).then((result) => {
-      if (result.error) {
-        toast.error(
-          <ToastMessage title={t('validation:error')} message={result.error} />
-        );
-      } else {
-        toast.success(t('search:advancedSearch.searchHistory.searchSaved'));
+    const sub = auth.userData?.profile?.sub;
+    if (!sub) {
+      return;
+    }
+    const params = {
+      name: searchName,
+      query: search,
+      ui_structure: JSON.stringify(searchFields),
+      description: searchDescription,
+    };
+    const result = await client.addHistory(sub, params);
+    if (result.error) {
+      toast.error(<ToastMessage title={t('validation:error')} message={result.error} />);
+    } else {
+      const userHistory = await client.getUserHistory(sub);
+      if (userHistory[0] && userHistory[0].ui_structure) {
+        userHistory.forEach((item) => {
+          item.ui_structure = JSON.parse(item.ui_structure);
+          item.label = `${item.name} - ${new Date(item.createdat).toLocaleString()}`;
+        });
+        setUserHistory(userHistory);
       }
-      fetchHistory(setUserHistory);
-    });
+    }
   };
 
   const SaveSearchModal = () => {
@@ -332,7 +348,7 @@ const SearchBar = ({
     const [searchDescription, setSearchDescription] = useState('');
 
     const onClickSaveSearch = () => {
-      if (!!searchName) {
+      if (searchName) {
         addHistory(search, searchName, searchFields, searchDescription, setUserHistory);
         setSearchName('');
         setSearchDescription('');
@@ -403,15 +419,14 @@ const SearchBar = ({
           <p>{t('search:advancedSearch.editableQueryToast.title')}</p>
         </EuiTitle>
         <EuiSpacer size={'s'} />
-        <p>{t('search:advancedSearch.editableQueryToast.content.part1')}</p>
-        <EuiSpacer size={'s'} />
-        <p>{t('search:advancedSearch.editableQueryToast.content.part2')}</p>
-        <EuiSpacer size={'s'} />
-        <ul>
-          <li>{t('search:advancedSearch.editableQueryToast.content.part3')}</li>
-          <EuiSpacer size={'s'} />
-          <li>{t('search:advancedSearch.editableQueryToast.content.part4')}</li>
-        </ul>
+        <EuiText>
+          <p>{t('search:advancedSearch.editableQueryToast.content.part1')}</p>
+          <p>{t('search:advancedSearch.editableQueryToast.content.part2')}</p>
+          <ul>
+            <li>{t('search:advancedSearch.editableQueryToast.content.part3')}</li>
+            <li>{t('search:advancedSearch.editableQueryToast.content.part4')}</li>
+          </ul>
+        </EuiText>
       </>
     );
   };
@@ -510,7 +525,7 @@ const PopoverSelect = ({ standardFields, searchFields, setSearchFields }) => {
   const [selectedSection, setSelectedSection] = useState([]);
 
   const handleAddField = () => {
-    if (!!selectedField[0]) {
+    if (selectedField[0]) {
       const field = standardFields.find(
         (item) =>
           item.field_name.replace(/_|\./g, ' ') ===
@@ -544,26 +559,9 @@ const PopoverSelect = ({ standardFields, searchFields, setSearchFields }) => {
     }
   };
 
-  const SelectField = () => {
-    const renderOption = (option, searchValue, contentClassName) => {
-      const { label, color } = option;
-      return <EuiHealth color={color}>{label}</EuiHealth>;
-    };
-    if (selectedSection.length) {
-      return (
-        <>
-          <EuiComboBox
-            placeholder={t('search:advancedSearch.fields.addFieldPopover.selectSection')}
-            singleSelection={{ asPlainText: true }}
-            options={getFieldsBySection(standardFields, selectedSection[0])}
-            selectedOptions={selectedField}
-            onChange={(selected) => setSelectedField(selected)}
-            isClearable={true}
-            renderOption={renderOption}
-          />
-        </>
-      );
-    }
+  const renderOption = (option) => {
+    const { label, color } = option;
+    return <EuiHealth color={color}>{label}</EuiHealth>;
   };
 
   return (
@@ -596,7 +594,18 @@ const PopoverSelect = ({ standardFields, searchFields, setSearchFields }) => {
           }}
           isClearable={false}
         />
-        <SelectField />
+        <EuiComboBox
+          placeholder={t('search:advancedSearch.fields.addFieldPopover.selectSection')}
+          singleSelection={{ asPlainText: true }}
+          options={getFieldsBySection(standardFields, selectedSection[0])}
+          selectedOptions={selectedField}
+          onChange={(selected) => {
+            setSelectedField(selected);
+          }}
+          isClearable={true}
+          renderOption={renderOption}
+          isDisabled={selectedSection.length === 0}
+        />
       </EuiFlexGroup>
       <EuiPopoverFooter style={styles.noBorder} paddingSize={'m'}>
         <EuiButton
@@ -649,9 +658,10 @@ const PopoverValueContent = ({
           <p>{t('search:advancedSearch.policyToast.title')}</p>
         </EuiTitle>
         <EuiSpacer size={'s'} />
-        <p>{t('search:advancedSearch.policyToast.content.0')}</p>
-        <EuiSpacer size={'s'} />
-        <p>{t('search:advancedSearch.policyToast.content.1')}</p>
+        <EuiText>
+          <p>{t('search:advancedSearch.policyToast.content.0')}</p>
+          <p>{t('search:advancedSearch.policyToast.content.1')}</p>
+        </EuiText>
       </>
     );
   };
@@ -661,7 +671,7 @@ const PopoverValueContent = ({
     if (Array.isArray(searchFields[index].values)) {
       fieldValues = [];
       searchFields[index].values.forEach((value) => {
-        if (!!value) {
+        if (value) {
           fieldValues.push(value);
         }
       });
@@ -683,7 +693,7 @@ const PopoverValueContent = ({
     setSearchFields(updatedSearchFields);
     updateSearch(setSearch, updatedSearchFields, selectedOperatorId, setSearchCount);
     setFieldCount(updateArrayElement(fieldCount, index));
-    if (searchFields[index].sources.length) {
+    if (searchFields[index].sources?.length) {
       const filteredSources = [];
       searchFields[index].sources.forEach((sourceId) => {
         let source;
@@ -782,7 +792,7 @@ const PopoverValueContent = ({
   };
 
   const SelectDates = ({ i }) => {
-    if (!!searchFields[index].values[i].option) {
+    if (searchFields[index].values[i].option) {
       switch (searchFields[index].values[i].option) {
         case 'between':
           return (
@@ -867,7 +877,7 @@ const PopoverValueContent = ({
   };
 
   const NumericValues = ({ i }) => {
-    if (!!searchFields[index].values[i].option) {
+    if (searchFields[index].values[i].option) {
       switch (searchFields[index].values[i].option) {
         case 'between':
           return (
@@ -1098,13 +1108,19 @@ const PopoverValueButton = ({
   const { t } = useTranslation('search');
   const [isPopoverValueOpen, setIsPopoverValueOpen] = useState(false);
 
+  const handleButtonClick = (e) => {
+    e.preventDefault();
+    e.stopPropagation();
+    setIsPopoverValueOpen((prevState) => !prevState);
+  };
+
   return (
     <EuiPopover
       panelPaddingSize="s"
       button={
         <EuiButtonIcon
           size="s"
-          onClick={() => setIsPopoverValueOpen(!isPopoverValueOpen)}
+          onClick={handleButtonClick}
           iconType="documentEdit"
           title={t('search:advancedSearch.fields.fieldContentPopover.addFieldValues')}
           aria-label={t(
@@ -1154,19 +1170,20 @@ const FieldsPanel = ({
   sources,
 }) => {
   const { t } = useTranslation('search');
+  const client = useGatekeeper();
 
-  const countFieldValues = (field, index) => {
+  const countFieldValues = async (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 advancedQuery = buildDslQuery(fieldStr, standardFields);
+    const params = {
+      query: advancedQuery,
+      sourcesId: availableSources.map((source) => source.id),
+      fieldsId: standardFields,
+      advancedQuery: true,
+    };
+    const result = await client.searchQuery(params);
+    const fieldCountUpdated = updateArrayElement(fieldCount, index, result.length);
+    setFieldCount(fieldCountUpdated);
   };
 
   const handleRemoveField = (index) => {
@@ -1224,8 +1241,12 @@ const FieldsPanel = ({
     updateSearch(setSearch, updatedSearchFields, selectedOperatorId, setSearchCount);
   };
 
-  if (standardFields === []) {
-    return <h2>{t('search:advancedSearch.fields.loadingFields')}</h2>;
+  if (standardFields == []) {
+    return (
+      <EuiTitle size="xs">
+        <h2>{t('search:advancedSearch.fields.loadingFields')}</h2>
+      </EuiTitle>
+    );
   }
 
   return (
@@ -1242,92 +1263,72 @@ const FieldsPanel = ({
               key={'field' + index}
               paddingSize="s"
             >
-              <EuiFlexGroup direction="row" alignItems="center">
-                <EuiFlexItem grow={false}>
+              <EuiFlexGroup
+                direction="row"
+                alignItems="center"
+                justifyContent={'spaceBetween'}
+              >
+                <EuiFlexGroup direction={'row'} alignItems="center" gutterSize={'m'}>
+                  <EuiSpacer size={'s'} />
                   <EuiButtonIcon
-                    size="s"
+                    iconSize="m"
                     color="danger"
                     onClick={() => handleRemoveField(index)}
-                    iconType="indexClose"
+                    iconType="cross"
                     title={t('search:advancedSearch.fields.removeFieldButton')}
                     aria-label={t('search:advancedSearch.fields.removeFieldButton')}
                   />
-                </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>
-                {!isNaN(fieldCount[index]) && (
-                  <EuiFlexItem grow={false}>
-                    <EuiTextColor color="secondary">
-                      {t('search:advancedSearch.resultsCount', {
-                        count: fieldCount[index],
-                      })}
-                    </EuiTextColor>
-                  </EuiFlexItem>
-                )}
-                {field.isValidated && (
-                  <EuiFlexItem grow={false}>
-                    <EuiButtonIcon
-                      size="s"
-                      onClick={() => countFieldValues(field, index)}
-                      iconType="number"
-                      title={t('search:advancedSearch.countResultsButton')}
-                      aria-label={t('search:advancedSearch.countResultsButton')}
-                    />
-                  </EuiFlexItem>
-                )}
-                {field.isValidated && (
-                  <EuiFlexItem grow={false}>
-                    <EuiButtonIcon
-                      size="s"
-                      color="danger"
-                      onClick={() => handleClearValues(index)}
-                      iconType="trash"
-                      title={t('search:advancedSearch.fields.clearValues')}
-                      aria-label={t('search:advancedSearch.fields.clearValues')}
-                    />
-                  </EuiFlexItem>
-                )}
+                  <EuiHealth color={field.sources?.length ? 'danger' : 'primary'}>
+                    {field.isValidated
+                      ? fieldValuesToString(field).replace(/_|\./g, ' ')
+                      : field.name.replace(/_|\./g, ' ')}
+                  </EuiHealth>
+                </EuiFlexGroup>
                 <EuiFlexItem grow={false}>
-                  <PopoverValueButton
-                    index={index}
-                    standardFields={standardFields}
-                    searchFields={searchFields}
-                    setSearchFields={setSearchFields}
-                    setSearch={setSearch}
-                    setSearchCount={setSearchCount}
-                    fieldCount={fieldCount}
-                    setFieldCount={setFieldCount}
-                    selectedOperatorId={selectedOperatorId}
-                    selectedSources={selectedSources}
-                    setSelectedSources={setSelectedSources}
-                    availableSources={availableSources}
-                    setAvailableSources={setAvailableSources}
-                  />
+                  <EuiFlexGroup direction={'row'} alignItems="center" gutterSize={'m'}>
+                    {!isNaN(fieldCount[index]) && (
+                      <EuiTextColor color="secondary">
+                        {t('search:advancedSearch.resultsCount', {
+                          count: fieldCount[index],
+                        })}
+                      </EuiTextColor>
+                    )}
+                    {field.isValidated && (
+                      <EuiButtonIcon
+                        size="s"
+                        onClick={() => countFieldValues(field, index)}
+                        iconType="number"
+                        title={t('search:advancedSearch.countResultsButton')}
+                        aria-label={t('search:advancedSearch.countResultsButton')}
+                      />
+                    )}
+                    {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')}
+                      />
+                    )}
+                    <PopoverValueButton
+                      index={index}
+                      standardFields={standardFields}
+                      searchFields={searchFields}
+                      setSearchFields={setSearchFields}
+                      setSearch={setSearch}
+                      setSearchCount={setSearchCount}
+                      fieldCount={fieldCount}
+                      setFieldCount={setFieldCount}
+                      selectedOperatorId={selectedOperatorId}
+                      selectedSources={selectedSources}
+                      setSelectedSources={setSelectedSources}
+                      availableSources={availableSources}
+                      setAvailableSources={setAvailableSources}
+                    />
+                    <EuiSpacer size={'s'} />
+                  </EuiFlexGroup>
                 </EuiFlexItem>
               </EuiFlexGroup>
             </EuiPanel>
@@ -1457,7 +1458,6 @@ const AdvancedSearch = ({
             setSearchResults={setSearchResults}
             searchFields={searchFields}
             setSearchFields={setSearchFields}
-            selectedSources={selectedSources}
             setSelectedSources={setSelectedSources}
             availableSources={availableSources}
             setAvailableSources={setAvailableSources}
diff --git a/src/pages/search/BasicSearch/BasicSearch.js b/src/pages/search/BasicSearch/BasicSearch.js
index dfdd1f16d842ba8653b675651fda7c532a0a9070..8f36200ba4551d8bb63fbc45a18761295c984796 100644
--- a/src/pages/search/BasicSearch/BasicSearch.js
+++ b/src/pages/search/BasicSearch/BasicSearch.js
@@ -6,15 +6,16 @@ import {
   EuiFlexItem,
   EuiSpacer,
 } from '@elastic/eui';
-import { createBasicQueriesBySource } from '../../../Utils';
-import { searchQuery } from '../../../actions/source';
 import { useTranslation } from 'react-i18next';
 import SearchModeSwitcher from '../SearchModeSwitcher';
+import { useGatekeeper } from '../../../contexts/GatekeeperContext';
+import HowToBasicSearch from './HowToBasicSearch';
+import { toast } from 'react-toastify';
+import ToastMessage from '../../../components/ToastMessage/ToastMessage';
 
 const BasicSearch = ({
   standardFields,
   availableSources,
-  selectedSources,
   basicSearch,
   setBasicSearch,
   setIsAdvancedSearch,
@@ -22,25 +23,31 @@ const BasicSearch = ({
   setSearchResults,
   setSelectedTabNumber,
 }) => {
-  const { t } = useTranslation('search');
+  const { t } = useTranslation(['search', 'validation']);
+  const client = useGatekeeper();
   const [isLoading, setIsLoading] = useState(false);
 
-  const onFormSubmit = (e) => {
+  const onFormSubmit = async (e) => {
     e.preventDefault();
     setIsLoading(true);
-    const queriesWithIndices = createBasicQueriesBySource(
-      standardFields,
-      basicSearch,
-      selectedSources,
-      availableSources
-    );
-    searchQuery(queriesWithIndices).then((result) => {
+    const result = await client.searchQuery({
+      query: basicSearch,
+      sourcesId: availableSources.map((source) => source.id),
+      fieldsId: standardFields,
+    });
+    setIsLoading(false);
+    if (!result.error) {
       setSearchResults(result);
-      if (isLoading) {
-        setIsLoading(false);
-      }
       setSelectedTabNumber(1);
-    });
+    } else if (result.statusCode === 400) {
+      toast.error(
+        <ToastMessage title={t('validation:error')} message={result.message} />
+      );
+    } else {
+      toast.error(
+        <ToastMessage title={t('validation:error')} message={t('search:queryError')} />
+      );
+    }
   };
 
   return (
@@ -71,6 +78,7 @@ const BasicSearch = ({
               </EuiFlexItem>
             </EuiFlexGroup>
           </form>
+          <HowToBasicSearch />
         </EuiFlexItem>
       </EuiFlexGroup>
     </>
diff --git a/src/pages/search/BasicSearch/HowToBasicSearch.js b/src/pages/search/BasicSearch/HowToBasicSearch.js
new file mode 100644
index 0000000000000000000000000000000000000000..1b4dd8ad82badb6e5f4b5d97487522715c492e33
--- /dev/null
+++ b/src/pages/search/BasicSearch/HowToBasicSearch.js
@@ -0,0 +1,120 @@
+import React from 'react';
+import {
+  EuiAccordion,
+  EuiButtonEmpty,
+  EuiFlexGrid,
+  EuiFlexGroup,
+  EuiFlexItem,
+  EuiIcon,
+  EuiPanel,
+  EuiSpacer,
+  EuiText,
+  EuiTitle,
+  useGeneratedHtmlId,
+} from '@elastic/eui';
+import { Trans, useTranslation } from 'react-i18next';
+
+const HowToBasicSearch = () => {
+  const { t, ready } = useTranslation('search');
+
+  const Example = ({ query, desc }) => {
+    return (
+      <EuiFlexGroup>
+        <EuiText color={'success'}>
+          <code>
+            <EuiIcon type="search" /> <Trans i18nKey={query} />
+          </code>
+        </EuiText>
+        <EuiText>
+          <Trans i18nKey={desc} />
+        </EuiText>
+      </EuiFlexGroup>
+    );
+  };
+
+  const Examples = () => {
+    const examples = t('search:basicSearch.howTo.examples', { returnObjects: true });
+
+    return (
+      <EuiPanel hasShadow={false} hasBorder={true}>
+        <EuiTitle size={'xs'}>
+          <p>{t('search:basicSearch.howTo.examplesTitle')}</p>
+        </EuiTitle>
+        <EuiSpacer size={'s'} />
+        <EuiFlexGrid columns={1} gutterSize={'s'} direction={'column'}>
+          {examples.map((example, index) => {
+            return (
+              <EuiFlexItem key={index}>
+                <Example query={example.query} desc={example.desc} />
+              </EuiFlexItem>
+            );
+          })}
+        </EuiFlexGrid>
+      </EuiPanel>
+    );
+  };
+
+  const Section = ({ title, text }) => {
+    return (
+      <EuiPanel hasShadow={false} hasBorder={true}>
+        <EuiTitle size={'xs'}>
+          <p>{t(title)}</p>
+        </EuiTitle>
+        <EuiPanel hasShadow={false} paddingSize={'s'}>
+          <EuiText>
+            <ul>
+              {text.map((item, index) => {
+                return (
+                  <li key={index}>
+                    <Trans i18nKey={item} />
+                  </li>
+                );
+              })}
+            </ul>
+          </EuiText>
+        </EuiPanel>
+      </EuiPanel>
+    );
+  };
+
+  if (!ready) {
+    return 'Loading translations...';
+  }
+
+  const sections = t('search:basicSearch.howTo.sections', { returnObjects: true });
+
+  return (
+    <>
+      <EuiSpacer size="s" />
+      <EuiFlexGroup>
+        <EuiFlexItem>
+          <EuiAccordion
+            id={useGeneratedHtmlId({ prefix: 'howToAccordion' })}
+            buttonContent={
+              <EuiButtonEmpty>
+                {t('search:basicSearch.howTo.toggleAction')}
+              </EuiButtonEmpty>
+            }
+            buttonElement={'div'}
+          >
+            <EuiSpacer size="s" />
+            <Examples />
+            <EuiSpacer size="s" />
+            <EuiFlexGrid columns={2} gutterSize={'s'}>
+              {sections.map((section, index) => {
+                return (
+                  <EuiFlexItem key={index}>
+                    <Section title={section.title} text={section.content} />
+                  </EuiFlexItem>
+                );
+              })}
+            </EuiFlexGrid>
+            <EuiSpacer size="s" />
+          </EuiAccordion>
+        </EuiFlexItem>
+      </EuiFlexGroup>
+    </>
+  );
+};
+
+export default HowToBasicSearch;
diff --git a/src/pages/search/Search.js b/src/pages/search/Search.js
index 6f31278b60fd47d8257f778f74fc011082ab5bb5..970e5d7e5794048b4290ee8c2f208a54d483b527 100644
--- a/src/pages/search/Search.js
+++ b/src/pages/search/Search.js
@@ -1,19 +1,15 @@
-import React, { useState, useEffect } from 'react';
-import { EuiTabbedContent, EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui';
+import React, { useEffect, useState } from 'react';
+import { EuiSpacer, EuiTabbedContent } from '@elastic/eui';
 import Results from '../results/Results';
 import SearchMap from '../maps/SearchMap';
-import { removeNullFields } from '../../Utils.js';
-import {
-  fetchPublicFields,
-  fetchUserPolicyFields,
-  fetchSources,
-} from '../../actions/source';
 import { useTranslation } from 'react-i18next';
 import AdvancedSearch from './AdvancedSearch/AdvancedSearch';
 import BasicSearch from './BasicSearch/BasicSearch';
+import { useGatekeeper } from '../../contexts/GatekeeperContext';
 import ResourceFlyout from '../results/ResourceFlyout/ResourceFlyout';
 
 const Search = () => {
+  const client = useGatekeeper();
   const { t } = useTranslation('search');
   const [selectedTabNumber, setSelectedTabNumber] = useState(0);
   const [isAdvancedSearch, setIsAdvancedSearch] = useState(false);
@@ -29,37 +25,59 @@ const Search = () => {
   const [resourceFlyoutData, setResourceFlyoutData] = useState({});
 
   useEffect(() => {
-    fetchPublicFields().then((resultStdFields) => {
-      resultStdFields.forEach((field) => {
-        field.sources = [];
-      });
-      setStandardFields(resultStdFields);
-      fetchUserPolicyFields(sessionStorage.getItem('kcId')).then((resultPolicyFields) => {
-        const userFields = resultStdFields;
-        resultPolicyFields.forEach((polField) => {
-          const stdFieldIndex = userFields.findIndex(
-            (stdField) => stdField.id === polField.std_id
-          );
-          if (stdFieldIndex >= 0) {
-            if (!userFields[stdFieldIndex].sources.includes(polField.source_id))
-              userFields[stdFieldIndex].sources.push(polField.source_id);
-          } else {
-            const newField = {
-              id: polField.std_id,
-              sources: [polField.source_id],
-              ...polField,
-            };
-            userFields.push(newField);
-          }
-        });
-        userFields.sort((a, b) => (a.id > b.id ? 1 : b.id > a.id ? -1 : 0));
-        setStandardFields(removeNullFields(userFields));
-      });
-    });
-    fetchSources(sessionStorage.getItem('kcId')).then((result) => {
-      setSources(result);
-      setAvailableSources(result);
-    });
+    if (!client) {
+      return;
+    }
+    const fetchElements = async () => {
+      const publicFields = await client.getPublicFields();
+      setStandardFields(publicFields);
+      const sources = await client.getSources();
+      setSources(sources);
+      setAvailableSources(sources);
+    };
+    fetchElements();
+    // client
+    //   .getPublicFields()
+    //   .then((resultStdFields) => {
+    //     resultStdFields.forEach((field) => {
+    //       field.sources = [];
+    //     });
+    //     setStandardFields(resultStdFields);
+    //     fetchUserPolicyFields(sessionStorage.getItem('kcId')).then(
+    //       (resultPolicyFields) => {
+    //         const userFields = resultStdFields;
+    //         resultPolicyFields.forEach((polField) => {
+    //           const stdFieldIndex = userFields.findIndex(
+    //             (stdField) => stdField.id === polField.std_id
+    //           );
+    //           if (stdFieldIndex >= 0) {
+    //             if (!userFields[stdFieldIndex].sources.includes(polField.source_id))
+    //               userFields[stdFieldIndex].sources.push(polField.source_id);
+    //           } else {
+    //             const newField = {
+    //               id: polField.std_id,
+    //               sources: [polField.source_id],
+    //               ...polField,
+    //             };
+    //             userFields.push(newField);
+    //           }
+    //         });
+    //         userFields.sort((a, b) => (a.id > b.id ? 1 : b.id > a.id ? -1 : 0));
+    //         setStandardFields(removeNullFields(userFields));
+    //       }
+    //     );
+    //   })
+    //   .catch((error) => {
+    //     console.error(error);
+    //   });
+    // fetchSources(sessionStorage.getItem('kcId'))
+    //   .then((result) => {
+    //     setSources(result);
+    //     setAvailableSources(result);
+    //   })
+    //   .catch((error) => {
+    //     console.error(error);
+    //   });
   }, []);
 
   // On new search, reset selected rows
@@ -76,7 +94,7 @@ const Search = () => {
   const setResourceFlyoutDataFromId = (id) => {
     const resourceData = getResourceDataFromRowId(id);
     // Extract all values except for id to avoid displaying it to user
-    setResourceFlyoutData((({ id, ...rest }) => rest)(resourceData));
+    setResourceFlyoutData((({ id, ...rest }) => rest)(resourceData)); // eslint-disable-line
   };
 
   const tabsContent = [
@@ -84,77 +102,72 @@ const Search = () => {
       id: 'tab1',
       name: t('search:tabs.composeSearch'),
       content: (
-        <EuiFlexGroup>
-          <EuiFlexItem>
-            <EuiSpacer size={'l'} />
-            {isAdvancedSearch ? (
-              <AdvancedSearch
-                search={search}
-                setSearch={setSearch}
-                setSearchResults={setSearchResults}
-                selectedSources={selectedSources}
-                setSelectedSources={setSelectedSources}
-                availableSources={availableSources}
-                setAvailableSources={setAvailableSources}
-                standardFields={standardFields}
-                setStandardFields={setStandardFields}
-                sources={sources}
-                setSelectedTabNumber={setSelectedTabNumber}
-                isAdvancedSearch={isAdvancedSearch}
-                setIsAdvancedSearch={setIsAdvancedSearch}
-              />
-            ) : (
-              <BasicSearch
-                isAdvancedSearch={isAdvancedSearch}
-                setIsAdvancedSearch={setIsAdvancedSearch}
-                standardFields={standardFields}
-                availableSources={availableSources}
-                selectedSources={selectedSources}
-                basicSearch={basicSearch}
-                setBasicSearch={setBasicSearch}
-                setSearchResults={setSearchResults}
-                setSelectedTabNumber={setSelectedTabNumber}
-              />
-            )}
-          </EuiFlexItem>
-        </EuiFlexGroup>
+        <>
+          <EuiSpacer size={'l'} />
+          {isAdvancedSearch ? (
+            <AdvancedSearch
+              search={search}
+              setSearch={setSearch}
+              setSearchResults={setSearchResults}
+              selectedSources={selectedSources}
+              setSelectedSources={setSelectedSources}
+              availableSources={availableSources}
+              setAvailableSources={setAvailableSources}
+              standardFields={standardFields}
+              setStandardFields={setStandardFields}
+              sources={sources}
+              setSelectedTabNumber={setSelectedTabNumber}
+              isAdvancedSearch={isAdvancedSearch}
+              setIsAdvancedSearch={setIsAdvancedSearch}
+            />
+          ) : (
+            <BasicSearch
+              isAdvancedSearch={isAdvancedSearch}
+              setIsAdvancedSearch={setIsAdvancedSearch}
+              standardFields={standardFields}
+              availableSources={availableSources}
+              basicSearch={basicSearch}
+              setBasicSearch={setBasicSearch}
+              setSearchResults={setSearchResults}
+              setSelectedTabNumber={setSelectedTabNumber}
+            />
+          )}
+        </>
       ),
     },
     {
       id: 'tab2',
       name: t('search:tabs.results'),
       content: (
-        <EuiFlexGroup>
-          <EuiFlexItem>
-            <EuiSpacer size="l" />
-            <Results
-              searchResults={searchResults}
-              searchQuery={isAdvancedSearch ? search : basicSearch}
-              selectedRowsIds={selectedResultsRowsIds}
-              setSelectedRowsIds={setSelectedResultsRowsIds}
-              setResourceFlyoutDataFromId={setResourceFlyoutDataFromId}
-              setIsResourceFlyoutOpen={setIsResourceFlyoutOpen}
-            />
-          </EuiFlexItem>
-        </EuiFlexGroup>
+        <>
+          <EuiSpacer size={'l'} />
+          <Results
+            searchResults={searchResults}
+            searchQuery={isAdvancedSearch ? search : basicSearch}
+            selectedRowsIds={selectedResultsRowsIds}
+            setSelectedRowsIds={setSelectedResultsRowsIds}
+            setResourceFlyoutDataFromId={setResourceFlyoutDataFromId}
+            setIsResourceFlyoutOpen={setIsResourceFlyoutOpen}
+            setSelectedTabNumber={setSelectedTabNumber}
+          />
+        </>
       ),
     },
     {
       id: 'tab3',
       name: t('search:tabs.map'),
       content: (
-        <EuiFlexGroup>
-          <EuiFlexItem>
-            <EuiSpacer size="l" />
-            <SearchMap
-              searchResults={searchResults}
-              selectedPointsIds={selectedResultsRowsIds}
-              setSelectedPointsIds={setSelectedResultsRowsIds}
-              setResourceFlyoutDataFromId={setResourceFlyoutDataFromId}
-              setIsResourceFlyoutOpen={setIsResourceFlyoutOpen}
-            />
-          </EuiFlexItem>
-        </EuiFlexGroup>
+        <>
+          <EuiSpacer size={'l'} />
+          <SearchMap
+            searchResults={searchResults}
+            selectedPointsIds={selectedResultsRowsIds}
+            setSelectedPointsIds={setSelectedResultsRowsIds}
+            setResourceFlyoutDataFromId={setResourceFlyoutDataFromId}
+            setIsResourceFlyoutOpen={setIsResourceFlyoutOpen}
+            setSelectedTabNumber={setSelectedTabNumber}
+          />
+        </>
       ),
     },
   ];
diff --git a/src/services/GatekeeperService.js b/src/services/GatekeeperService.js
new file mode 100644
index 0000000000000000000000000000000000000000..f13dc9192c3a1f2e6bcdcc158c7623dab405a109
--- /dev/null
+++ b/src/services/GatekeeperService.js
@@ -0,0 +1,153 @@
+export class InSylvaGatekeeperClient {
+  constructor(getUserInfo) {
+    this.getUserInfo = getUserInfo;
+  }
+  async get(path, payload) {
+    const userInfo = await this.getUserInfo();
+    const { access_token } = userInfo;
+    const headers = {
+      'Content-Type': 'application/json',
+      Authorization: `Bearer ${access_token}`,
+    };
+    const response = await fetch(
+      `${process.env.REACT_APP_IN_SYLVA_GATEKEEPER_BASE_URL}${path}`,
+      {
+        method: 'GET',
+        headers,
+        mode: 'cors',
+        body: JSON.stringify(payload),
+      }
+    );
+    return await response.json();
+  }
+
+  async post(path, requestContent) {
+    const userInfo = await this.getUserInfo();
+    const { access_token } = userInfo;
+    const headers = {
+      'Content-Type': 'application/json',
+      Authorization: `Bearer ${access_token}`,
+    };
+    const response = await fetch(
+      `${process.env.REACT_APP_IN_SYLVA_GATEKEEPER_BASE_URL}${path}`,
+      {
+        method: 'POST',
+        headers,
+        body: JSON.stringify(requestContent),
+        mode: 'cors',
+      }
+    );
+    return await response.json();
+  }
+
+  async delete(path, requestContent) {
+    const userInfo = await this.getUserInfo();
+    const { access_token } = userInfo;
+    const headers = {
+      'Content-Type': 'application/json',
+      Authorization: `Bearer ${access_token}`,
+    };
+    const response = await fetch(
+      `${process.env.REACT_APP_IN_SYLVA_GATEKEEPER_BASE_URL}${path}`,
+      {
+        method: 'DELETE',
+        headers,
+        body: JSON.stringify(requestContent),
+        mode: 'cors',
+      }
+    );
+    if (response.status === 204) {
+      return {
+        success: true,
+        message: 'Request deleted successfully',
+      };
+    } else {
+      return {
+        success: false,
+        message: 'Request not deleted',
+      };
+    }
+  }
+
+  async createUser(sub, email) {
+    const path = `/users`;
+    return await this.post(path, {
+      kc_id: sub,
+      email,
+    });
+  }
+
+  async findUserBySub(sub) {
+    const path = `/users/${sub}`;
+    return await this.get(path);
+  }
+
+  async getRoles() {
+    const path = `/roles`;
+    return await this.get(path);
+  }
+
+  async createUserRequest(message, sub) {
+    const path = `/users/${sub}/requests`;
+    return await this.post(path, {
+      message: message,
+    });
+  }
+
+  async deleteUserRequest(id) {
+    const path = `/user-requests/${id}`;
+    return await this.delete(path, {});
+  }
+
+  async getGroups() {
+    const path = `/groups`;
+    return await this.get(path);
+  }
+
+  async getPublicFields() {
+    const path = `/public_std_fields`;
+    return await this.get(path);
+  }
+
+  async getUserFieldsDisplaySettings(sub) {
+    const path = `/users/${sub}/fields`;
+    return await this.get(path);
+  }
+
+  async setUserFieldsDisplaySettings(sub, fields) {
+    const path = `/users/${sub}/fields`;
+    return await this.post(path, {
+      fields_id: fields,
+    });
+  }
+
+  async getDefaultFieldsDisplaySettings() {
+    const path = `/std_fields/default`;
+    return await this.get(path);
+  }
+
+  async getSources() {
+    const path = `/sources`;
+    return await this.get(path);
+  }
+
+  async getUserSources(sub) {
+    const path = `/users/${sub}/sources`;
+    return await this.get(path);
+  }
+
+  async searchQuery(payload) {
+    const path = `/search`;
+    return await this.post(path, payload);
+  }
+
+  async getUserHistory(sub) {
+    const path = `/users/${sub}/history`;
+    return await this.get(path);
+  }
+
+  async addHistory(sub, payload) {
+    const path = `/users/${sub}/history`;
+    return await this.post(path, payload);
+  }
+}
diff --git a/src/services/TokenService.js b/src/services/TokenService.js
new file mode 100644
index 0000000000000000000000000000000000000000..4ead0437e0a703af625156f75479432cdd855ec2
--- /dev/null
+++ b/src/services/TokenService.js
@@ -0,0 +1,38 @@
+export default class TokenService {
+  async getUserInfo(access_token) {
+    const issuerUrl = process.env.REACT_APP_KEYCLOAK_BASE_URL;
+    const userInfoEndpoint = issuerUrl + '/protocol/openid-connect/userinfo';
+
+    const response = await fetch(userInfoEndpoint, {
+      headers: {
+        Authorization: `Bearer ${access_token}`,
+      },
+    });
+    if (response.status !== 200) {
+      return null;
+    } else {
+      const userInfo = await response.json();
+      return userInfo;
+    }
+  }
+
+  async refreshToken(refresh_token) {
+    const issuerUrl = process.env.REACT_APP_KEYCLOAK_BASE_URL;
+    const tokenEndpoint = issuerUrl + '/protocol/openid-connect/token';
+
+    const response = await fetch(tokenEndpoint, {
+      method: 'POST',
+      headers: {
+        'Content-Type': 'application/x-www-form-urlencoded',
+      },
+      body: `grant_type=refresh_token&refresh_token=${refresh_token}&client_id=${process.env.REACT_APP_KEYCLOAK_CLIENT_ID}&client_secret=${process.env.REACT_APP_KEYCLOAK_CLIENT_SECRET}`,
+    });
+
+    if (response.status !== 200) {
+      return null;
+    } else {
+      const response = await response.json();
+      return response;
+    }
+  }
+}