diff --git a/.gitignore b/.gitignore
index ee5c9d8336b76b3bb5658d0888bdec86eb7b0990..dca7088f54475232a1e94a0a1127a18183ed3eb3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -18,11 +18,8 @@
 *.sublime-workspace
 
 # IDE - VSCode
-.vscode/*
-!.vscode/settings.json
-!.vscode/tasks.json
-!.vscode/launch.json
-!.vscode/extensions.json
+.vscode
+
 
 # misc
 /.sass-cache
diff --git a/config/config-dev.json b/config/config-dev.json
index adf822d5cb7103ae1e517c14ba05e845c154d0c4..fafda28003d1c1fb0d069a53e406741ae014584b 100644
--- a/config/config-dev.json
+++ b/config/config-dev.json
@@ -13,5 +13,17 @@
   },
   "middlewareLegacyAuth": {
     "url": "https://kong-dev.alpha.grandlyon.com/middleware-legacy/"
+  },
+  "changelog": {
+    "url": "https://kong-dev.alpha.grandlyon.com/changelog/"
+  },
+  "credits": {
+    "url": "https://kong-dev.alpha.grandlyon.com/credits/credits/"
+  },
+  "restHeartAggregations": {
+    "url": "https://kong-dev.alpha.grandlyon.com/indexer-logs/indexerdb/"
+  },
+  "reuses": {
+    "url": "https://kong-dev.alpha.grandlyon.com/reuses/"
   }
 }
diff --git a/config/config-rec.json b/config/config-rec.json
index 804b6b67d735472bc7d026d357d0ff410c63004f..d1d5b2d87a719ae58ff25c27c0059e4ad87e685a 100644
--- a/config/config-rec.json
+++ b/config/config-rec.json
@@ -13,5 +13,17 @@
   },
   "middlewareLegacyAuth": {
     "url": "https://kong-rec.alpha.grandlyon.com/middleware-legacy/"
+  },
+  "changelog": {
+    "url": "https://kong-rec.alpha.grandlyon.com/changelog/"
+  },
+  "credits": {
+    "url": "https://kong-rec.alpha.grandlyon.com/credits/credits/"
+  },
+  "reuses": {
+    "url": "https://kong-rec.alpha.grandlyon.com/reuses/"
+  },
+  "restHeartAggregations": {
+    "url": "https://kong-rec.alpha.grandlyon.com/indexer-logs/indexerdb/"
   }
 }
diff --git a/package-lock.json b/package-lock.json
index 0addc5a51aa9f1e45293776e4b13a5ee7e197b37..811cd7cd1efe1b94e51e38bfce35ed1aa5f987a0 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
 {
   "name": "admin-gui",
-  "version": "1.1.0",
+  "version": "1.2.0",
   "lockfileVersion": 1,
   "requires": true,
   "dependencies": {
@@ -1147,6 +1147,21 @@
         }
       }
     },
+    "@types/highlight.js": {
+      "version": "9.12.3",
+      "resolved": "https://registry.npmjs.org/@types/highlight.js/-/highlight.js-9.12.3.tgz",
+      "integrity": "sha512-pGF/zvYOACZ/gLGWdQH8zSwteQS1epp68yRcVLJMgUck/MjEn/FBYmPub9pXT8C1e4a8YZfHo1CKyV8q1vKUnQ==",
+      "dev": true
+    },
+    "@types/highlightjs": {
+      "version": "9.12.0",
+      "resolved": "https://registry.npmjs.org/@types/highlightjs/-/highlightjs-9.12.0.tgz",
+      "integrity": "sha512-MmUcjkDtCBfx2BPeLLTtJ5mFmGgWk9nAgZmNesixaGHOr0tCecsTU2iUgYvhRsWJSts2WbcpAtVPuIzZ0ybJ1A==",
+      "dev": true,
+      "requires": {
+        "@types/highlight.js": "*"
+      }
+    },
     "@types/jasmine": {
       "version": "2.8.16",
       "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-2.8.16.tgz",
@@ -1575,6 +1590,7 @@
       "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz",
       "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==",
       "dev": true,
+      "optional": true,
       "requires": {
         "delegates": "^1.0.0",
         "readable-stream": "^2.0.6"
@@ -1661,9 +1677,7 @@
     "asap": {
       "version": "2.0.6",
       "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
-      "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=",
-      "dev": true,
-      "optional": true
+      "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY="
     },
     "asn1": {
       "version": "0.2.4",
@@ -2273,8 +2287,7 @@
     "buffer-from": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
-      "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
-      "dev": true
+      "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A=="
     },
     "buffer-indexof": {
       "version": "1.1.1",
@@ -2310,6 +2323,11 @@
       "resolved": "https://registry.npmjs.org/bulma/-/bulma-0.7.5.tgz",
       "integrity": "sha512-cX98TIn0I6sKba/DhW0FBjtaDpxTelU166pf7ICXpCCuplHWyu6C9LYZmL5PEsnePIeJaiorsTEzzNk3Tsm1hw=="
     },
+    "bulma-switch": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/bulma-switch/-/bulma-switch-2.0.0.tgz",
+      "integrity": "sha512-myD38zeUfjmdduq+pXabhJEe3x2hQP48l/OI+Y0fO3HdDynZUY/VJygucvEAJKRjr4HxD5DnEm4yx+oDOBXpAA=="
+    },
     "bytes": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
@@ -2430,6 +2448,39 @@
       "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==",
       "dev": true
     },
+    "chart.js": {
+      "version": "2.8.0",
+      "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-2.8.0.tgz",
+      "integrity": "sha512-Di3wUL4BFvqI5FB5K26aQ+hvWh8wnP9A3DWGvXHVkO13D3DSnaSsdZx29cXlEsYKVkn1E2az+ZYFS4t0zi8x0w==",
+      "requires": {
+        "chartjs-color": "^2.1.0",
+        "moment": "^2.10.2"
+      }
+    },
+    "chartjs-color": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/chartjs-color/-/chartjs-color-2.3.0.tgz",
+      "integrity": "sha512-hEvVheqczsoHD+fZ+tfPUE+1+RbV6b+eksp2LwAhwRTVXEjCSEavvk+Hg3H6SZfGlPh/UfmWKGIvZbtobOEm3g==",
+      "requires": {
+        "chartjs-color-string": "^0.6.0",
+        "color-convert": "^0.5.3"
+      },
+      "dependencies": {
+        "color-convert": {
+          "version": "0.5.3",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-0.5.3.tgz",
+          "integrity": "sha1-vbbGnOZg+t/+CwAHzER+G59ygr0="
+        }
+      }
+    },
+    "chartjs-color-string": {
+      "version": "0.6.0",
+      "resolved": "https://registry.npmjs.org/chartjs-color-string/-/chartjs-color-string-0.6.0.tgz",
+      "integrity": "sha512-TIB5OKn1hPJvO7JcteW4WY/63v6KwEdt6udfnDE9iCAZgy+V4SrbSxoIbTw/xkUIapjEI4ExGtD0+6D3KyFd7A==",
+      "requires": {
+        "color-name": "^1.0.0"
+      }
+    },
     "chokidar": {
       "version": "2.0.4",
       "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.4.tgz",
@@ -2721,7 +2772,6 @@
       "version": "1.6.2",
       "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
       "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
-      "dev": true,
       "requires": {
         "buffer-from": "^1.0.0",
         "inherits": "^2.0.3",
@@ -2783,7 +2833,8 @@
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
       "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
-      "dev": true
+      "dev": true,
+      "optional": true
     },
     "constants-browserify": {
       "version": "1.0.0",
@@ -2868,8 +2919,7 @@
     "core-util-is": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
-      "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
-      "dev": true
+      "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
     },
     "cosmiconfig": {
       "version": "4.0.0",
@@ -3179,7 +3229,8 @@
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
       "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=",
-      "dev": true
+      "dev": true,
+      "optional": true
     },
     "depd": {
       "version": "1.1.2",
@@ -4266,7 +4317,8 @@
         "ansi-regex": {
           "version": "2.1.1",
           "bundled": true,
-          "dev": true
+          "dev": true,
+          "optional": true
         },
         "aproba": {
           "version": "1.2.0",
@@ -4287,12 +4339,14 @@
         "balanced-match": {
           "version": "1.0.0",
           "bundled": true,
-          "dev": true
+          "dev": true,
+          "optional": true
         },
         "brace-expansion": {
           "version": "1.1.11",
           "bundled": true,
           "dev": true,
+          "optional": true,
           "requires": {
             "balanced-match": "^1.0.0",
             "concat-map": "0.0.1"
@@ -4307,17 +4361,20 @@
         "code-point-at": {
           "version": "1.1.0",
           "bundled": true,
-          "dev": true
+          "dev": true,
+          "optional": true
         },
         "concat-map": {
           "version": "0.0.1",
           "bundled": true,
-          "dev": true
+          "dev": true,
+          "optional": true
         },
         "console-control-strings": {
           "version": "1.1.0",
           "bundled": true,
-          "dev": true
+          "dev": true,
+          "optional": true
         },
         "core-util-is": {
           "version": "1.0.2",
@@ -4434,7 +4491,8 @@
         "inherits": {
           "version": "2.0.3",
           "bundled": true,
-          "dev": true
+          "dev": true,
+          "optional": true
         },
         "ini": {
           "version": "1.3.5",
@@ -4446,6 +4504,7 @@
           "version": "1.0.0",
           "bundled": true,
           "dev": true,
+          "optional": true,
           "requires": {
             "number-is-nan": "^1.0.0"
           }
@@ -4460,6 +4519,7 @@
           "version": "3.0.4",
           "bundled": true,
           "dev": true,
+          "optional": true,
           "requires": {
             "brace-expansion": "^1.1.7"
           }
@@ -4467,12 +4527,14 @@
         "minimist": {
           "version": "0.0.8",
           "bundled": true,
-          "dev": true
+          "dev": true,
+          "optional": true
         },
         "minipass": {
           "version": "2.2.4",
           "bundled": true,
           "dev": true,
+          "optional": true,
           "requires": {
             "safe-buffer": "^5.1.1",
             "yallist": "^3.0.0"
@@ -4491,6 +4553,7 @@
           "version": "0.5.1",
           "bundled": true,
           "dev": true,
+          "optional": true,
           "requires": {
             "minimist": "0.0.8"
           }
@@ -4571,7 +4634,8 @@
         "number-is-nan": {
           "version": "1.0.1",
           "bundled": true,
-          "dev": true
+          "dev": true,
+          "optional": true
         },
         "object-assign": {
           "version": "4.1.1",
@@ -4583,6 +4647,7 @@
           "version": "1.4.0",
           "bundled": true,
           "dev": true,
+          "optional": true,
           "requires": {
             "wrappy": "1"
           }
@@ -4668,7 +4733,8 @@
         "safe-buffer": {
           "version": "5.1.1",
           "bundled": true,
-          "dev": true
+          "dev": true,
+          "optional": true
         },
         "safer-buffer": {
           "version": "2.1.2",
@@ -4704,6 +4770,7 @@
           "version": "1.0.2",
           "bundled": true,
           "dev": true,
+          "optional": true,
           "requires": {
             "code-point-at": "^1.0.0",
             "is-fullwidth-code-point": "^1.0.0",
@@ -4723,6 +4790,7 @@
           "version": "3.0.1",
           "bundled": true,
           "dev": true,
+          "optional": true,
           "requires": {
             "ansi-regex": "^2.0.0"
           }
@@ -4766,12 +4834,14 @@
         "wrappy": {
           "version": "1.0.2",
           "bundled": true,
-          "dev": true
+          "dev": true,
+          "optional": true
         },
         "yallist": {
           "version": "3.0.2",
           "bundled": true,
-          "dev": true
+          "dev": true,
+          "optional": true
         }
       }
     },
@@ -4780,6 +4850,7 @@
       "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz",
       "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=",
       "dev": true,
+      "optional": true,
       "requires": {
         "graceful-fs": "^4.1.2",
         "inherits": "~2.0.0",
@@ -4792,6 +4863,7 @@
       "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
       "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
       "dev": true,
+      "optional": true,
       "requires": {
         "aproba": "^1.0.3",
         "console-control-strings": "^1.0.0",
@@ -4829,7 +4901,8 @@
       "version": "4.0.1",
       "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz",
       "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=",
-      "dev": true
+      "dev": true,
+      "optional": true
     },
     "get-stream": {
       "version": "3.0.0",
@@ -5065,7 +5138,8 @@
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
       "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=",
-      "dev": true
+      "dev": true,
+      "optional": true
     },
     "has-value": {
       "version": "1.0.0",
@@ -5119,6 +5193,11 @@
         "minimalistic-assert": "^1.0.1"
       }
     },
+    "highlight.js": {
+      "version": "9.15.10",
+      "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.15.10.tgz",
+      "integrity": "sha512-RoV7OkQm0T3os3Dd2VHLNMoaoDVx77Wygln3n9l5YV172XonWG6rgQD3XnF/BuFFZw9A0TJgmMSO8FEWQgvcXw=="
+    },
     "hmac-drbg": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
@@ -5154,6 +5233,23 @@
       "integrity": "sha1-DfKTUfByEWNRXfueVUPl9u7VFi8=",
       "dev": true
     },
+    "http-basic": {
+      "version": "2.5.1",
+      "resolved": "https://registry.npmjs.org/http-basic/-/http-basic-2.5.1.tgz",
+      "integrity": "sha1-jORHvbW2xXf4pj4/p4BW7Eu02/s=",
+      "requires": {
+        "caseless": "~0.11.0",
+        "concat-stream": "^1.4.6",
+        "http-response-object": "^1.0.0"
+      },
+      "dependencies": {
+        "caseless": {
+          "version": "0.11.0",
+          "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.11.0.tgz",
+          "integrity": "sha1-cVuW6phBWTzDMGeSP17GDr2k99c="
+        }
+      }
+    },
     "http-cache-semantics": {
       "version": "3.8.1",
       "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz",
@@ -5228,6 +5324,11 @@
         "micromatch": "^3.1.9"
       }
     },
+    "http-response-object": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/http-response-object/-/http-response-object-1.1.0.tgz",
+      "integrity": "sha1-p8TnWq6C87tJBOT0P2FWc7TVGMM="
+    },
     "http-signature": {
       "version": "1.2.0",
       "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
@@ -5317,6 +5418,11 @@
         "minimatch": "^3.0.4"
       }
     },
+    "image-extensions": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/image-extensions/-/image-extensions-1.1.0.tgz",
+      "integrity": "sha1-uOa/YDnfAFbjM1AqALZjejEF2JQ="
+    },
     "image-size": {
       "version": "0.5.5",
       "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz",
@@ -5738,6 +5844,24 @@
         "is-extglob": "^2.1.1"
       }
     },
+    "is-image": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/is-image/-/is-image-1.0.1.tgz",
+      "integrity": "sha1-b9UadSoaERUG0GDZUhGLC5ibQm4=",
+      "requires": {
+        "image-extensions": "^1.0.1"
+      }
+    },
+    "is-image-url": {
+      "version": "1.1.8",
+      "resolved": "https://registry.npmjs.org/is-image-url/-/is-image-url-1.1.8.tgz",
+      "integrity": "sha1-qmK/l1fFvlQCJpmcdMOiGtqYuDw=",
+      "requires": {
+        "is-image": "^1.0.1",
+        "is-url": "^1.2.1",
+        "sync-request": "^2.1.0"
+      }
+    },
     "is-number": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
@@ -5821,11 +5945,17 @@
       "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
       "dev": true
     },
+    "is-url": {
+      "version": "1.2.4",
+      "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz",
+      "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww=="
+    },
     "is-utf8": {
       "version": "0.2.1",
       "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz",
       "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=",
-      "dev": true
+      "dev": true,
+      "optional": true
     },
     "is-windows": {
       "version": "1.0.2",
@@ -5842,8 +5972,7 @@
     "isarray": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
-      "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
-      "dev": true
+      "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
     },
     "isbinaryfile": {
       "version": "3.0.3",
@@ -6433,6 +6562,7 @@
       "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
       "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=",
       "dev": true,
+      "optional": true,
       "requires": {
         "graceful-fs": "^4.1.2",
         "parse-json": "^2.2.0",
@@ -6445,7 +6575,8 @@
           "version": "2.3.0",
           "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
           "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
-          "dev": true
+          "dev": true,
+          "optional": true
         }
       }
     },
@@ -6732,7 +6863,8 @@
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz",
       "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=",
-      "dev": true
+      "dev": true,
+      "optional": true
     },
     "map-visit": {
       "version": "1.0.0",
@@ -7015,6 +7147,11 @@
         "minimist": "0.0.8"
       }
     },
+    "moment": {
+      "version": "2.24.0",
+      "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz",
+      "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg=="
+    },
     "move-concurrently": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz",
@@ -7366,6 +7503,7 @@
       "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
       "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
       "dev": true,
+      "optional": true,
       "requires": {
         "are-we-there-yet": "~1.1.2",
         "console-control-strings": "~1.1.0",
@@ -7573,6 +7711,11 @@
         "lcid": "^1.0.0"
       }
     },
+    "os-shim": {
+      "version": "0.1.3",
+      "resolved": "https://registry.npmjs.org/os-shim/-/os-shim-0.1.3.tgz",
+      "integrity": "sha1-a2LDeRz3kJ6jXtRuF2WLtBfLORc="
+    },
     "os-tmpdir": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
@@ -8123,15 +8266,12 @@
     "process-nextick-args": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
-      "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==",
-      "dev": true
+      "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw=="
     },
     "promise": {
       "version": "7.3.1",
       "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
       "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==",
-      "dev": true,
-      "optional": true,
       "requires": {
         "asap": "~2.0.3"
       }
@@ -8364,8 +8504,7 @@
     "qs": {
       "version": "6.5.2",
       "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
-      "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==",
-      "dev": true
+      "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
     },
     "querystring": {
       "version": "0.2.0",
@@ -8473,6 +8612,7 @@
       "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz",
       "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=",
       "dev": true,
+      "optional": true,
       "requires": {
         "load-json-file": "^1.0.0",
         "normalize-package-data": "^2.3.2",
@@ -8484,6 +8624,7 @@
           "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz",
           "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=",
           "dev": true,
+          "optional": true,
           "requires": {
             "graceful-fs": "^4.1.2",
             "pify": "^2.0.0",
@@ -8494,7 +8635,8 @@
           "version": "2.3.0",
           "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
           "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
-          "dev": true
+          "dev": true,
+          "optional": true
         }
       }
     },
@@ -8503,6 +8645,7 @@
       "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz",
       "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=",
       "dev": true,
+      "optional": true,
       "requires": {
         "find-up": "^1.0.0",
         "read-pkg": "^1.0.0"
@@ -8513,6 +8656,7 @@
           "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
           "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=",
           "dev": true,
+          "optional": true,
           "requires": {
             "path-exists": "^2.0.0",
             "pinkie-promise": "^2.0.0"
@@ -8523,6 +8667,7 @@
           "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz",
           "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=",
           "dev": true,
+          "optional": true,
           "requires": {
             "pinkie-promise": "^2.0.0"
           }
@@ -8533,7 +8678,6 @@
       "version": "2.3.6",
       "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
       "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
-      "dev": true,
       "requires": {
         "core-util-is": "~1.0.0",
         "inherits": "~2.0.3",
@@ -8854,8 +8998,7 @@
     "safe-buffer": {
       "version": "5.1.2",
       "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
-      "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
-      "dev": true
+      "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
     },
     "safe-regex": {
       "version": "1.1.0",
@@ -9523,6 +9666,15 @@
       "integrity": "sha512-CYAPYdBu34781kLHkaW3m6b/uUSyMOC2R61gcYMWooeuaGtjof86ZA/8T+qVPPt7np1085CR9hmMGrySwEc8Xg==",
       "dev": true
     },
+    "spawn-sync": {
+      "version": "1.0.15",
+      "resolved": "https://registry.npmjs.org/spawn-sync/-/spawn-sync-1.0.15.tgz",
+      "integrity": "sha1-sAeZVX63+wyDdsKdROih6mfldHY=",
+      "requires": {
+        "concat-stream": "^1.4.7",
+        "os-shim": "^0.1.2"
+      }
+    },
     "spdx-correct": {
       "version": "3.0.2",
       "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.2.tgz",
@@ -9805,7 +9957,6 @@
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
       "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
-      "dev": true,
       "requires": {
         "safe-buffer": "~5.1.0"
       }
@@ -9823,6 +9974,7 @@
       "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz",
       "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=",
       "dev": true,
+      "optional": true,
       "requires": {
         "is-utf8": "^0.2.0"
       }
@@ -9917,6 +10069,17 @@
       "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==",
       "dev": true
     },
+    "sync-request": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/sync-request/-/sync-request-2.2.0.tgz",
+      "integrity": "sha1-p70sES+glGPrkUnP8OnUKMR5do8=",
+      "requires": {
+        "concat-stream": "^1.4.7",
+        "http-response-object": "^1.0.1",
+        "spawn-sync": "^1.0.1",
+        "then-request": "^2.0.1"
+      }
+    },
     "tapable": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.1.tgz",
@@ -10127,6 +10290,26 @@
         }
       }
     },
+    "then-request": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/then-request/-/then-request-2.2.0.tgz",
+      "integrity": "sha1-ZnizL6DKIY/laZgbvYhxtZQGDYE=",
+      "requires": {
+        "caseless": "~0.11.0",
+        "concat-stream": "^1.4.7",
+        "http-basic": "^2.5.1",
+        "http-response-object": "^1.1.0",
+        "promise": "^7.1.1",
+        "qs": "^6.1.0"
+      },
+      "dependencies": {
+        "caseless": {
+          "version": "0.11.0",
+          "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.11.0.tgz",
+          "integrity": "sha1-cVuW6phBWTzDMGeSP17GDr2k99c="
+        }
+      }
+    },
     "through": {
       "version": "2.3.8",
       "resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz",
@@ -10457,8 +10640,7 @@
     "typedarray": {
       "version": "0.0.6",
       "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
-      "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=",
-      "dev": true
+      "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
     },
     "typescript": {
       "version": "3.2.4",
@@ -10661,8 +10843,7 @@
     "util-deprecate": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
-      "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
-      "dev": true
+      "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
     },
     "utils-merge": {
       "version": "1.0.1",
@@ -11231,6 +11412,7 @@
       "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
       "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
       "dev": true,
+      "optional": true,
       "requires": {
         "string-width": "^1.0.2 || 2"
       }
diff --git a/package.json b/package.json
index fae0d245c8f2f151cde8da873bc68ef38ffd1d04..13be3fff3d1aa4d8884b9189d90f6c4015b157b4 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "admin-gui",
-  "version": "1.2.0",
+  "version": "1.3.0",
   "scripts": {
     "ng": "ng",
     "start": "ng serve",
@@ -25,7 +25,11 @@
     "@angular/platform-browser-dynamic": "^7.2.4",
     "@angular/router": "^7.2.4",
     "bulma": "^0.7.5",
+    "bulma-switch": "^2.0.0",
+    "chart.js": "^2.5.0",
     "core-js": "^2.6.4",
+    "highlight.js": "^9.15.10",
+    "is-image-url": "^1.1.8",
     "node-rsa": "^1.0.5",
     "rxjs": "^6.4.0",
     "rxjs-tslint": "^0.1.7",
@@ -54,4 +58,4 @@
     "tslint-config-airbnb": "^5.11.1",
     "typescript": "~3.2.4"
   }
-}
+}
\ No newline at end of file
diff --git a/src/app/app.component.ts b/src/app/app.component.ts
index 994fd9e1ac95c651f574ab12b569a2e5710c373c..c494026aa4f529dd12701bb7c7a31208dc8e3a94 100644
--- a/src/app/app.component.ts
+++ b/src/app/app.component.ts
@@ -37,7 +37,6 @@ export class AppComponent implements OnInit {
   }
 
   loggout() {
-    console.log('blabla')
     this._userService.resetAuth();
     this.closeUserDropdown();
     this._router.navigate(['/login']);
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index e08e4f57d586c603d9d14761b331bb57983f3b25..2cd76bcaf31df57efb6ca59481ff4c73bbcb790d 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -11,6 +11,7 @@ import { AppComponents } from './components';
 import { UserModule } from './user/user.module';
 import { UserService } from './user/services';
 import { AppDirectives } from './directives';
+import { FilterPipe } from './components/logs-dashboard/logs-slugs/filter.pipe';
 import { HttpErrorResponseInterceptor } from './interceptors/http-error-response-interceptor';
 
 // Function used by APP_INITIALIZER before the app start: init user info / statut (expect a promise)
@@ -35,6 +36,7 @@ export function initAppConfig(appConfigService: AppConfigService) {
 @NgModule({
   declarations: [
     AppComponent,
+    FilterPipe,
     ...AppComponents,
     ...AppDirectives,
   ],
@@ -49,6 +51,7 @@ export function initAppConfig(appConfigService: AppConfigService) {
     UserModule,
   ],
   providers: [
+    FilterPipe,
     ...AppServices,
     {
       provide: HTTP_INTERCEPTORS,
diff --git a/src/app/app.routing.module.ts b/src/app/app.routing.module.ts
index f58c4ca7d2b473f77f8e8f9d00f94bf8bc158155..d3c9bdf1a9bc12f4b564434a7295039f51a05821 100644
--- a/src/app/app.routing.module.ts
+++ b/src/app/app.routing.module.ts
@@ -6,8 +6,20 @@ import { OrganizationFormComponent } from './components/organizations/edit/organ
 import { ResourcesComponent } from './components/resources/list/resources.component';
 import { ResourceFormComponent } from './components/resources/edit/resource-form.component';
 import { ResourceDetailComponent } from './components/resources/detail/resource-detail.component';
-import { FormatsComponent, FormatDetailComponent, FormatFormComponent } from './components';
+import {
+  LogsPreReportComponent, LogsReportComponent, LogsHomeComponent, FormatsComponent,
+  FormatDetailComponent, FormatFormComponent, ChangelogDetailComponent, ChangelogFormComponent,
+  CreditsComponent, CreditFormComponent, CreditDetailComponent, ReusesComponent, ReuseFormComponent,
+  ReuseDetailComponent,
+  ProjectionsComponent,
+  ProjectionFormComponent,
+  ProjectionDetailComponent,
+  MediaComponent,
+  MediaFormComponent,
+  MediaDetailComponent,
+} from './components';
 import { AuthenticatedGuard } from './user/guards/authenticated.guard';
+import { ChangelogComponent } from './components/changelog/list/changelog.component';
 
 const appRoutes: Routes = [
   {
@@ -28,7 +40,7 @@ const appRoutes: Routes = [
     component: OrganizationFormComponent,
     canActivate: [AuthenticatedGuard],
     data: {
-      title: 'Nouveau producteur de données',
+      title: 'Nouveau partenaire',
     },
   },
   {
@@ -36,7 +48,7 @@ const appRoutes: Routes = [
     component: OrganizationFormComponent,
     canActivate: [AuthenticatedGuard],
     data: {
-      title: 'Modifier le producteur de données',
+      title: 'Modifier le partenaire',
     },
   },
   {
@@ -44,7 +56,7 @@ const appRoutes: Routes = [
     component: OrganizationDetailComponent,
     canActivate: [AuthenticatedGuard],
     data: {
-      title: 'Detail du producteur de données',
+      title: 'Detail du partenaire',
     },
   },
   {
@@ -111,6 +123,190 @@ const appRoutes: Routes = [
       title: 'Detail du format',
     },
   },
+  {
+    path: 'changelog',
+    component: ChangelogComponent,
+    canActivate: [AuthenticatedGuard],
+    data: {
+      title: 'Changelog',
+    },
+  },
+  {
+    path: 'changelog/new',
+    component: ChangelogFormComponent,
+    canActivate: [AuthenticatedGuard],
+    data: {
+      title: 'Nouveau changelog',
+    },
+  },
+  {
+    path: 'changelog/:id/edit',
+    component: ChangelogFormComponent,
+    canActivate: [AuthenticatedGuard],
+    data: {
+      title: 'Modifier le changelog',
+    },
+  },
+  {
+    path: 'changelog/:id',
+    component: ChangelogDetailComponent,
+    canActivate: [AuthenticatedGuard],
+    data: {
+      title: 'Detail du changelog',
+    },
+  },
+  {
+    path: 'credits',
+    component: CreditsComponent,
+    canActivate: [AuthenticatedGuard],
+    data: {
+      title: 'Crédits',
+    },
+  },
+  {
+    path: 'credits/new',
+    component: CreditFormComponent,
+    canActivate: [AuthenticatedGuard],
+    data: {
+      title: 'Nouveau crédit',
+    },
+  },
+  {
+    path: 'credits/:id/edit',
+    component: CreditFormComponent,
+    canActivate: [AuthenticatedGuard],
+    data: {
+      title: 'Modifier le crédit',
+    },
+  },
+  {
+    path: 'credits/:id',
+    component: CreditDetailComponent,
+    canActivate: [AuthenticatedGuard],
+    data: {
+      title: 'Detail du crédit',
+    },
+  },
+  {
+    path: 'reutilisations',
+    component: ReusesComponent,
+    canActivate: [AuthenticatedGuard],
+    data: {
+      title: 'Réutilisations',
+    },
+  },
+  {
+    path: 'reutilisations/new',
+    component: ReuseFormComponent,
+    canActivate: [AuthenticatedGuard],
+    data: {
+      title: 'Nouvelle réutilisation',
+    },
+  },
+  {
+    path: 'reutilisations/:id/edit',
+    component: ReuseFormComponent,
+    canActivate: [AuthenticatedGuard],
+    data: {
+      title: 'Modifier la réutilisation',
+    },
+  },
+  {
+    path: 'reutilisations/:id',
+    component: ReuseDetailComponent,
+    canActivate: [AuthenticatedGuard],
+    data: {
+      title: 'Detail de la réutilisation',
+    },
+  },
+  {
+    path: 'datalogs',
+    component: LogsHomeComponent,
+    canActivate: [AuthenticatedGuard],
+    data: {
+      title: "Logs d'indexation: recherche",
+    },
+  },
+  {
+    path: 'datalogs/preReport/:type/:id',
+    component: LogsPreReportComponent,
+    canActivate: [AuthenticatedGuard],
+    data: {
+      title: "Logs d'indexation: recherche affinée",
+    },
+  },
+  {
+    path: 'datalogs/report/:slug/:sessionId/:urlCode',
+    component: LogsReportComponent,
+    canActivate: [AuthenticatedGuard],
+    data: {
+      title: "Rapport final d'indexation",
+    },
+  },
+  {
+    path: 'projections',
+    component: ProjectionsComponent,
+    canActivate: [AuthenticatedGuard],
+    data: {
+      title: 'Projections',
+    },
+  },
+  {
+    path: 'projections/new',
+    component: ProjectionFormComponent,
+    canActivate: [AuthenticatedGuard],
+    data: {
+      title: 'Nouvelle projection',
+    },
+  },
+  {
+    path: 'projections/:id/edit',
+    component: ProjectionFormComponent,
+    canActivate: [AuthenticatedGuard],
+    data: {
+      title: 'Modifier la projection',
+    },
+  },
+  {
+    path: 'projections/:id',
+    component: ProjectionDetailComponent,
+    canActivate: [AuthenticatedGuard],
+    data: {
+      title: 'Detail de la projection',
+    },
+  },
+  {
+    path: 'media',
+    component: MediaComponent,
+    canActivate: [AuthenticatedGuard],
+    data: {
+      title: 'Médias',
+    },
+  },
+  {
+    path: 'media/new',
+    component: MediaFormComponent,
+    canActivate: [AuthenticatedGuard],
+    data: {
+      title: 'Nouveau média',
+    },
+  },
+  {
+    path: 'media/:id/edit',
+    component: MediaFormComponent,
+    canActivate: [AuthenticatedGuard],
+    data: {
+      title: 'Modifier le média',
+    },
+  },
+  {
+    path: 'media/:id',
+    component: MediaDetailComponent,
+    canActivate: [AuthenticatedGuard],
+    data: {
+      title: 'Detail du média',
+    },
+  },
 ];
 
 @NgModule({
diff --git a/src/app/components/changelog/detail/changelog-detail.component.html b/src/app/components/changelog/detail/changelog-detail.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..4c4fb3fff5f023ad41c79c189b39a3208802973f
--- /dev/null
+++ b/src/app/components/changelog/detail/changelog-detail.component.html
@@ -0,0 +1,61 @@
+<section class="section page-container" *ngIf="changelog">
+  <app-page-header [pageInfo]="{title: title}"></app-page-header>
+
+  <div class="columns is-centered">
+    <div class="column is-8">
+      <div class="card">
+        <header class="card-header">
+          <p class="card-header-title has-text-centered">
+            Version {{changelog.version}}
+          </p>
+        </header>
+        <div class="card-content">
+          <div class="content">
+            <p>
+              <span class="has-text-weight-bold">Id: </span>
+              <span>{{changelog._id}}</span>
+            </p>
+            <p>
+              <span class="has-text-weight-bold">Date de création: </span>
+              <span>{{changelog.createDate | date:'dd-LL-yyyy HH:mm:ss'}}</span>
+            </p>
+            <p>
+              <span class="has-text-weight-bold">Date de dernière mise à jour: </span>
+              <span>{{changelog.updateDate | date:'dd-LL-yyyy HH:mm:ss'}}</span>
+            </p>
+            <p>
+              <span class="has-text-weight-bold">Langue: </span>
+              <span>{{changelog.language}}</span>
+            </p>
+            <div class="improvment-list-container"
+              *ngIf="changelog.majorImprovements && changelog.majorImprovements.length > 0">
+              <span class="has-text-weight-bold">Amélioration(s) majeure(s): </span>
+              <ul>
+                <li *ngFor="let major of changelog.majorImprovements">
+                  {{ major }}
+                </li>
+              </ul>
+            </div>
+            <div class="improvment-list-container"
+              *ngIf="changelog.minorImprovements && changelog.minorImprovements.length > 0">
+              <span class="has-text-weight-bold">Amélioration(s) mineure(s): </span>
+              <ul>
+                <li *ngFor="let minor of changelog.minorImprovements">
+                  {{ minor }}
+                </li>
+              </ul>
+            </div>
+            <div class="improvment-list-container" *ngIf="changelog.bugFixes && changelog.bugFixes.length > 0">
+              <span class="has-text-weight-bold">Correction(s) de bug(s): </span>
+              <ul>
+                <li *ngFor="let bug of changelog.bugFixes">
+                  {{ bug }}
+                </li>
+              </ul>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</section>
\ No newline at end of file
diff --git a/src/app/components/changelog/detail/changelog-detail.component.scss b/src/app/components/changelog/detail/changelog-detail.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..6c4c597c8549528b13c5ef646c06badf7a49fa60
--- /dev/null
+++ b/src/app/components/changelog/detail/changelog-detail.component.scss
@@ -0,0 +1,11 @@
+.card-header-title {
+  justify-content: center;
+}
+
+.improvment-list-container:not(:last-of-type) {
+  margin-bottom: 1rem;
+
+  ul {
+    margin-top: 0.5rem;
+  }
+}
diff --git a/src/app/components/changelog/detail/changelog-detail.component.ts b/src/app/components/changelog/detail/changelog-detail.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..13c119ceea40d68dbb3a539e5ebcaffa27d4b4ec
--- /dev/null
+++ b/src/app/components/changelog/detail/changelog-detail.component.ts
@@ -0,0 +1,31 @@
+
+import { switchMap } from 'rxjs/operators';
+import { Component, OnInit } from '@angular/core';
+import { ActivatedRoute, ParamMap } from '@angular/router';
+import { FormatService, ChangelogService } from 'src/app/services';
+import { Format } from 'src/app/models/format.model';
+import { Changelog } from '../../../models/changelog.model';
+
+@Component({
+  selector: 'app-changelog-detail',
+  templateUrl: './changelog-detail.component.html',
+  styleUrls: ['./changelog-detail.component.scss'],
+})
+export class ChangelogDetailComponent implements OnInit {
+
+  changelog: Changelog;
+  title: string;
+
+  constructor(
+    private _route: ActivatedRoute,
+    private _changelogService: ChangelogService,
+  ) {
+  }
+
+  ngOnInit(): void {
+    this.title = this._route.snapshot.data.title;
+    this._route.paramMap.pipe(
+      switchMap((params: ParamMap) => this._changelogService.findById(params.get('id'))))
+      .subscribe((changelog: Changelog) => this.changelog = changelog);
+  }
+}
diff --git a/src/app/components/changelog/edit/changelog-form.component.html b/src/app/components/changelog/edit/changelog-form.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..b4598bb289af245ec4b3c89b3047b8ad3170f6a3
--- /dev/null
+++ b/src/app/components/changelog/edit/changelog-form.component.html
@@ -0,0 +1,124 @@
+<section class="section page-container">
+  <app-page-header [pageInfo]="{title: title}"></app-page-header>
+
+  <form [formGroup]="form" (ngSubmit)="onSubmit()" class="columns is-centered is-marginless">
+    <div class="column is-7">
+      <input type="hidden" formControlName="_id" [value]="changelog._id">
+
+      <div class="field">
+        <label class="label required" for="version">Version</label>
+        <div class="control">
+          <input class="input" type="text" [value]="changelog.version" formControlName="version" id="version" required>
+        </div>
+        <div *ngIf="version.invalid && (version.dirty || version.touched)" class="alert alert-danger">
+          <p *ngIf="version.errors['required']" class="help is-danger">
+            La version est obligatoire.
+          </p>
+        </div>
+      </div>
+
+      <div class="field">
+        <label class="label required" for="language">Langue</label>
+        <div class="select">
+          <select [value]="changelog.language" formControlName="language" id="language">
+            <option value="EN">Anglais</option>
+            <option value="FR">Français</option>
+          </select>
+        </div>
+      </div>
+
+      <div class="field">
+        <div class="form-array-header">
+          <label class="label">Amélioration(s) majeure(s)</label>
+          <span class="icon" tabindex=0 (click)="addMajorImprovement()" (keyup.enter)="addMajorImprovement()"
+            title="Ajouter une amélioration majeure">
+            <i class="fas fa-plus"></i>
+          </span>
+        </div>
+
+        <div formArrayName="majorImprovements">
+          <div *ngFor="let majorImprovement of majorImprovements.controls; let i = index;" class="form-array-item">
+            <div class="form-array-input-wrapper">
+              <div class="control">
+                <input class="input" type="text" formControlName="{{i}}" required>
+              </div>
+              <div *ngIf="majorImprovement.invalid && (majorImprovement.dirty || majorImprovement.touched)"
+                class="alert alert-danger">
+                <p *ngIf="majorImprovement.hasError('required')" class="help is-danger">
+                  Vous devez saisir la description de l'amélioration majeure.
+                </p>
+              </div>
+            </div>
+            <span class="icon" tabindex=0 (click)="removeMajorImprovement(i)" (keyup.enter)="removeMajorImprovement(i)"
+              title="Supprimer l'amélioration majeure">
+              <i class="fas fa-trash"></i>
+            </span>
+          </div>
+        </div>
+      </div>
+
+      <div class="field">
+        <div class="form-array-header">
+          <label class="label">Amélioration(s) mineure(s)</label>
+          <span class="icon" tabindex=0 (click)="addMinorImprovement()" (keyup.enter)="addMinorImprovement()"
+            title="Ajouter une amélioration mineure">
+            <i class="fas fa-plus"></i>
+          </span>
+        </div>
+
+        <div formArrayName="minorImprovements">
+          <div *ngFor="let minorImprovement of minorImprovements.controls; let i = index;" class="form-array-item">
+            <div class="form-array-input-wrapper">
+              <div class="control">
+                <input class="input" type="text" formControlName="{{i}}" required>
+              </div>
+              <div *ngIf="minorImprovement.invalid && (minorImprovement.dirty || minorImprovement.touched)"
+                class="alert alert-danger">
+                <p *ngIf="minorImprovement.hasError('required')" class="help is-danger">
+                  Vous devez saisir la description de l'amélioration mineur.
+                </p>
+              </div>
+            </div>
+            <span class="icon" tabindex=0 (click)="removeMinorImprovement(i)" (keyup.enter)="removeMinorImprovement(i)"
+              title="Supprimer l'amélioration mineure">
+              <i class="fas fa-trash"></i>
+            </span>
+          </div>
+        </div>
+      </div>
+
+      <div class="field">
+        <div class="form-array-header">
+          <label class="label">Correction(s) de bug(s)</label>
+          <span class="icon" tabindex=0 (click)="addBugFix()" (keyup.enter)="addBugFix()"
+            title="Ajouter une correction de bug">
+            <i class="fas fa-plus"></i>
+          </span>
+        </div>
+
+        <div formArrayName="bugFixes">
+          <div *ngFor="let bugFix of bugFixes.controls; let i = index;" class="form-array-item">
+            <div class="form-array-input-wrapper">
+              <div class="control">
+                <input class="input" type="text" formControlName="{{i}}" required>
+              </div>
+              <div *ngIf="bugFix.invalid && (bugFix.dirty || bugFix.touched)" class="alert alert-danger">
+                <p *ngIf="bugFix.hasError('required')" class="help is-danger">
+                  Vous devez saisir la description de la correction de bug.
+                </p>
+              </div>
+            </div>
+            <span class="icon" tabindex=0 (click)="removeBugFix(i)" (keyup.keyup.enter)="removeBugFix(i)"
+              title="Supprimer la correction de bug">
+              <i class="fas fa-trash"></i>
+            </span>
+          </div>
+        </div>
+      </div>
+
+      <div class="has-text-right">
+        <button class="button button-gl" type="submit" [disabled]="formInvalid == true">Valider</button>
+      </div>
+    </div>
+  </form>
+</section>
\ No newline at end of file
diff --git a/src/app/components/changelog/edit/changelog-form.component.scss b/src/app/components/changelog/edit/changelog-form.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..4bc5c21221ab9f7ba636ca989a4038e8b3db6af0
--- /dev/null
+++ b/src/app/components/changelog/edit/changelog-form.component.scss
@@ -0,0 +1,43 @@
+.full-width {
+  width: 100%;
+}
+
+h1 {
+  text-align: center
+}
+
+.icon {
+  cursor: pointer;
+
+  &:hover {
+    .fa-plus {
+      color: lightblue;
+    }
+
+    .fa-trash {
+      color: #d5232a;
+    }
+  }
+}
+
+.form-array-header {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  margin-bottom: 0.5em;
+
+  label {
+    margin-bottom: 0;
+  }
+}
+
+.form-array-item {
+  display: flex;
+  align-items: center;
+  margin-bottom: 0.5em;
+}
+
+.form-array-input-wrapper {
+  flex-grow: 1;
+  margin-right: 0.5rem;
+}
diff --git a/src/app/components/changelog/edit/changelog-form.component.ts b/src/app/components/changelog/edit/changelog-form.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7bd875d87f7dff96a484c9ea9487d54e486daf91
--- /dev/null
+++ b/src/app/components/changelog/edit/changelog-form.component.ts
@@ -0,0 +1,145 @@
+import { Component, OnInit } from '@angular/core';
+import { ActivatedRoute, ParamMap, Router } from '@angular/router';
+import { FormBuilder, FormGroup, Validators, FormArray, FormControl } from '@angular/forms';
+import { filter, switchMap } from 'rxjs/operators';
+import { Format } from 'src/app/models/format.model';
+import { FormatService, ChangelogService, NotificationService } from 'src/app/services';
+import { Changelog } from '../../../models/changelog.model';
+
+@Component({
+  selector: 'app-changelog-form',
+  templateUrl: './changelog-form.component.html',
+  styleUrls: ['./changelog-form.component.scss'],
+})
+export class ChangelogFormComponent implements OnInit {
+
+  changelog: Changelog = new Changelog();
+  form: FormGroup;
+  title: string;
+
+  constructor(
+    private _changelogService: ChangelogService,
+    private _route: ActivatedRoute,
+    private _router: Router,
+    private _fb: FormBuilder,
+    private _notificationService: NotificationService,
+  ) {
+  }
+
+  ngOnInit() {
+    this.title = this._route.snapshot.data.title;
+    this.initForm();
+
+    this._route.paramMap.pipe(
+      filter((paramMap: ParamMap) => (paramMap.get('id') !== null)),
+      switchMap((paramMap: ParamMap) => this._changelogService.findById(paramMap.get('id'))))
+      .subscribe((changelog: Changelog) => {
+
+        this.changelog = changelog;
+
+        this.initForm();
+
+      });
+  }
+
+  initForm() {
+    this.form = this._fb.group({
+      _id: [this.changelog._id],
+      version: [this.changelog.version, Validators.required],
+      language: [this.changelog.language, Validators.required],
+      majorImprovements: new FormArray(this.changelog.majorImprovements.map(major => new FormControl(major))),
+      minorImprovements: new FormArray(this.changelog.minorImprovements.map(minor => new FormControl(minor))),
+      bugFixes: new FormArray(this.changelog.bugFixes.map(bug => new FormControl(bug))),
+    });
+  }
+
+  onSubmit() {
+    if (!this.formInvalid) {
+      this.changelog = new Changelog(this.form.value);
+
+      if (this.changelog._id) {
+        this._changelogService.update(this.changelog).subscribe(
+          (changelogCreated) => {
+            this._notificationService.notify({
+              type: 'success',
+              message: 'Le changelog a bien été mis à jour.',
+            });
+            this._router.navigate(['/changelog', changelogCreated._id]);
+          },
+          () => {
+            this._notificationService.notify({
+              type: 'error',
+              message: 'Une erreur est survenue lors de la mise à jour du changelog.',
+            });
+          },
+        );
+      } else {
+        this._changelogService.create(this.changelog).subscribe(
+          (changelogCreated) => {
+            this._notificationService.notify({
+              type: 'success',
+              message: 'Le changelog a bien été créé.',
+            });
+            this._router.navigate(['/changelog', changelogCreated._id]);
+          },
+          () => {
+            this._notificationService.notify({
+              type: 'error',
+              message: 'Une erreur est survenue lors de la création du changelog.',
+            });
+          },
+        );
+      }
+    }
+  }
+
+  addMajorImprovement() {
+    this.majorImprovements.push(new FormControl());
+  }
+
+  addMinorImprovement() {
+    this.minorImprovements.push(new FormControl());
+  }
+
+  addBugFix() {
+    this.bugFixes.push(new FormControl());
+  }
+
+  removeMajorImprovement(index) {
+    this.majorImprovements.removeAt(index);
+  }
+
+  removeMinorImprovement(index) {
+    this.minorImprovements.removeAt(index);
+  }
+
+  removeBugFix(index) {
+    this.bugFixes.removeAt(index);
+  }
+
+  // Getters for each property
+  get version() {
+    return this.form.controls['version'];
+  }
+
+  get language() {
+    return this.form.controls['language'];
+  }
+
+  get majorImprovements(): FormArray {
+    return this.form.get('majorImprovements') as FormArray;
+  }
+
+  get minorImprovements(): FormArray {
+    return this.form.get('minorImprovements') as FormArray;
+  }
+
+  get bugFixes(): FormArray {
+    return this.form.get('bugFixes') as FormArray;
+  }
+
+  get formInvalid() {
+    return this.form.invalid;
+  }
+
+}
diff --git a/src/app/components/changelog/list/changelog.component.html b/src/app/components/changelog/list/changelog.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..5485e47fa3865ad82522cbf13925b1c79c072985
--- /dev/null
+++ b/src/app/components/changelog/list/changelog.component.html
@@ -0,0 +1,90 @@
+<section class="section page-container">
+  <app-page-header [pageInfo]="pageHeaderInfo" [hideBackButton]="true"></app-page-header>
+  <div class="add-item-link has-text-right">
+    <a class="button button-gl" [routerLink]="['new']">
+      Ajouter
+    </a>
+  </div>
+  <div class="table entity-list-table" *ngIf="changelogs">
+    <div class="header columns is-marginless">
+      <div class="column is-2">
+        <span (click)="sortBy('version')" class="is-sortable">
+          <span class="sort-icons">
+            <span class="icon">
+              <i class="fas fa-sort-up"
+                [ngClass]="{'icon-red': sortOptions.value === 'version' && sortOptions.order === 'desc'}"></i>
+            </span>
+            <span class="icon">
+              <i class="fas fa-sort-down"
+                [ngClass]="{'icon-red': sortOptions.value === 'version' && sortOptions.order === 'asc'}"></i>
+            </span>
+          </span>
+          <span class="column-title" [ngClass]="{'active': sortOptions.value === version}">Version</span>
+        </span>
+      </div>
+      <div class="column is-2">
+        <span (click)="sortBy('language')" class="is-sortable">
+          <span class="sort-icons">
+            <span class="icon">
+              <i class="fas fa-sort-up"
+                [ngClass]="{'icon-red': sortOptions.value === 'language' && sortOptions.order === 'desc'}"></i>
+            </span>
+            <span class="icon">
+              <i class="fas fa-sort-down"
+                [ngClass]="{'icon-red': sortOptions.value === 'language' && sortOptions.order === 'asc'}"></i>
+            </span>
+          </span>
+          <span class="column-title" [ngClass]="{'active': sortOptions.value === language}">Langue</span>
+        </span>
+      </div>
+      <div class="column is-2">
+        <span class="column-title">Amélioration(s) majeure(s)</span>
+      </div>
+      <div class="column is-2">
+        <span class="column-title">Amélioration(s) mineure(s)</span>
+      </div>
+      <div class="column is-2">
+        <span class="column-title">Correction(s) de bug(s)</span>
+      </div>
+      <div class="column is-offset-1 is-1 has-text-centered">
+        <span class="column-title">Actions</span>
+      </div>
+    </div>
+    <div class="data-list">
+      <div class="data columns is-multiline is-vcentered is-marginless"
+        *ngFor="let changelog of changelogs; let i=index; let odd=odd; let even=even;"
+        [ngClass]="{ odd: odd, even: even }">
+        <div class="column is-2">
+          <span>{{ changelog.version }}</span>
+        </div>
+        <div class="column is-2">
+          <span>{{ changelog.language }}</span>
+        </div>
+        <div class="column is-2">
+          <span>{{ changelog.majorImprovements ? changelog.majorImprovements.length : 0 }}</span>
+        </div>
+        <div class="column is-2">
+          <span>{{ changelog.minorImprovements ? changelog.minorImprovements.length : 0 }}</span>
+        </div>
+        <div class="column is-2">
+          <span>{{ changelog.bugFixes ? changelog.bugFixes.length : 0 }}</span>
+        </div>
+        <div class="column is-offset-1 is-1 has-text-centered actions">
+          <app-crud-buttons [id]="changelog._id" (delete)="displayDeletePopup($event)"></app-crud-buttons>
+        </div>
+      </div>
+    </div>
+    <div class="columns is-marginless paginator">
+      <div class="column">
+        <app-paginator *ngIf="paginator.length > 0" [length]="paginator.length" [pageSize]="paginator.limit"
+          [pageSizeOptions]="paginator.pageSizeOptions" [pageIndex]="paginator.pageIndex" [pagesToShow]="5"
+          [showFirstLastButtons]="true" (page)="changePagination($event)">
+        </app-paginator>
+      </div>
+    </div>
+  </div>
+</section>
+
+<app-confirmation-modal (cancel)="objectToBeDeletedId=null" (continue)="deleteChangelog()" [texts]="deleteModalTexts"
+  [isOpened]="objectToBeDeletedId !== null">
+</app-confirmation-modal>
\ No newline at end of file
diff --git a/src/app/components/changelog/list/changelog.component.scss b/src/app/components/changelog/list/changelog.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/app/components/changelog/list/changelog.component.ts b/src/app/components/changelog/list/changelog.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..169fce8b9b7266bc9e950793d30384030e981082
--- /dev/null
+++ b/src/app/components/changelog/list/changelog.component.ts
@@ -0,0 +1,144 @@
+import { Component, OnInit, OnDestroy } from '@angular/core';
+import { PaginatorOptions } from 'src/app/models/paginator-options.model';
+import { IPageHeaderInfo } from '../../../models/page.model';
+import { Subscription } from 'rxjs';
+import { ChangelogService, NotificationService } from '../../../services';
+import { ChangelogRO, Changelog } from '../../../models/changelog.model';
+
+@Component({
+  selector: 'app-changelog',
+  templateUrl: './changelog.component.html',
+  styleUrls: ['./changelog.component.scss'],
+})
+export class ChangelogComponent implements OnInit, OnDestroy {
+
+  pageHeaderInfo: IPageHeaderInfo = {
+    title: '',
+  };
+  objectToBeDeletedId = null;
+  deleteModalTexts = {
+    main: 'Si vous poursuivez, le changelog sera définitivement supprimé.',
+    cancel: 'Annuler',
+    continue: 'Supprimer',
+  };
+  changelogs: Changelog[] = [];
+  searchChangeSub: Subscription;
+
+  // Paginator options
+  paginator: PaginatorOptions;
+  pageSize = 10;
+  pageSizeOptions = [5, 10, 25, 100];
+
+  sortValue: string;
+
+  totalElement: number;
+  filters = {
+    name: '',
+  };
+  where = {};
+
+  constructor(
+    private _changelogService: ChangelogService,
+    private _notificationService: NotificationService,
+  ) {
+    this.paginator = {
+      pageIndex: this._changelogService.pageNumber,
+      length: 0,
+      limit: this._changelogService.limit,
+      pageSizeOptions: [5, 10, 20],
+    };
+  }
+
+  ngOnInit(): void {
+    this._changelogService.sortOptions = {
+      value: 'version',
+      order: 'desc',
+    };
+    this.search();
+
+    this.searchChangeSub = this._changelogService.searchChange$.subscribe(
+      () => {
+        this.search();
+      },
+    );
+  }
+
+  private search() {
+    this._changelogService.getChangelogs()
+      .subscribe(
+        (items: ChangelogRO) => {
+          this.changelogs = items.changelogs;
+          this.totalElement = items.totalCount;
+
+          this.pageHeaderInfo.title = this.totalElement > 1 ?
+            `${this.totalElement} changelogs trouvés` :
+            `${this.totalElement} changelog trouvé`;
+
+          this.paginator.limit = this._changelogService.limit;
+          this.paginator.pageIndex = this._changelogService.pageNumber;
+          this.paginator.length = items.totalCount;
+        },
+        () => {
+          this.pageHeaderInfo.title = '0 changelog trouvé';
+          this._notificationService.notify({
+            type: 'error',
+            message: 'Une erreur est survenue lors du chargement des changelogs.',
+          });
+        },
+      );
+  }
+
+  // When pagination is changed by user, we update datasetList with new pagination options
+  changePagination(pageIndex) {
+    this._changelogService.paginationChanged(this.paginator.limit, pageIndex);
+  }
+
+  changePageSize(pageSize) {
+    this._changelogService.paginationChanged(pageSize, 1);
+  }
+
+  sortBy(key: string) {
+    if (this._changelogService.sortOptions.value === key) {
+      this._changelogService.reverseSortOrder();
+    } else {
+      this._changelogService.sortOptions.value = key;
+      this._changelogService.sortOptions.order = 'asc';
+    }
+    this.search();
+  }
+
+  get sortOptions() {
+    return this._changelogService.sortOptions;
+  }
+
+  displayDeletePopup(id) {
+    this.objectToBeDeletedId = id;
+  }
+
+  deleteChangelog() {
+    this._changelogService.delete(this.objectToBeDeletedId).subscribe(
+      () => {
+        this._notificationService.notify({
+          type: 'success',
+          message: 'Le changelog a été supprimé avec succès.',
+        });
+        this._changelogService.pageNumber = 1;
+        this.search();
+      },
+      () => {
+        this._notificationService.notify({
+          type: 'error',
+          message: 'Une erreur est survenue lors de la suppression du changelog.',
+        });
+      },
+      () => {
+        this.objectToBeDeletedId = null;
+      },
+    );
+
+  }
+
+  ngOnDestroy() {
+    this.searchChangeSub.unsubscribe();
+  }
+}
diff --git a/src/app/components/confirmation-modal/confirmation-modal.component.html b/src/app/components/confirmation-modal/confirmation-modal.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..81ebcd8a1cc17e2a51771d8d9a8c69ead71e1da9
--- /dev/null
+++ b/src/app/components/confirmation-modal/confirmation-modal.component.html
@@ -0,0 +1,13 @@
+<div class="modal" [ngClass]="{'is-active': isOpened === true}">
+  <div class="modal-background"></div>
+  <div class="modal-card">
+    <section class="modal-card-body">
+      <p>{{ texts.main }}</p>
+    </section>
+    <footer class="modal-card-foot">
+      <button class="button btn-red-text" (click)="close()">{{ texts.cancel }}</button>
+      <button class="button button-gl" (click)="carryOn()">{{ texts.continue }}</button>
+    </footer>
+  </div>
+  <button class="modal-close is-large" aria-label="close" (click)="close()"></button>
+</div>
\ No newline at end of file
diff --git a/src/app/components/confirmation-modal/confirmation-modal.component.scss b/src/app/components/confirmation-modal/confirmation-modal.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..8cc9988f537af838eb65e787a12be7b7ca29cf19
--- /dev/null
+++ b/src/app/components/confirmation-modal/confirmation-modal.component.scss
@@ -0,0 +1,8 @@
+.modal-card-body {
+  border-top-left-radius: 6px;
+  border-top-right-radius: 6px;
+}
+
+.modal-card-foot {
+  justify-content: flex-end;
+}
diff --git a/src/app/components/confirmation-modal/confirmation-modal.component.ts b/src/app/components/confirmation-modal/confirmation-modal.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..09b6de5727e9d4204b7053e4d6447f31c14f42e8
--- /dev/null
+++ b/src/app/components/confirmation-modal/confirmation-modal.component.ts
@@ -0,0 +1,38 @@
+import { Component, Input, EventEmitter, Output, Renderer2, OnChanges } from '@angular/core';
+
+@Component({
+  selector: 'app-confirmation-modal',
+  templateUrl: './confirmation-modal.component.html',
+  styleUrls: ['./confirmation-modal.component.scss'],
+})
+export class ConfirmationModalComponent implements OnChanges {
+
+  @Input() isOpened: boolean;
+  @Input() texts: {
+    main: string;
+    cancel: string;
+    continue: string;
+  };
+  @Output() cancel = new EventEmitter<boolean>();
+  @Output() continue = new EventEmitter<boolean>();
+
+  constructor(
+    private renderer: Renderer2,
+  ) { }
+
+  ngOnChanges() {
+    if (this.isOpened) {
+      this.renderer.addClass(document.body, 'is-clipped');
+    } else {
+      this.renderer.removeClass(document.body, 'is-clipped');
+    }
+  }
+
+  close() {
+    this.cancel.emit(true);
+  }
+
+  carryOn() {
+    this.continue.emit(true);
+  }
+}
diff --git a/src/app/components/credits/detail/credit-detail.component.html b/src/app/components/credits/detail/credit-detail.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..3bb156fe9c838717d528d354848458737ac55b3b
--- /dev/null
+++ b/src/app/components/credits/detail/credit-detail.component.html
@@ -0,0 +1,43 @@
+<section class="section page-container" *ngIf="credit">
+  <app-page-header [pageInfo]="{title: title}"></app-page-header>
+  <div class="columns is-centered">
+    <div class="column is-8">
+      <div class="card">
+        <header class="card-header">
+          <p class="card-header-title has-text-centered">
+            {{credit.name}}
+          </p>
+        </header>
+        <div class="card-image">
+          <figure class="image">
+            <img [src]="credit.logo" alt="Logo du credit">
+          </figure>
+        </div>
+        <div class="card-content">
+
+          <div class="content">
+            <div>
+              <p>{{credit.description}}</p>
+            </div>
+            <br>
+            <div>
+              <p><span class="has-text-weight-bold">Statut:</span> {{ credit.published ? 'Publié' : 'Brouillon' }}
+              </p>
+            </div>
+            <br>
+            <div>
+              <p><span class="has-text-weight-bold">Id:</span> {{ credit.id}}</p>
+            </div>
+            <br>
+            <div>
+              <p class="has-text-weight-bold">Liens</p>
+              <p *ngFor="let link of credit.links">
+                <a href="{{ link.url }}" target="_blank">{{link.url}}</a>
+              </p>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</section>
\ No newline at end of file
diff --git a/src/app/components/credits/detail/credit-detail.component.scss b/src/app/components/credits/detail/credit-detail.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..63a36307795f8cebd94c864bbbaa3deba1ae7da7
--- /dev/null
+++ b/src/app/components/credits/detail/credit-detail.component.scss
@@ -0,0 +1,13 @@
+figure {
+  text-align: center;
+}
+
+figure img {
+  max-width: 150px;
+  display: inline-block;
+  margin-top: 20px;
+}
+
+.card-header-title {
+  justify-content: center;
+}
diff --git a/src/app/components/credits/detail/credit-detail.component.ts b/src/app/components/credits/detail/credit-detail.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4a707c38d54934939e5494f02d8d86ccb4108d80
--- /dev/null
+++ b/src/app/components/credits/detail/credit-detail.component.ts
@@ -0,0 +1,31 @@
+
+import { switchMap } from 'rxjs/operators';
+import { Component, OnInit } from '@angular/core';
+import { ActivatedRoute, ParamMap } from '@angular/router';
+
+import { Credit } from 'src/app/models/credit.model';
+import { CreditService } from 'src/app/services/credit.service';
+
+@Component({
+  selector: 'app-credit-detail',
+  templateUrl: './credit-detail.component.html',
+  styleUrls: ['./credit-detail.component.scss'],
+})
+export class CreditDetailComponent implements OnInit {
+
+  credit: Credit;
+  title: string;
+
+  constructor(
+    private _route: ActivatedRoute,
+    private _creditService: CreditService,
+  ) {
+  }
+
+  ngOnInit(): void {
+    this.title = this._route.snapshot.data.title;
+    this._route.paramMap.pipe(
+      switchMap((params: ParamMap) => this._creditService.findById(params.get('id'))))
+      .subscribe((credit: Credit) => this.credit = credit);
+  }
+}
diff --git a/src/app/components/credits/edit/credit-form.component.html b/src/app/components/credits/edit/credit-form.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..34fccf9b7361ed73e1bf9ce29be4bb33e7e30f33
--- /dev/null
+++ b/src/app/components/credits/edit/credit-form.component.html
@@ -0,0 +1,91 @@
+<section class="section page-container" *ngIf="credit">
+  <form [formGroup]="form" (ngSubmit)="onSubmit()" class="columns is-centered is-marginless is-multiline">
+    <div class="column is-12 header-with-publication-status">
+      <app-page-header [pageInfo]="{title: title}"></app-page-header>
+      <div class="field status-field">
+        <span class="fake-label" *ngIf="form.get('published').value === true">Publié</span>
+        <span class="fake-label" *ngIf="form.get('published').value === false">Brouillon</span>
+        <input id="published" type="checkbox" formControlName="published" class="switch is-rounded">
+        <label for="published"></label>
+      </div>
+    </div>
+    <div class="column is-7">
+      <input type="hidden" formControlName="id" value="{{credit.id}}">
+
+
+      <div class="field">
+        <label class="label required" for="name">Nom</label>
+        <div class="control">
+          <input class="input" type="text" [value]="credit.name" formControlName="name" id="name" required>
+        </div>
+        <div *ngIf="name.invalid && (name.dirty || name.touched)" class="alert alert-danger">
+          <p *ngIf="name.errors['required']" class="help is-danger">
+            Le nom du crédit est obligatoire.
+          </p>
+        </div>
+      </div>
+
+      <app-image-upload [fieldParams]="logoFieldParams" (fileChanged)="logoChanged($event)"
+        (imageRemoved)="removeLogo()">
+      </app-image-upload>
+
+      <div class="field">
+        <label class="label required" for="description">Description <span class="is-italic has-text-weight-normal">(150
+            caractères max)</span></label>
+        <div class="control">
+          <textarea class="textarea" formControlName="description" id="description" required>
+            {{ credit.description }}
+          </textarea>
+        </div>
+        <div *ngIf="description.invalid && (description.dirty || description.touched)" class="alert alert-danger">
+          <p *ngIf="description.errors['required']" class="help is-danger">
+            La description du crédit est obligatoire.
+          </p>
+          <p *ngIf="description.errors['maxlength']" class="help is-danger">
+            La description ne peut dépasser 150 caractères.
+          </p>
+        </div>
+      </div>
+
+      <div class="field links">
+        <div class="columns">
+          <div class="column is-11">
+            <label class="label">Liens</label>
+          </div>
+          <div class="column is-1">
+            <span class="icon" (click)="addLink()" title="Ajouter un lien">
+              <i class="fas fa-plus"></i>
+            </span>
+          </div>
+        </div>
+
+        <div formArrayName="links">
+          <div *ngFor="let link of formLinks.controls; let i = index;" [formGroupName]="i"
+            class="columns is-multiline field">
+            <div class="column is-11">
+              <div class="control">
+                <input class="input" type="text" formControlName="url" required>
+              </div>
+              <div *ngIf="link['controls'].url.invalid && (link['controls'].url.dirty || link['controls'].url.touched)"
+                class="alert alert-danger">
+                <p *ngIf="link.hasError('required', 'url')" class="help is-danger">
+                  L'URL du lien est obligatoire.
+                </p>
+              </div>
+            </div>
+
+            <div class="column is-1">
+              <span class="icon" (click)="removeLink(i)" title="Supprimer le lien">
+                <i class="fas fa-trash"></i>
+              </span>
+            </div>
+          </div>
+        </div>
+      </div>
+      <br>
+      <div class="has-text-right">
+        <button class="button button-gl" type="submit" [disabled]="formInvalid == true">Valider</button>
+      </div>
+    </div>
+  </form>
+</section>
\ No newline at end of file
diff --git a/src/app/components/credits/edit/credit-form.component.scss b/src/app/components/credits/edit/credit-form.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..0762aa33dcef6109e5521239a03190ee9c3670a1
--- /dev/null
+++ b/src/app/components/credits/edit/credit-form.component.scss
@@ -0,0 +1,25 @@
+.full-width {
+  width: 100%;
+}
+
+.page-container {
+  position: relative;
+}
+
+h1 {
+  text-align: center
+}
+
+.icon {
+  cursor: pointer;
+
+  &:hover {
+    .fa-plus {
+      color: lightblue;
+    }
+
+    .fa-trash {
+      color: #d5232a;
+    }
+  }
+}
diff --git a/src/app/components/credits/edit/credit-form.component.ts b/src/app/components/credits/edit/credit-form.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..160ab094596df2af8528566750c94c91e2e0002f
--- /dev/null
+++ b/src/app/components/credits/edit/credit-form.component.ts
@@ -0,0 +1,240 @@
+import { Component, OnInit } from '@angular/core';
+import { ActivatedRoute, ParamMap, Router } from '@angular/router';
+import { Credit } from 'src/app/models/credit.model';
+import { FormBuilder, FormGroup, Validators, FormArray } from '@angular/forms';
+import { filter, switchMap, mergeMap, catchError } from 'rxjs/operators';
+import { IImageUploadFieldParams } from 'src/app/models/image-upload.model';
+import { NotificationService, MediaService, CreditService } from 'src/app/services';
+import { throwError } from 'rxjs';
+import { Media } from '../../../models/media.model';
+
+@Component({
+  selector: 'app-credit-form',
+  templateUrl: './credit-form.component.html',
+  styleUrls: ['./credit-form.component.scss'],
+})
+export class CreditFormComponent implements OnInit {
+
+  credit: Credit;
+  form: FormGroup;
+  logoFile: File;
+  logoFieldParams: IImageUploadFieldParams = {
+    inputName: 'logo',
+    label: 'Logo',
+    existingImageUrl: null,
+    isRequired: true,
+  };
+  logo: File;
+  title: string;
+
+  constructor(
+    private _creditService: CreditService,
+    private _mediaService: MediaService,
+    private _notificationService: NotificationService,
+    private _route: ActivatedRoute,
+    private _router: Router,
+    private _fb: FormBuilder,
+  ) {
+  }
+
+  ngOnInit() {
+    this.title = this._route.snapshot.data.title;
+    this.initForm();
+
+    this._route.paramMap.pipe(
+      filter((paramMap: ParamMap) => (paramMap.get('id') !== null)),
+      switchMap((paramMap: ParamMap) => this._creditService.findById(paramMap.get('id'))))
+      .subscribe((credit: Credit) => {
+
+        this.credit = credit;
+
+        const arr = new FormArray([]);
+        this.credit.links.forEach((y) => {
+          arr.push(this._fb.group({
+            url: y.url,
+            creditId: y.creditId,
+          }));
+        });
+
+        this.logoFieldParams.existingImageUrl = credit.logo;
+
+        this.form = this._fb.group(
+          {
+            id: [this.credit.id],
+            name: [credit.name, Validators.required],
+            description: [credit.description, [Validators.required, Validators.maxLength(150)]],
+            logo: [credit.logo],
+            links: arr,
+            published: [this.credit.published],
+          });
+      });
+
+  }
+
+  initForm() {
+    this.credit = new Credit();
+    const arr = new FormArray([]);
+    this.logoFieldParams.existingImageUrl = null;
+    this.form = this._fb.group(
+      {
+        id: [this.credit.id],
+        name: [this.credit.name, Validators.required],
+        description: [this.credit.description, [Validators.required, Validators.maxLength(150)]],
+        logo: [this.credit.logo],
+        links: arr,
+        published: [this.credit.published],
+      });
+  }
+
+  initItemLinkForm() {
+    return this._fb.group({
+      name: '',
+      url: ['', Validators.required],
+    });
+  }
+
+  addLink() {
+    if (!this.form.controls.links) {
+      this.credit.links = [];
+    }
+    (this.form.controls.links as FormArray).push(this._fb.group(
+      {
+        id: null, name: '',
+        url: ['', Validators.required],
+        creditId: this.credit.id,
+      }),
+    );
+  }
+
+  removeLink(index) {
+    (this.form.controls.links as FormArray).removeAt(index);
+  }
+
+  get formLinks() {
+    return this.form.controls.links as FormArray;
+  }
+
+  onSubmit() {
+    if (!this.formInvalid) {
+      this.credit = new Credit(this.form.value);
+
+      if (this.credit.id) {
+        if (this.logoFile) {
+          this._mediaService.uploadFile(this.logoFile).pipe(
+            catchError(() => {
+              return throwError('Une erreur est survenue lors de l\'upload du logo du crédit.');
+            }),
+            mergeMap((response: Media) => {
+              this.credit.logo = response.url;
+              return this._creditService.update(this.credit).pipe(
+                catchError(() => {
+                  return throwError('Une erreur est survenue lors de la mise à jour du crédit.');
+                }),
+              );
+            }),
+          ).subscribe(
+            (creditUpdated) => {
+              this._notificationService.notify({
+                message: 'Le crédit a été mis à jour avec succès.',
+                type: 'success',
+              });
+              this._router.navigate(['/credits', creditUpdated.id]);
+            },
+            (err) => {
+              this._notificationService.notify({
+                message: err,
+                type: 'error',
+              });
+            },
+          );
+        } else {
+          return this._creditService.update(this.credit).subscribe(
+            (creditUpdated) => {
+              this._notificationService.notify({
+                message: 'Le crédit a été mis à jour avec succès.',
+                type: 'success',
+              });
+              this._router.navigate(['/credits', creditUpdated.id]);
+            },
+            () => {
+              this._notificationService.notify({
+                message: 'Une erreur est survenue lors de la mise à jour du crédit.',
+                type: 'error',
+              });
+            },
+          );
+        }
+      } else {
+        if (this.logoFile) {
+          this._mediaService.uploadFile(this.logoFile).pipe(
+            catchError(() => {
+              return throwError('Une erreur est survenue lors de l\'upload du logo du crédit.');
+            }),
+            mergeMap((response: Media) => {
+              this.credit.logo = response.url;
+              return this._creditService.create(this.credit).pipe(
+                catchError(() => {
+                  return throwError('Une erreur est survenue lors de la création du crédit.');
+                }),
+              );
+            }),
+          ).subscribe(
+            (creditCreated) => {
+              this._notificationService.notify({
+                message: 'Le crédit a été créé avec succès.',
+                type: 'success',
+              });
+              this._router.navigate(['/credits', creditCreated.id]);
+            },
+            (err) => {
+              this._notificationService.notify({
+                message: err,
+                type: 'error',
+              });
+            },
+          );
+        } else {
+          return this._creditService.create(this.credit).subscribe(
+            (creditUpdated) => {
+              this._notificationService.notify({
+                message: 'Le crédit a été créé avec succès.',
+                type: 'success',
+              });
+              this._router.navigate(['/credits', creditUpdated.id]);
+            },
+            () => {
+              this._notificationService.notify({
+                message: 'Une erreur est survenue lors de la création du crédit.',
+                type: 'error',
+              });
+            },
+          );
+        }
+      }
+    }
+  }
+
+  // Getters for each property
+  get name() {
+    return this.form.controls['name'];
+  }
+
+  get description() {
+    return this.form.controls['description'];
+  }
+
+  get formInvalid() {
+    return this.form.invalid ||
+      (!this.credit.logo && !this.logoFile);
+  }
+
+  logoChanged(fileList: FileList) {
+    if (fileList && fileList.length > 0) {
+      this.logoFile = fileList[0];
+    }
+  }
+
+  removeLogo() {
+    this.form.get('logo').setValue(null);
+  }
+}
diff --git a/src/app/components/credits/list/credits.component.html b/src/app/components/credits/list/credits.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..cd73d71338e91035a7536f3749855c9e8a6f15a0
--- /dev/null
+++ b/src/app/components/credits/list/credits.component.html
@@ -0,0 +1,97 @@
+<div class="section page-container">
+  <app-page-header [pageInfo]="pageHeaderInfo" [hideBackButton]="true"></app-page-header>
+  <div class="add-item-link has-text-right">
+    <a class="button button-gl" [routerLink]="['new']">
+      Ajouter
+    </a>
+  </div>
+  <div class="table entity-list-table" *ngIf="credits">
+    <div class="header columns is-marginless">
+      <div class="column is-2">
+        <span (click)="sortBy('name')" class="is-sortable">
+          <span class="sort-icons">
+            <span class="icon">
+              <i class="fas fa-sort-up"
+                [ngClass]="{'icon-red': sortOptions.value === 'name' && sortOptions.order === 'desc'}"></i>
+            </span>
+            <span class="icon">
+              <i class="fas fa-sort-down"
+                [ngClass]="{'icon-red': sortOptions.value === 'name' && sortOptions.order === 'asc'}"></i>
+            </span>
+          </span>
+          <span class="column-title" [ngClass]="{'active': sortOptions.value === name}">Nom</span>
+        </span>
+      </div>
+      <div class="column is-1 has-text-centered">
+        <span class="column-title">Publié</span>
+      </div>
+      <div class="column is-1 has-text-centered">
+        <span class="column-title">Logo</span>
+      </div>
+      <div class="column is-4">
+        <span (click)="sortBy('description')" class="is-sortable">
+          <span class="sort-icons">
+            <span class="icon">
+              <i class="fas fa-sort-up"
+                [ngClass]="{'icon-red': sortOptions.value === 'description' && sortOptions.order === 'desc'}"></i>
+            </span>
+            <span class="icon">
+              <i class="fas fa-sort-down"
+                [ngClass]="{'icon-red': sortOptions.value === 'description' && sortOptions.order === 'asc'}"></i>
+            </span>
+          </span>
+          <span class="column-title">Description</span>
+        </span>
+      </div>
+      <div class="column is-3">
+        <span class="column-title">Liens</span>
+      </div>
+      <div class="column is-1 has-text-centered">
+        <span class="column-title">Actions</span>
+      </div>
+    </div>
+    <div class="data-list">
+      <div class="data columns is-multiline is-vcentered is-marginless"
+        *ngFor="let credits of credits; let i=index; let odd=odd; let even=even;" [ngClass]="{ odd: odd, even: even }">
+        <div class="column is-2">
+          <span>{{ credits.name}}</span>
+        </div>
+        <div class="column is-1 has-text-centered">
+          <span class="icon has-text-success" *ngIf="credits.published">
+            <i class="far fa-check-circle"></i>
+          </span>
+          <span class="icon has-text-danger" *ngIf="!credits.published">
+            <i class="far fa-times-circle"></i>
+          </span>
+        </div>
+        <div class="column is-1 has-text-centered">
+          <img class="entity-logo-in-list" [src]="credits.logo" alt="">
+        </div>
+        <div class="column is-4">
+          <span>{{ credits.description | slice:0:200}}...</span>
+        </div>
+        <div class="column is-3">
+          <p *ngFor="let link of credits.links">
+            <a href="{{ link.url }}" target="_blank">{{link.url}}</a>
+          </p>
+        </div>
+        <div class="column is-1 has-text-centered actions">
+          <app-crud-buttons [id]="credits.id" (delete)="displayDeletePopup($event)"></app-crud-buttons>
+        </div>
+
+      </div>
+    </div>
+    <div class="columns is-marginless paginator">
+      <div class="column">
+        <app-paginator *ngIf="paginator.length > 0" [length]="paginator.length" [pageSize]="paginator.limit"
+          [pageSizeOptions]="paginator.pageSizeOptions" [pageIndex]="paginator.pageIndex" [pagesToShow]="5"
+          [showFirstLastButtons]="true" (page)="changePagination($event)" (pageSizeChanged)="changePageSize($event)">
+        </app-paginator>
+      </div>
+    </div>
+  </div>
+</div>
+
+<app-confirmation-modal (cancel)="objectToBeDeletedId=null" (continue)="deleteCredit()" [texts]="deleteModalTexts"
+  [isOpened]="objectToBeDeletedId !== null">
+</app-confirmation-modal>
\ No newline at end of file
diff --git a/src/app/components/credits/list/credits.component.scss b/src/app/components/credits/list/credits.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..d1ab57677f61171791f67b163f6b933ef1dd4a41
--- /dev/null
+++ b/src/app/components/credits/list/credits.component.scss
@@ -0,0 +1,3 @@
+img {
+  max-width: 100px;
+}
diff --git a/src/app/components/credits/list/credits.component.ts b/src/app/components/credits/list/credits.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d417e76fe35c8d1eba3c39f18c7f628a89c58bb9
--- /dev/null
+++ b/src/app/components/credits/list/credits.component.ts
@@ -0,0 +1,142 @@
+import { Component, OnInit, OnDestroy } from '@angular/core';
+import { Subscription } from 'rxjs';
+import { PaginatorOptions } from 'src/app/models/paginator-options.model';
+import { IPageHeaderInfo } from '../../../models/page.model';
+import { NotificationService, CreditService } from '../../../services';
+import { Credit, CreditRO } from '../../../models/credit.model';
+
+@Component({
+  selector: 'app-credits',
+  templateUrl: './credits.component.html',
+  styleUrls: ['./credits.component.scss'],
+})
+export class CreditsComponent implements OnInit, OnDestroy {
+
+  pageHeaderInfo: IPageHeaderInfo = {
+    title: '',
+  };
+  objectToBeDeletedId = null;
+  deleteModalTexts = {
+    main: 'Si vous poursuivez, le crédit sera définitivement supprimé.',
+    cancel: 'Annuler',
+    continue: 'Supprimer',
+  };
+  credits: Credit[] = [];
+  searchChangeSub: Subscription;
+
+  // Paginator options
+  paginator: PaginatorOptions;
+
+  sortValue: string;
+
+  totalElement: number;
+  pageSize = 10;
+  pageSizeOptions = [5, 10, 25, 100];
+  filters = {
+    name: '',
+  };
+  where = {};
+
+  constructor(
+    private _creditService: CreditService,
+    private _notificationService: NotificationService,
+  ) {
+    this.paginator = {
+      pageIndex: this._creditService.pageNumber,
+      length: 0,
+      limit: this._creditService.limit,
+      pageSizeOptions: [5, 10, 20],
+    };
+  }
+
+  ngOnInit(): void {
+    this._creditService.sortOptions = {
+      value: 'name',
+      order: 'asc',
+    };
+    this.search();
+
+    this.searchChangeSub = this._creditService.searchChange$.subscribe(
+      () => {
+        this.search();
+      },
+    );
+  }
+
+  private search() {
+    this._creditService.getCredits()
+      .subscribe(
+        (items: CreditRO) => {
+          this.credits = items.credits;
+          this.totalElement = items.totalCount;
+
+          this.pageHeaderInfo.title = this.totalElement > 1 ?
+            `${this.totalElement} crédits trouvés` : `${this.totalElement} crédit trouvé`;
+
+          this.paginator.limit = this._creditService.limit;
+          this.paginator.pageIndex = this._creditService.pageNumber;
+          this.paginator.length = items.totalCount;
+        },
+        () => {
+          this.pageHeaderInfo.title = '0 crédit trouvé';
+          this._notificationService.notify({
+            type: 'error',
+            message: 'Une erreur est survenue lors du chargement des crédits.',
+          });
+        },
+      );
+  }
+
+  // When pagination is changed by user, we update datasetList with new pagination options
+  changePagination(pageIndex) {
+    this._creditService.paginationChanged(this.paginator.limit, pageIndex);
+  }
+
+  changePageSize(pageSize) {
+    this._creditService.paginationChanged(pageSize, 1);
+  }
+
+  sortBy(key: string) {
+    if (this._creditService.sortOptions.value === key) {
+      this._creditService.reverseSortOrder();
+    } else {
+      this._creditService.sortOptions.value = key;
+      this._creditService.sortOptions.order = 'asc';
+    }
+    this.search();
+  }
+
+  get sortOptions() {
+    return this._creditService.sortOptions;
+  }
+
+  displayDeletePopup(id) {
+    this.objectToBeDeletedId = id;
+  }
+
+  deleteCredit() {
+    this._creditService.delete(this.objectToBeDeletedId).subscribe(
+      () => {
+        this._notificationService.notify({
+          type: 'success',
+          message: 'Le crédit a été supprimé avec succès.',
+        });
+        this._creditService.pageNumber = 1;
+        this.search();
+      },
+      () => {
+        this._notificationService.notify({
+          type: 'error',
+          message: 'Une erreur est survenue lors de la suppression du crédit.',
+        });
+      },
+      () => {
+        this.objectToBeDeletedId = null;
+      },
+    );
+  }
+
+  ngOnDestroy() {
+    this.searchChangeSub.unsubscribe();
+  }
+}
diff --git a/src/app/components/formats/edit/format-form.component.ts b/src/app/components/formats/edit/format-form.component.ts
index cd1a1d6d67dbb99887d6434e6e58f933e098cc05..e494f7ab5ae5361b6d0c1814408578d886fb707c 100644
--- a/src/app/components/formats/edit/format-form.component.ts
+++ b/src/app/components/formats/edit/format-form.component.ts
@@ -3,7 +3,7 @@ import { ActivatedRoute, ParamMap, Router } from '@angular/router';
 import { FormBuilder, FormGroup, Validators } from '@angular/forms';
 import { filter, switchMap } from 'rxjs/operators';
 import { Format } from 'src/app/models/format.model';
-import { FormatService } from 'src/app/services';
+import { FormatService, NotificationService } from 'src/app/services';
 
 @Component({
   selector: 'app-format-form',
@@ -21,6 +21,7 @@ export class FormatFormComponent implements OnInit {
     private _route: ActivatedRoute,
     private _router: Router,
     private _fb: FormBuilder,
+    private _notificationService: NotificationService,
   ) {
   }
 
@@ -52,15 +53,39 @@ export class FormatFormComponent implements OnInit {
   onSubmit() {
     if (!this.formInvalid) {
       this.format = new Format(this.form.value);
-      this._formatService.replaceOrCreate(this.format)
-        .subscribe(
+      if (this.format.id) {
+        this._formatService.update(this.format).subscribe(
+          (formatUpdate) => {
+            this._notificationService.notify({
+              type: 'success',
+              message: 'Le format a bien été mis à jour.',
+            });
+            this._router.navigate(['/formats', formatUpdate.id]);
+          },
+          () => {
+            this._notificationService.notify({
+              type: 'error',
+              message: 'Une erreur est survenue lors de la mise à jour du format.',
+            });
+          },
+        );
+      } else {
+        this._formatService.create(this.format).subscribe(
           (formatCreated) => {
+            this._notificationService.notify({
+              type: 'success',
+              message: 'Le format a bien été créé.',
+            });
             this._router.navigate(['/formats', formatCreated.id]);
           },
-          (err) => {
-            alert(err.message);
+          () => {
+            this._notificationService.notify({
+              type: 'error',
+              message: 'Une erreur est survenue lors de la création du format.',
+            });
           },
         );
+      }
     }
   }
 
diff --git a/src/app/components/formats/list/formats.component.html b/src/app/components/formats/list/formats.component.html
index ff125702863e6c03ef08f1cf50fd1958f2101b5a..987f71d2484d077424d82ae18e4de0f52e8596c8 100644
--- a/src/app/components/formats/list/formats.component.html
+++ b/src/app/components/formats/list/formats.component.html
@@ -1,11 +1,11 @@
-<section class="section page-container" *ngIf="formats">
+<section class="section page-container">
   <app-page-header [pageInfo]="pageHeaderInfo" [hideBackButton]="true"></app-page-header>
   <div class="add-item-link has-text-right">
     <a class="button button-gl" [routerLink]="['new']">
       Ajouter
     </a>
   </div>
-  <div class="table entity-list-table">
+  <div class="table entity-list-table" *ngIf="formats">
     <div class="header columns is-marginless">
       <div class="column is-2">
         <span (click)="sortBy('name')" class="is-sortable">
@@ -84,4 +84,8 @@
       </div>
     </div>
   </div>
-</section>
\ No newline at end of file
+</section>
+
+<app-confirmation-modal (cancel)="objectToBeDeletedId=null" (continue)="deleteFormat()" [texts]="deleteModalTexts"
+  [isOpened]="objectToBeDeletedId !== null">
+</app-confirmation-modal>
\ No newline at end of file
diff --git a/src/app/components/formats/list/formats.component.ts b/src/app/components/formats/list/formats.component.ts
index 950708a57bc2a87bbf015edbf48ff40fb99248a6..93c3b7ad6b10e15a7e6fc0ce399497aab3ff7bd4 100644
--- a/src/app/components/formats/list/formats.component.ts
+++ b/src/app/components/formats/list/formats.component.ts
@@ -1,21 +1,28 @@
-import { Component, OnInit } from '@angular/core';
+import { Component, OnInit, OnDestroy } from '@angular/core';
 import { FormatService } from 'src/app/services/format.service';
 import { Subscription } from 'rxjs';
 import { PaginatorOptions } from 'src/app/models/paginator-options.model';
 import { Format, FormatRO } from 'src/app/models/format.model';
 import { IPageHeaderInfo } from '../../../models/page.model';
+import { NotificationService } from '../../../services';
 
 @Component({
   selector: 'app-formats',
   templateUrl: './formats.component.html',
   styleUrls: ['./formats.component.scss'],
 })
-export class FormatsComponent implements OnInit {
+export class FormatsComponent implements OnInit, OnDestroy {
 
   pageHeaderInfo: IPageHeaderInfo = {
     title: '',
   };
-  formats: Format[];
+  objectToBeDeletedId = null;
+  deleteModalTexts = {
+    main: 'Si vous poursuivez, le format sera définitivement supprimé.',
+    cancel: 'Annuler',
+    continue: 'Supprimer',
+  };
+  formats: Format[] = [];
   searchChangeSub: Subscription;
 
   // Paginator options
@@ -32,24 +39,25 @@ export class FormatsComponent implements OnInit {
   where = {};
 
   constructor(
-    private formatsService: FormatService,
+    private _formatService: FormatService,
+    private _notificationService: NotificationService,
   ) {
     this.paginator = {
-      pageIndex: this.formatsService.pageNumber,
+      pageIndex: this._formatService.pageNumber,
       length: 0,
-      limit: this.formatsService.limit,
+      limit: this._formatService.limit,
       pageSizeOptions: [5, 10, 20],
     };
   }
 
   ngOnInit(): void {
-    this.formatsService.sortOptions = {
+    this._formatService.sortOptions = {
       value: 'name',
       order: 'asc',
     };
     this.search();
 
-    this.searchChangeSub = this.formatsService.searchChange$.subscribe(
+    this.searchChangeSub = this._formatService.searchChange$.subscribe(
       () => {
         this.search();
       },
@@ -57,49 +65,81 @@ export class FormatsComponent implements OnInit {
   }
 
   private search() {
-    this.formatsService.getFormats()
-      .subscribe((items: FormatRO) => {
-        this.formats = items.formats;
-        this.totalElement = items.totalCount;
-
-        this.pageHeaderInfo.title = `${this.totalElement} formats trouvés`;
-
-        this.paginator.limit = this.formatsService.limit;
-        this.paginator.pageIndex = this.formatsService.pageNumber;
-        this.paginator.length = items.totalCount;
-      });
+    this._formatService.getFormats()
+      .subscribe(
+        (items: FormatRO) => {
+          this.formats = items.formats;
+          this.totalElement = items.totalCount;
+
+          this.pageHeaderInfo.title = `${this.totalElement} formats trouvés`;
+          this.pageHeaderInfo.title = this.totalElement > 1 ?
+            `${this.totalElement} formats trouvés` :
+            `${this.totalElement} format trouvé`;
+
+          this.paginator.limit = this._formatService.limit;
+          this.paginator.pageIndex = this._formatService.pageNumber;
+          this.paginator.length = items.totalCount;
+        },
+        () => {
+          this.pageHeaderInfo.title = '0 format trouvé';
+          this._notificationService.notify({
+            type: 'error',
+            message: 'Une erreur est survenue lors du chargement des formats.',
+          });
+        },
+      );
   }
 
   // When pagination is changed by user, we update datasetList with new pagination options
   changePagination(pageIndex) {
-    this.formatsService.paginationChanged(this.paginator.limit, pageIndex);
+    this._formatService.paginationChanged(this.paginator.limit, pageIndex);
   }
 
   changePageSize(pageSize) {
-    this.formatsService.paginationChanged(pageSize, 1);
+    this._formatService.paginationChanged(pageSize, 1);
   }
 
   sortBy(key: string) {
-    if (this.formatsService.sortOptions.value === key) {
-      this.formatsService.reverseSortOrder();
+    if (this._formatService.sortOptions.value === key) {
+      this._formatService.reverseSortOrder();
     } else {
-      this.formatsService.sortOptions.value = key;
-      this.formatsService.sortOptions.order = 'asc';
+      this._formatService.sortOptions.value = key;
+      this._formatService.sortOptions.order = 'asc';
     }
     this.search();
   }
 
   get sortOptions() {
-    return this.formatsService.sortOptions;
+    return this._formatService.sortOptions;
+  }
+
+  displayDeletePopup(id) {
+    this.objectToBeDeletedId = id;
   }
 
-  displayDeletePopup(formatId) {
-    const pop = confirm('Etes vous sûr de vouloir supprimer ce format ?');
-    if (pop) {
-      this.formatsService.delete(formatId).subscribe(() => {
-        this.formatsService.pageNumber = 1;
+  deleteFormat() {
+    this._formatService.delete(this.objectToBeDeletedId).subscribe(
+      () => {
+        this._notificationService.notify({
+          type: 'success',
+          message: 'Le format a été supprimé avec succès.',
+        });
+        this._formatService.pageNumber = 1;
         this.search();
-      });
-    }
+      },
+      () => {
+        this._notificationService.notify({
+          type: 'error',
+          message: 'Une erreur est survenue lors de la suppression du format.',
+        });
+      },
+      () => {
+        this.objectToBeDeletedId = null;
+      },
+    );
+  }
+
+  ngOnDestroy() {
+    this.searchChangeSub.unsubscribe();
   }
 }
diff --git a/src/app/components/image-upload/image-upload.component.scss b/src/app/components/image-upload/image-upload.component.scss
index d4a9d5613206de3aa75a786ce5adcc2bb26cfa48..b0d727c7ce9831cd3f3e45564d4111d7744d33e5 100644
--- a/src/app/components/image-upload/image-upload.component.scss
+++ b/src/app/components/image-upload/image-upload.component.scss
@@ -2,6 +2,10 @@
   display: inline-block;
   position: relative;
   max-height: 5rem;
+
+  img {
+    max-height: 5rem;
+  }
 }
 
 button {
diff --git a/src/app/components/index.ts b/src/app/components/index.ts
index 90f5e966751673233933f0a7cb01fc0caef66634..24f8f061a7ae06f7172508b4591f8816c511090b 100644
--- a/src/app/components/index.ts
+++ b/src/app/components/index.ts
@@ -13,7 +13,32 @@ import { FormatDetailComponent } from './formats/detail/format-detail.component'
 import { FormatFormComponent } from './formats/edit/format-form.component';
 import { ImageUploadComponent } from './image-upload/image-upload.component';
 import { NotificationsComponent } from './notifications/notifications.component';
+import { LogsInfoComponent } from './logs-dashboard/report/logs-info/logs-info.component';
+import { LogsErrorComponent } from './logs-dashboard/report/logs-error/logs-error.component';
+import { LogsGraphComponent } from './logs-dashboard/report/logs-graph/logs-graph.component';
+import { LogsSessionsComponent } from './logs-dashboard/logs-sessions/logs-sessions.component';
+import { LogsSlugsComponent } from './logs-dashboard/logs-slugs/logs-slugs.component';
+import { FilterPipe } from './logs-dashboard/logs-slugs/filter.pipe';
+import { LogsPreReportComponent } from './logs-dashboard/report/logs-pre-report/logs-pre-report.component';
+import { LogsReportComponent } from './logs-dashboard/report/logs-report/logs-report.component';
+import { LogsHomeComponent } from './logs-dashboard/logs-home/logs-home.component';
 import { PageHeaderComponent } from './page-header/page-header.component';
+import { ChangelogComponent } from './changelog/list/changelog.component';
+import { ChangelogDetailComponent } from './changelog/detail/changelog-detail.component';
+import { ChangelogFormComponent } from './changelog/edit/changelog-form.component';
+import { ConfirmationModalComponent } from './confirmation-modal/confirmation-modal.component';
+import { CreditDetailComponent } from './credits/detail/credit-detail.component';
+import { CreditFormComponent } from './credits/edit/credit-form.component';
+import { CreditsComponent } from './credits/list/credits.component';
+import { ReusesComponent } from './reuses/list/reuses.component';
+import { ReuseFormComponent } from './reuses/edit/reuse-form.component';
+import { ReuseDetailComponent } from './reuses/detail/reuse-detail.component';
+import { ProjectionsComponent } from './projections/list/projections.component';
+import { ProjectionDetailComponent } from './projections/detail/projection-detail.component';
+import { ProjectionFormComponent } from './projections/edit/projection-form.component';
+import { MediaComponent } from './media/list/media.component';
+import { MediaFormComponent } from './media/edit/media-form.component';
+import { MediaDetailComponent } from './media/detail/media-detail.component';
 
 export {
   MenuComponent,
@@ -31,7 +56,32 @@ export {
   FormatFormComponent,
   ImageUploadComponent,
   NotificationsComponent,
+  LogsInfoComponent,
+  LogsErrorComponent,
+  LogsGraphComponent,
+  LogsSessionsComponent,
+  LogsSlugsComponent,
+  LogsPreReportComponent,
+  LogsReportComponent,
+  LogsHomeComponent,
   PageHeaderComponent,
+  FilterPipe,
+  ChangelogComponent,
+  ChangelogDetailComponent,
+  ChangelogFormComponent,
+  ConfirmationModalComponent,
+  CreditDetailComponent,
+  CreditFormComponent,
+  CreditsComponent,
+  ReuseDetailComponent,
+  ReuseFormComponent,
+  ReusesComponent,
+  ProjectionsComponent,
+  ProjectionDetailComponent,
+  ProjectionFormComponent,
+  MediaComponent,
+  MediaFormComponent,
+  MediaDetailComponent,
 };
 
 // tslint:disable-next-line:variable-name
@@ -51,5 +101,29 @@ export const AppComponents = [
   FormatFormComponent,
   ImageUploadComponent,
   NotificationsComponent,
+  LogsInfoComponent,
+  LogsErrorComponent,
+  LogsGraphComponent,
+  LogsSessionsComponent,
+  LogsSlugsComponent,
+  LogsPreReportComponent,
+  LogsReportComponent,
+  LogsHomeComponent,
   PageHeaderComponent,
+  ChangelogComponent,
+  ChangelogDetailComponent,
+  ChangelogFormComponent,
+  ConfirmationModalComponent,
+  CreditDetailComponent,
+  CreditFormComponent,
+  CreditsComponent,
+  ReuseDetailComponent,
+  ReuseFormComponent,
+  ReusesComponent,
+  ProjectionsComponent,
+  ProjectionDetailComponent,
+  ProjectionFormComponent,
+  MediaComponent,
+  MediaFormComponent,
+  MediaDetailComponent,
 ];
diff --git a/src/app/components/logs-dashboard/logs-home/logs-home.component.html b/src/app/components/logs-dashboard/logs-home/logs-home.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..fee2694bc8bd11d8afd6c89dd221c02709578098
--- /dev/null
+++ b/src/app/components/logs-dashboard/logs-home/logs-home.component.html
@@ -0,0 +1,56 @@
+<div class="section page-container">
+  <app-page-header [pageInfo]="pageHeaderInfo" [hideBackButton]="true"></app-page-header>
+  <div *ngIf="isRestheartDown">
+    <h3> {{serverDownMessage}}</h3>
+  </div>
+  <div *ngIf="!isRestheartDown">
+    <div class="columns is-centered is-marginless">
+
+        
+      <div class="input-field">
+        <form>
+          <div class="table">
+            <div class="data-list">
+              <div class="data columns is-multiline is-vcentered is-marginless">
+                  <!-- <div class="column is-12 has-text-left is marginless">
+                      <a (click)="information()">Information</a>
+                  </div> -->
+                <div class="column is-5 has-text-left">
+                  <label class="label"> Slug/Uuid </label>
+                  <input class="input" type="text" size="45" name="slug" [(ngModel)]="slug" list="slugsList">
+                </div>
+                <div class="column is-5 has-text-left">
+                  <label class="label"> Session Id </label>
+                  <input class="input" type="text" size="45" name="sessionId" [(ngModel)]="sessionId" list="sessionsList">
+                </div>
+                <div class="column is-2 has-text-left">
+                  <label class="label"> &nbsp;</label>
+
+                  <a class="button button-gl" (click)="trimAndNavigate(slug, sessionId)">
+                    <i class="fas fa-search"></i>
+                  </a>
+                </div>
+              </div>
+            </div>
+          </div>
+        </form>
+        <br>
+
+        <!-- Tabs -->
+        <ul class="tabs-container">
+          <li *ngFor="let tab of tabs" (click)="tabsToggler(tab.name)" [ngClass]="{'is-active':tab.isActive}">
+            <span class="tab-label">{{ tab.name }}</span>
+          </li>
+        </ul>
+      </div>
+    </div>
+    <div class="columns is-centered is-marginless">
+      <div class="column has-text-centered is-marginless">
+        <app-logs-sessions [hidden]="isToggled" [sessionsList]="sessionsList" [childNbSessions]="nbSessionId">
+        </app-logs-sessions>
+        <app-logs-slugs [hidden]="!isToggled" [slugsList]="slugsList" [childNbSlugs]="nbSlug"></app-logs-slugs>
+      </div>
+    </div>
+  </div>
+</div>
+<app-confirmation-modal [isOpened]="isInfoOpened" [texts]= "texts" (continue)= "isInfoOpened"></app-confirmation-modal>
\ No newline at end of file
diff --git a/src/app/components/logs-dashboard/logs-home/logs-home.component.scss b/src/app/components/logs-dashboard/logs-home/logs-home.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..5ce2ea74326a2bb2f4f2f4ff033dca6dd38979e9
--- /dev/null
+++ b/src/app/components/logs-dashboard/logs-home/logs-home.component.scss
@@ -0,0 +1,116 @@
+@import "../../../../scss/variables.scss";
+@import "../../../../../node_modules/bulma/sass/utilities/_all.sass";
+
+figure {
+    text-align: center;
+  }
+  figure img{
+    max-width: 9.3rem;
+    display: inline-block;
+    margin-top: 1.25rem;
+  }
+  
+  .card-header-title {
+    justify-content: center;
+  }
+
+  .mini-log-card{
+    margin-top: 1.25rem;
+    padding: 1.25rem;
+    background-color: yellow;
+
+  }
+  .input-field{
+    width: 200%;
+    margin-top: 1.25rem;
+  }
+
+  .hidden {
+    display: none;
+  }
+  .icon {
+    cursor: pointer;
+    &:hover {
+      .fa-plus {
+        color: lightblue;
+      }
+      .fa-trash {
+        color: #d5232a;
+      }
+    }
+  }
+  .tabs-container {
+  display: flex;
+  align-content: stretch;
+  flex-wrap: wrap;
+  border-bottom: 1px solid $grey-super-light-color;
+
+  li {
+    display: flex;
+    align-items: center;
+    padding-right: 2rem;
+    padding-bottom: 0.4rem;
+    padding-top: 0.4rem;
+    cursor: pointer;
+    position: relative;
+    margin-bottom: 1px;
+    width: 50%;
+
+    @media screen and (min-width: $tablet) {
+      width: 33%;
+      padding-right: 3rem;
+    }
+
+    @media screen and (min-width: $desktop) {
+      width: 20%;
+    }
+  }
+
+  li:last-child {
+    padding-right: 0;
+  }
+
+  .tab-label {
+    font-size: 1rem;
+    font-weight: 500;
+    color: $brand-color;
+  }
+
+  li:hover {
+    .tab-label {
+      font-weight: 700;
+    }
+  }
+
+  li:focus{
+    .tab-label {
+      color: $tomato-color;
+      font-weight: 700;
+    }
+  }
+  li.is-active {
+    .tab-label {
+      color: $tomato-color;
+      font-weight: 600;
+    }
+  }
+
+  li::after {
+    content: '';
+    display: block;
+    height: .1rem;
+    background: $tomato-color;
+    width: 0;
+    position: absolute;
+    bottom: -.1rem;
+  }
+
+  li.is-active::after {
+    transition: width 0.3s;
+    width: 100%;
+  }
+
+  .button-div{
+    padding: 1.25rem ;
+  }
+}
diff --git a/src/app/components/logs-dashboard/logs-home/logs-home.component.ts b/src/app/components/logs-dashboard/logs-home/logs-home.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..995a53f1b682a3f8a2c39a838bbcbce52a4208bd
--- /dev/null
+++ b/src/app/components/logs-dashboard/logs-home/logs-home.component.ts
@@ -0,0 +1,164 @@
+import { Component, OnInit } from '@angular/core';
+import { DataLogsService } from 'src/app/services/data-logs.service';
+import { ActivatedRoute, Router } from '@angular/router';
+import { ILogs } from 'src/app/models/logs.model';
+import { ISimpleTab } from 'src/app/models/basic-tabs.model';
+import { IPageHeaderInfo } from 'src/app/models/page.model';
+import { NotificationService } from 'src/app/services';
+@Component({
+  selector: 'app-logs-home',
+  templateUrl: './logs-home.component.html',
+  styleUrls: ['./logs-home.component.scss'],
+})
+export class LogsHomeComponent implements OnInit {
+  pageHeaderInfo: IPageHeaderInfo = {
+    title: "Logs d'indexation: recherche",
+  };
+  isUuid: boolean = false;
+  slugsList: any;
+  serverDownMessage: string = '';
+  sessionsList: any;
+  nbSessionId: number;
+  nbUuid: number;
+  isInfoOpened: boolean = false;
+  texts: Object = {
+    main : 'hello',
+    cancel: 'end',
+    continue: 'ok',
+  };
+  nbSlug: number;
+  isToggled: boolean = true;
+  data: any = [];
+  uuid: string = 'c1b069ca-181d-4265-9838-8d182f207bd3';
+  sessionId: string = '372b59a4-72b0-4a3a-b518-843e516da0cd';
+  slug: any = 'arbres-alignement-metropole-lyon';
+  allStepsData: any = [];
+  allFields: any = [];
+  isRestheartDown: boolean = true;
+  tabs: ISimpleTab[] =  [{
+    name: 'Slug',
+    isActive: true},
+  {
+    name: 'Session',
+    isActive: false},
+  ];
+  constructor(
+    private _dataLogsService: DataLogsService,
+    private _notificationService: NotificationService,
+    private _route: ActivatedRoute,
+    private _router: Router,
+  ) { }
+
+  ngOnInit() {
+    this.getAllUniqueFields();
+  }
+
+  getAllUniqueFields() {
+    this._dataLogsService.getAllUniqueFields().subscribe(
+      (result) => {
+        this.allFields = result[0];
+        this.nbSessionId = result[0]['session_id_list'].length;
+        this.nbUuid = result[0]['uuid_list'].length;
+        this.nbSlug = result[0]['slug_list'].length;
+        this.slugsList = result[0]['slug_list'];
+        this.sessionsList = result[0]['session_id_list'];
+        this.isRestheartDown = false;
+      },
+      (error) => {
+        this._notificationService.notify(
+          {
+            type: 'error',
+            message: "Le serveur restheart n'est pas disponible.",
+          });
+        this.allFields = [];
+        this.nbSessionId = 0;
+        this.nbUuid = 0;
+        this.nbSlug = 0;
+        this.slugsList = [];
+        this.sessionsList = [];
+        this.serverDownMessage = "Le serveur restheart n'est pas disponible.";
+      });
+  }
+  tabsToggler(tabName) {
+    if (tabName === 'Session') {
+      this.isToggled = false;
+      this.tabs[0].isActive = false;
+      this.tabs[1].isActive = true;
+    }
+    if (tabName === 'Slug') {
+      this.isToggled = true;
+      this.tabs[0].isActive = true;
+      this.tabs[1].isActive = false;
+    }
+  }
+
+  information() {
+    this.isInfoOpened = true;
+  }
+
+  trimAndNavigate(slugOrUuid, sessionId) {
+    const trimmedSlugOrUuid = slugOrUuid.trim();
+    this.sessionId = sessionId.trim();
+    this.uuidDetectorAndNavigate(trimmedSlugOrUuid);
+  }
+
+  uuidDetectorAndNavigate(slugOrUuid) {
+    const regexp = new RegExp('^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$');
+    this.isUuid = regexp.test(slugOrUuid);
+    if (this.isUuid) {
+      this.convertUuidToSlugAndNavigate(slugOrUuid);
+    } else {
+      this.mightyGeneralRouter(slugOrUuid, this.sessionId);
+    }
+  }
+
+  convertUuidToSlugAndNavigate(uuidInput) {
+    this._dataLogsService.getSlugFromUuid(uuidInput).subscribe(
+      (result) => {
+        this.slug = result;
+        this.mightyGeneralRouter(this.slug, this.sessionId);
+      },
+    );
+  }
+
+  mightyGeneralRouter(slug, sessionId) {
+    let slugUuidIsEmpty = true;
+    let sessionIdIsEmpty = true;
+
+    if (slug !== '') {
+      slugUuidIsEmpty = false;
+    }
+    if (sessionId !== '') {
+      sessionIdIsEmpty = false;
+    }
+
+    if (slugUuidIsEmpty && !sessionIdIsEmpty) {
+      this.goToSessionPage(sessionId);
+    }
+    if (!slugUuidIsEmpty && sessionIdIsEmpty) {
+      this.goToSlugPage(slug);
+    }
+    if (slugUuidIsEmpty && sessionIdIsEmpty) {
+      return;
+    }
+    if (!slugUuidIsEmpty && !sessionIdIsEmpty) {
+      this.goToFinalReportPage(slug, sessionId);
+    }
+
+  }
+
+  goToSessionPage(sessionId) {
+    this._router.navigate([this._route, 'preReport', 'session', sessionId]);
+
+  }
+
+  goToSlugPage(slug) {
+    this._router.navigate([this._route, 'preReport', 'slug', slug]);
+
+  }
+
+  goToFinalReportPage (slug, sessionId) {
+    this._router.navigate([this._route, 'report', slug, sessionId, 'home']);
+  }
+
+}
diff --git a/src/app/components/logs-dashboard/logs-sessions/logs-sessions.component.html b/src/app/components/logs-dashboard/logs-sessions/logs-sessions.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..39c3dd673866fe866c14a822a64f6e24414d1013
--- /dev/null
+++ b/src/app/components/logs-dashboard/logs-sessions/logs-sessions.component.html
@@ -0,0 +1,154 @@
+<div class="input-field is-centered">
+  <div class="columns is-5 is-vcentered">
+    <div class="column is-5 has-text-left">
+      <label class="label"> Recherche par session: </label>
+      <input class="input" type="text" [(ngModel)]="foundSession" (keyup)="getAllInfoForOneSession($event)"
+        id="foundSession" list="sessionsList" />
+      <datalist id="sessionsList">
+        <option *ngFor="let session of sessionsList" [value]="session"> {{session}}</option>
+      </datalist>
+    </div>
+  </div>
+</div>
+
+<div class="columns is-centered is-marginless">
+  <div class="column is-6 has-text-left">
+    <p>{{ childNbSessions }} session(s) dans la base de données</p>
+  </div>
+  <div class="column is-6 has-text-left">
+    <p>Pour chaque session, les valeurs affichées concernent le slug le plus récent.</p>
+  </div>
+</div>
+
+<div class="table entity-list-table">
+  <div class="header columns is-marginless">
+    <div class="column is-4 has-text-centered">
+      <span class="is-sortable">
+        <span class="column-title">SessionId</span>
+        <p></p>
+      </span>
+    </div>
+    <div class="column is-1 has-text-left">
+      <span class="is-sortable">
+<!--        <span class="sort-icons">-->
+<!--          <span class="icon">-->
+<!--            <i class="fas fa-sort-up"></i>-->
+<!--          </span>-->
+<!--          <span class="icon">-->
+<!--            <i class="fas fa-sort-down"></i>-->
+<!--          </span>-->
+<!--        </span>-->
+        <span class="column-title">Slugs</span>
+      </span>
+    </div>
+    <div class="column is-2 has-text-left">
+      <span class="is-sortable">
+        <span class="sort-icons">
+          <span class="icon">
+            <i class="fas fa-sort-up"
+              (click)="sortInfos('_id.slug', -1, 'latest.completionDate', -1, this.childNbSessions, 1);"></i>
+          </span>
+          <span class="icon">
+            <i class="fas fa-sort-down"
+              (click)="sortInfos('_id.slug', -1, 'latest.completionDate', 1, this.childNbSessions, 1);"></i>
+          </span>
+        </span>
+        <span class="column-title">Dernière date d'exécution</span>
+      </span>
+    </div>
+    <div class="column is-1 has-text-left">
+      <span class="is-sortable">
+<!--        <span class="sort-icons">-->
+<!--          <span class="icon">-->
+<!--            <i class="fas fa-sort-up"-->
+<!--              (click)="sortInfos('_id.slug', -1, 'latest.duration', -1, this.childNbSessions, 1);"></i>-->
+<!--          </span>-->
+<!--          <span class="icon">-->
+<!--            <i class="fas fa-sort-down"-->
+<!--              (click)="sortInfos('_id.slug', -1, 'latest.duration', 1, this.childNbSessions, 1);"></i>-->
+<!--          </span>-->
+<!--        </span>-->
+        <span class="column-title">Durée</span>
+      </span>
+    </div>
+    <div class="column  is-1 has-text-left">
+      <span class="is-sortable">
+        <span class="sort-icons">
+          <span class="icon">
+            <i class="fas fa-sort-up"
+              (click)="sortInfos('_id.slug', -1, 'latest.count.INFO', -1, this.childNbSessions, 1);"></i>
+          </span>
+          <span class="icon">
+            <i class="fas fa-sort-down"
+              (click)="sortInfos('_id.slug', -1, 'latest.count.INFO', 1, this.childNbSessions, 1);"></i>
+          </span>
+        </span>
+        <span class="column-title">Infos</span>
+      </span>
+    </div>
+    <div class="column  is-1 has-text-left">
+      <span class="is-sortable">
+        <span class="sort-icons">
+          <span class="icon">
+            <i class="fas fa-sort-up"
+              (click)="sortInfos('_id.slug', -1, 'latest.count.ERROR', -1, this.childNbSessions, 1);"></i>
+          </span>
+          <span class="icon">
+            <i class="fas fa-sort-down"
+              (click)="sortInfos('_id.slug', -1, 'latest.count.ERROR', 1, this.childNbSessions, 1);"></i>
+          </span>
+        </span>
+        <span class="column-title">Erreurs</span>
+      </span>
+    </div>
+    <div class="column is-1 has-text-left">
+      <span class="column-title"></span>
+    </div>
+  </div>
+  <div class="data-list">
+    <div class="data columns is-multiline is-vcentered is-marginless"
+      *ngFor="let session of allSessionsInfoList; let i=index; let odd=odd; let even=even;"
+      [ngClass]="{ odd: odd, even: even }">
+      <div class="column is-4 has-text-left">
+        {{ session._id.session_id }}
+      </div>
+      <div class="column is-1 has-text-left">
+        {{ session.all.length }}
+      </div>
+      <div class="column is-2 has-text-left">
+        {{ session.latest.completionDate.$date | date: 'EE dd/MM/yyyy hh:mm:ss'}}
+      </div>
+      <div class="column is-1 has-text-left">
+        <span class="column-title">
+          {{ session.latest.duration.hours }}:
+          {{ session.latest.duration.minutes }}:
+          {{ session.latest.duration.seconds }}</span>
+      </div>
+      <div class="column  is-1 has-text-left actions">
+        {{ session.latest.count.INFO ? session.latest.count.INFO : 0 }}
+      </div>
+      <div class="column  is-1 has-text-left actions">
+        {{ session.latest.count.ERROR ? +session.latest.count.ERROR:'Non'}}
+      </div>
+      <div class="column is-1 actions">
+        <a class="button button-gl " [routerLink]="['preReport','session', session._id.session_id]">
+          <i class="fas fa-eye"></i>
+        </a>
+      </div>
+    </div>
+  </div>
+</div>
+<div class="columns is-marginless">
+  <div class="column is-10">
+    <app-paginator [length]="paginator.length" [pageSize]="paginator.limit"
+      [pageSizeOptions]="paginator.pageSizeOptions" [pageIndex]="paginator.pageIndex" [pagesToShow]="1"
+      [showFirstLastButtons]="true" (page)="changePagination($event)" (pageSizeChanged)="changePageSize($event)">
+    </app-paginator>
+  </div>
+  <div class="column is-1 is-vcentered">
+    <a class="button button-gl is-left" (click)="reset('_id.slug', -1,
+     'latest.completionDate', -1, this.childNbSessions, 1)">
+      <p class="bold-white">Reset</p>
+    </a>
+  </div>
+</div>
diff --git a/src/app/components/logs-dashboard/logs-sessions/logs-sessions.component.scss b/src/app/components/logs-dashboard/logs-sessions/logs-sessions.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..22f7b197fd884a7acc1e0611cd15c065f5af62dc
--- /dev/null
+++ b/src/app/components/logs-dashboard/logs-sessions/logs-sessions.component.scss
@@ -0,0 +1,12 @@
+
+@import "../../../../scss/variables.scss";
+@import "../../../../../node_modules/bulma/sass/utilities/_all.sass";
+
+.small-text{
+    font-size:0.8em;
+}
+
+.bold-white{
+    color: white;
+    font-weight: bold;
+}
\ No newline at end of file
diff --git a/src/app/components/logs-dashboard/logs-sessions/logs-sessions.component.ts b/src/app/components/logs-dashboard/logs-sessions/logs-sessions.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..af1d160f4c6c4b3b4bdceb2d66721da5f8bc82c3
--- /dev/null
+++ b/src/app/components/logs-dashboard/logs-sessions/logs-sessions.component.ts
@@ -0,0 +1,134 @@
+import { Component, OnChanges, SimpleChanges, Input, OnDestroy } from '@angular/core';
+import { DataLogsService } from 'src/app/services/data-logs.service';
+import { PaginatorOptions } from 'src/app/models/paginator-options.model';
+import { Subscription } from 'rxjs';
+
+@Component({
+  selector: 'app-logs-sessions',
+  templateUrl: './logs-sessions.component.html',
+  styleUrls: ['./logs-sessions.component.scss'],
+})
+export class LogsSessionsComponent implements OnChanges, OnDestroy {
+  @Input() childNbSessions: number;
+  @Input() sessionsList: any;
+
+  sessionChangeSub: Subscription;
+  nbOfItemsPerPage: number = 10;
+  nbSessions: number = 0;
+  foundSession: string = '';
+  allSessionsIdInfo: any = [];
+  allSessionsInfoList: any = [];
+  paginator: PaginatorOptions;
+  urlCode: string = 'sessionUrl';
+  sortFilter1:string = '_id';
+  orderFilter1: number = -1;
+  isResearchFieldActive: boolean = false;
+  sortFilter2:string = '_id';
+  orderFilter2: number = -1;
+  constructor(
+    private _dataLogsService: DataLogsService,
+  ) {
+    this.paginator = {
+      pageIndex: this._dataLogsService.pageNumber,
+      length: 1,
+      limit: this._dataLogsService.limit,
+      pageSizeOptions: [5, 10, 20],
+    };
+  }
+
+  ngOnChanges(changes: SimpleChanges): void {
+    this.sessionChangeSub = this._dataLogsService.sessionLogChange$.subscribe(
+      () => {
+        if (this.isResearchFieldActive) {
+          this.getAllInfoForOneSession('');
+        } else {
+          this.paginator.limit = this._dataLogsService.limit;
+          this.paginator.pageIndex = this._dataLogsService.pageNumber;
+          this.paginator.length = this._dataLogsService.length;
+          this.getAllSessionsInfo(
+            this.sortFilter1, this.orderFilter1, this.sortFilter2, this.orderFilter2, this.childNbSessions, this.paginator.pageIndex);
+        }
+      },
+      (error) => {
+        console.log('error in logs-session:', error);
+      },
+    );
+    this.getAllSessionsInfo('_id.slug', -1, 'latest.completionDate', -1, this.childNbSessions, 1);
+  }
+  ngOnDestroy() {
+    this.sessionChangeSub.unsubscribe();
+  }
+
+  getAllSessionsInfo(sortFilter1, order1, sortFilter2, order2, length, pageNumber) {
+    this._dataLogsService.getAllInfoForAllSessions(sortFilter1, order1, sortFilter2, order2, length, pageNumber).subscribe(
+      (result) => {
+        this.foundSession = '';
+        this.allSessionsInfoList = result;
+        this.nbSessions = result.length;
+        this.paginator.limit = this._dataLogsService.limit;
+        this.paginator.pageIndex = this._dataLogsService.pageNumber;
+        this.paginator.length = this.childNbSessions;
+        this.formatDate();
+      },
+      (error) => {
+        console.log('error in getAllSessionsInfo:', error);
+        this.allSessionsInfoList = [];
+        this.nbSessions = 0;
+        this.paginator.limit = 0;
+        this.paginator.pageIndex = 0;
+        this.paginator.length = 0;
+      },
+    );
+  }
+  sortInfos(sortFilter1, order1, sortFilter2, order2, length, pageNumber) {
+    if (!this.isResearchFieldActive) {
+      this.sortFilter1 = sortFilter1;
+      this.orderFilter1 = order1;
+      this.sortFilter2 = sortFilter2;
+      this.orderFilter2 = order2;
+      this.getAllSessionsInfo(this.sortFilter1, this.orderFilter1, this.sortFilter2, this.orderFilter2, length, pageNumber);
+    }
+  }
+
+  reset(sortFilter1, order1, sortFilter2, order2, length, pageNumber) {
+    this.isResearchFieldActive = false;
+    this.paginator.length = this.nbOfItemsPerPage;
+    this.getAllSessionsInfo(sortFilter1, order1, sortFilter2, order2, length, pageNumber);
+  }
+
+  getAllInfoForOneSession($event) {
+    // const foundSlug = (<HTMLInputElement>document.getElementById('foundSlug')).value;
+    if (this.foundSession === '') {
+      this.isResearchFieldActive = false;
+      this.paginator.limit = this.nbOfItemsPerPage;
+      this.getAllSessionsInfo('_id.slug', -1, 'latest.completionDate', 1, this.childNbSessions, 1);
+    } else {
+      this._dataLogsService.getAllInfoForOneSession(this.foundSession).subscribe((result) => {
+        // console.log('result for getAllInfoForOneSlug', result);
+        this.isResearchFieldActive = true;
+        this.allSessionsInfoList = result;
+        this.nbSessions = 1;
+        this.paginator.limit = this.nbOfItemsPerPage;
+        this.paginator.pageIndex = 1;
+        this.paginator.length = 1;
+        this.formatDate();
+      },
+      );
+    }
+  }
+
+  formatDate() {
+    for (let i = 0; i < this.nbSessions; i += 1) {
+      this.allSessionsInfoList[i].latest.completionDate.$date = new Date(this.allSessionsInfoList[i].latest.completionDate.$date);
+    }
+  }
+
+    // When pagination is changed by user, we update datasetList with new pagination options
+  changePagination(pageIndex) {
+    this._dataLogsService.paginationChanged(this.paginator.limit, pageIndex, 'SESSION');
+  }
+
+  changePageSize(pageSize) {
+    this._dataLogsService.paginationChanged(pageSize, 1, 'SESSION');
+  }
+}
diff --git a/src/app/components/logs-dashboard/logs-slugs/filter.pipe.ts b/src/app/components/logs-dashboard/logs-slugs/filter.pipe.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ac204fd5f6d5b52422f5ec33413b9ba279873dca
--- /dev/null
+++ b/src/app/components/logs-dashboard/logs-slugs/filter.pipe.ts
@@ -0,0 +1,12 @@
+import { Pipe, PipeTransform } from '@angular/core';
+@Pipe({
+  name: 'filter',
+})
+export class FilterPipe implements PipeTransform {
+  transform(items: any[], value: string, label:string): any[] {
+    if (!items) return [];
+    if (!value) return  items;
+    if (value === '' || value === null) return [];
+    return items.filter(e => e['_id'][label].toLowerCase().indexOf(value) > -1);
+  }
+}
diff --git a/src/app/components/logs-dashboard/logs-slugs/logs-slugs.component.html b/src/app/components/logs-dashboard/logs-slugs/logs-slugs.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..b21f8509c20fa0dfc8953d29a0865c4a7afb08b8
--- /dev/null
+++ b/src/app/components/logs-dashboard/logs-slugs/logs-slugs.component.html
@@ -0,0 +1,148 @@
+<div class="input-field is-centered">
+  <div class="columns is-5 is-vcentered">
+    <div class="column is-5 has-text-left">
+      <label class="label"> Recherche par slug: </label>
+      <input class="input" type="text" [(ngModel)]="foundSlug" (keyup)="getAllInfoForOneSlug($event)" id="foundSlug" list="slugsList" />
+      <datalist id="slugsList">
+        <option *ngFor="let slug of slugsList" [value]="slug"> {{slug}}</option>
+      </datalist>
+    </div>
+  </div>
+</div>
+
+<div class="columns is-centered is-marginless">
+  <div class="column is-6 has-text-left">
+    <P>{{ childNbSlugs }} slug(s) dans la base de données</P>
+  </div>
+  <div class="column  is-6 has-text-left">
+    <p>Pour chaque slug, les valeurs affichées concernent la session la plus récente.</p>
+  </div>
+</div>
+<div class="table entity-list-table">
+  <div class="header columns is-marginless">
+    <div class="column is-3 has-text-centered">
+      <span class="is-sortable">
+        <span class="sort-icons">
+          <span class="icon">
+            <i class="fas fa-sort-up" (click)="sortInfos('completionDate', -1, '_id.slug', -1, this.childNbSlugs, 1);"></i>
+          </span>
+          <span class="icon">
+            <i class="fas fa-sort-down" (click)="sortInfos('completionDate', -1, '_id.slug', 1, this.childNbSlugs, 1);">
+              </i>
+          </span>
+        </span>
+        <span class="column-title">Slugs</span>
+      </span>
+    </div>
+    <div class="column is-1 has-text-left">
+        <span class="column-title">Donnée complète</span>
+      </div>
+    <div class="column is-1 has-text-left">
+      <span class="column-title">Sessions</span>
+    </div>
+    <div class="column is-2 has-text-left">
+      <span class="is-sortable">
+        <span class="sort-icons">
+          <span class="icon">
+            <i class="fas fa-sort-up" (click)="sortInfos('_id.slug', -1, 'latest.completionDate', -1, this.childNbSlugs, 1);"></i>
+          </span>
+          <span class="icon">
+            <i class="fas fa-sort-down" (click)="sortInfos('_id.slug', -1, 'latest.completionDate', 1, this.childNbSlugs, 1);"></i>
+          </span>
+        </span>
+        <span class="column-title">Dernière date d'exécution</span>
+      </span>
+    </div>
+    <div class="column is-1 has-text-left">
+      <span class="is-sortable">
+<!--        <span class="sort-icons">-->
+<!--          <span class="icon">-->
+<!--              <i class="fas fa-sort-up" (click)="sortInfos('_id.slug', -1, 'latest.duration', -1, this.childNbSlugs, 1);"></i>-->
+<!--            </span>-->
+<!--          <span class="icon">-->
+<!--              <i class="fas fa-sort-down" (click)="sortInfos('_id.slug', -1, 'latest.duration', 1, this.childNbSlugs, 1);"></i>-->
+<!--          </span>-->
+<!--        </span>-->
+        <span class="column-title">Durée</span>
+      </span> </div>
+        <div class="column  is-1 has-text-left">
+          <span class="is-sortable">
+            <span class="sort-icons">
+              <span class="icon">
+                  <i class="fas fa-sort-up" (click)="sortInfos('_id.slug', -1, 'latest.count.INFO', -1, this.childNbSlugs, 1);"></i>
+                </span>
+              <span class="icon">
+                  <i class="fas fa-sort-down"  (click)="sortInfos('_id.slug', -1, 'latest.count.INFO', 1, this.childNbSlugs, 1);"></i>
+                </span>
+            </span>
+            <span class="column-title">Infos</span>
+          </span>
+        </div>
+    <div class="column  is-1 has-text-left">
+      <span class="is-sortable">
+        <span class="sort-icons">
+          <span class="icon">
+              <i class="fas fa-sort-up" (click)="sortInfos('_id.slug', -1, 'latest.count.ERROR', -1, this.childNbSlugs, 1);"></i>
+            </span>
+          <span class="icon">
+            <i class="fas fa-sort-down"  (click)="sortInfos('_id.slug', -1, 'latest.count.ERROR', 1, this.childNbSlugs, 1);"></i>
+          </span>
+        </span>
+        <span class="column-title">Erreurs</span>
+      </span>
+    </div>
+    <div class="column  is-1 has-text-left">
+      <span class="column-title"></span>
+    </div>
+  </div>
+  <div class="data-list">
+    <div *ngFor="let slug of allSlugsInfoList | filter : searchSlug: 'slug'; let i=index; let odd=odd; let even=even;">
+      <div class="data columns is-multiline is-vcentered is-marginless" *ngIf="slug._id.slug"
+        [ngClass]="{ odd: odd, even: even }">
+        <div class="column is-3 has-text-left">
+          {{slug._id.slug}} <br>
+          <div class="small-text">
+            <p>{{slug._id.uuid}}</p>
+          </div>
+        </div>
+        <div class="column is-1 has-text-left">
+            {{ slug.latest.uuid_suffix === 'full' ? 'Oui' : 'Non'}}
+        </div>
+        <div class="column is-1 has-text-left">
+          {{slug.all.length}}
+        </div>
+        <div class="column is-2 has-text-left">
+          {{slug.latest.completionDate.$date | date: 'EE dd/MM/yyyy hh:mm:ss'}}
+        </div>
+        <div class="column is-1 has-text-left">
+          {{slug.latest.duration.hours}}:{{slug.latest.duration.minutes}}:{{slug.latest.duration.seconds}}
+        </div>
+        <div class="column  is-1 has-text-left actions">
+            {{ slug.latest.count.INFO ? slug.latest.count.INFO : 0}}
+        </div>
+        <div class="column  is-1 has-text-left actions">
+          {{ slug.latest.count.ERROR ? slug.latest.count.ERROR:'No'}}
+        </div>
+        <div class="column  is-1 has-text-left actions">
+          <a class="button button-gl is-left" [routerLink]="['preReport', 'slug', slug._id.slug]">
+            <i class="fas fa-eye"></i>
+          </a>
+        </div>
+      </div>
+    </div>
+  </div>
+</div>
+<div class="columns is-marginless">
+  <div class="column is-10">
+    <app-paginator *ngIf="paginator.length > 0" [length]="paginator.length" [pageSize]="paginator.limit"
+      [pageSizeOptions]="paginator.pageSizeOptions" [pageIndex]="paginator.pageIndex" [pagesToShow]="1"
+      [showFirstLastButtons]="true" (page)="changePagination($event)" (pageSizeChanged)="changePageSize($event)">
+    </app-paginator>
+  </div>
+  <div class="column is-1 is-vcentered">
+      <a class="button button-gl is-left" (click)="reset('_id.slug', -1,
+      'latest.completionDate', -1, this.childNbSlugs, 1);">
+        <p class="bold-white">Reset</p>
+      </a>
+  </div>
+</div>
diff --git a/src/app/components/logs-dashboard/logs-slugs/logs-slugs.component.scss b/src/app/components/logs-dashboard/logs-slugs/logs-slugs.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..2f5cc96d6937ebd6aaa1c9b3cb90cfad6e11c37a
--- /dev/null
+++ b/src/app/components/logs-dashboard/logs-slugs/logs-slugs.component.scss
@@ -0,0 +1,11 @@
+@import "../../../../scss/variables.scss";
+@import "../../../../../node_modules/bulma/sass/utilities/_all.sass";
+
+.small-text{
+    font-size:0.8em;
+}
+
+.bold-white{
+    color: white;
+    font-weight: bold;
+}
\ No newline at end of file
diff --git a/src/app/components/logs-dashboard/logs-slugs/logs-slugs.component.ts b/src/app/components/logs-dashboard/logs-slugs/logs-slugs.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5152942ce9db5c66d17b6327e723b11fe6cc8517
--- /dev/null
+++ b/src/app/components/logs-dashboard/logs-slugs/logs-slugs.component.ts
@@ -0,0 +1,132 @@
+import { Component, OnChanges, SimpleChanges, Input, OnDestroy } from '@angular/core';
+import { DataLogsService } from 'src/app/services/data-logs.service';
+import { PaginatorOptions } from 'src/app/models/paginator-options.model';
+import { Subscription } from 'rxjs';
+import { FilterPipe }from './filter.pipe';
+
+@Component({
+  selector: 'app-logs-slugs',
+  templateUrl: './logs-slugs.component.html',
+  styleUrls: ['./logs-slugs.component.scss'],
+})
+export class LogsSlugsComponent implements OnChanges, OnDestroy {
+  @Input() childNbSlugs: number;
+  @Input() slugsList: any;
+
+  slugChangeSub: Subscription;
+  nbOfItemsPerPage: number = 10;
+  foundSlug: string = '';
+  sortFilter1:string = '_id';
+  orderFilter1: number = -1;
+  sortFilter2:string = '_id';
+  orderFilter2: number = -1;
+  allSlugsInfoList:any = [];
+  searchSlug: string = '';
+  slugInOnePage:number = 1;
+  isResearchFieldActive: boolean = false;
+  paginator: PaginatorOptions;
+  constructor(
+    private _dataLogsService: DataLogsService,
+    private _pipe: FilterPipe,
+  ) {
+    this.paginator = {
+      pageIndex: this._dataLogsService.pageNumber,
+      length: 1,
+      limit: this._dataLogsService.limit,
+      pageSizeOptions: [5, 10, 20],
+    };
+  }
+
+  ngOnChanges(changes: SimpleChanges): void {
+    this.slugChangeSub = this._dataLogsService.slugLogChange$.subscribe(
+      () => {
+        if (this.isResearchFieldActive) {
+          this.getAllInfoForOneSlug('');
+        } else {
+          this.paginator.limit = this._dataLogsService.limit;
+          this.paginator.pageIndex = this._dataLogsService.pageNumber;
+          this.paginator.length = this.childNbSlugs;
+          this.getAllInfoForAllSlugs(
+            this.sortFilter1, this.orderFilter1, this.sortFilter2, this.orderFilter2, this.childNbSlugs, this.paginator.pageIndex);
+        }
+      },
+    );
+    // console.log(' this.slugsList', this.slugsList);
+    this.getAllInfoForAllSlugs('_id.slug', -1, 'latest.completionDate', 1, this.childNbSlugs, 1);
+  }
+  ngOnDestroy() {
+    this.slugChangeSub.unsubscribe();
+  }
+  getAllInfoForAllSlugs(sortFilter1, order1, sortFilter2, order2, length, pageNumber) {
+    this._dataLogsService.getAllInfoForAllSlugs(sortFilter1, order1, sortFilter2, order2, length, pageNumber).subscribe(
+      (result) => {
+        this.foundSlug = '';
+        this.allSlugsInfoList = result;
+        this.slugInOnePage = result.length;
+        this.paginator.limit = this._dataLogsService.limit;
+        this.paginator.pageIndex = this._dataLogsService.pageNumber;
+        this.paginator.length = this.childNbSlugs;
+        this.formatDate();
+        // console.log('slugs result', result);
+      },
+      (error) => {
+        console.log('error in getAllInfoForAllSlugs:', error);
+        this.allSlugsInfoList = [];
+        this.slugInOnePage = 0;
+        this.paginator.limit = 0;
+        this.paginator.pageIndex = 0;
+        this.paginator.length = 0;
+      },
+    );
+  }
+  reset(sortFilter1, order1, sortFilter2, order2, length, pageNumber) {
+    this.isResearchFieldActive = false;
+    this.paginator.length = this.nbOfItemsPerPage;
+    this.getAllInfoForAllSlugs(sortFilter1, order1, sortFilter2, order2, length, pageNumber);
+  }
+
+  sortInfos(sortFilter1, order1, sortFilter2, order2, length, pageNumber) {
+    if (!this.isResearchFieldActive){
+      this.sortFilter1 = sortFilter1;
+      this.orderFilter1 = order1;
+      this.sortFilter2 = sortFilter2;
+      this.orderFilter2 = order2;
+      this.getAllInfoForAllSlugs(this.sortFilter1, this.orderFilter1, this.sortFilter2, this.orderFilter2, length, pageNumber);
+    }
+  }
+
+  getAllInfoForOneSlug($event) {
+    if (this.foundSlug === '') {
+      this.isResearchFieldActive = false;
+      this.paginator.limit = this.nbOfItemsPerPage;
+      this.getAllInfoForAllSlugs('_id.slug', -1, 'latest.completionDate', 1, this.childNbSlugs, 1);
+    } else {
+    // const foundSlug = (<HTMLInputElement>document.getElementById('foundSlug')).value;
+      this._dataLogsService.getAllInfoForOneSlug(this.foundSlug).subscribe((result) => {
+        this.isResearchFieldActive = true;
+        this.allSlugsInfoList = result;
+        this.slugInOnePage = 1;
+        this.paginator.limit = this.nbOfItemsPerPage;
+        this.paginator.pageIndex = 1;
+        this.paginator.length = 1;
+        this.formatDate();
+      },
+    );
+    }
+  }
+
+  formatDate() {
+    for (let i = 0; i < this.slugInOnePage; i += 1) {
+      this.allSlugsInfoList[i].latest.completionDate.$date = new Date(this.allSlugsInfoList[i].latest.completionDate.$date);
+    }
+  }
+    // When pagination is changed by user, we update datasetList with new pagination options
+  changePagination(pageIndex) {
+    this._dataLogsService.paginationChanged(this.paginator.limit, pageIndex, 'SLUG');
+  }
+
+  changePageSize(pageSize) {
+    this._dataLogsService.paginationChanged(pageSize, 1, 'SLUG');
+  }
+
+}
diff --git a/src/app/components/logs-dashboard/report/logs-error/logs-error.component.html b/src/app/components/logs-dashboard/report/logs-error/logs-error.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..5968072c06cd4564255b8121afa4dc888bf632f1
--- /dev/null
+++ b/src/app/components/logs-dashboard/report/logs-error/logs-error.component.html
@@ -0,0 +1,103 @@
+<div class="columns is-centered is-marginless">
+
+  <div class="column is-4 has-text-left is-marginless">
+
+    <div class="table entity-list-table is-marginless" >
+      <div class="header columns is-marginless is-multiline is-vcentered">
+        <div class="column is-6 has-text-left">
+          <p> Toutes les étapes:</p>
+        </div>
+      </div>
+      <div class="data-list">
+        <div *ngFor="let oneStepData of allStepsData; let i=index; let odd=odd; let even=even;">
+          <div *ngIf="oneStepData" [ngClass]="{ odd: odd, even: even }" (click)='getLogsSelector(oneStepData._id.step, oneStepData.counts);'>
+            <div class="data columns is-multiline is-vcentered is-marginless" [ngClass]="{ odd: odd, even: even, 'is-selected': displayedStep === oneStepData._id.step }" >
+              <div class="column is-6 has-text-left">
+                <br>
+                <p [ngClass]="{'is-selected': displayedStep === oneStepData._id.step}">
+                  <span class="bold-text"> Etape : </span>
+                  {{oneStepData._id.step}}</p>
+                <br>
+                <p [ngClass]="{'is-selected': displayedStep === oneStepData._id.step}">
+                  <span class="bold-text"> Durée : </span>
+                  {{oneStepData.totalHoursSpent}}h : {{oneStepData.totalMinutesSpent}}m :
+                  {{oneStepData.totalSecondsSpent}}s</p>
+                <br>
+                <p [ngClass]="{'is-selected': displayedStep === oneStepData._id.step}">
+                  <span class="bold-text"> Info(s) : </span>
+                  {{oneStepData.counts.INFO ? oneStepData.counts.INFO : 0 }}</p>
+                <br>
+              </div>
+              <div class="column is-6 has-text-right">
+                <br><br>
+                <div class="arrows" [ngClass]="{'is-displayed': displayedStep === oneStepData._id.step}">
+                  <svg xmlns="https://www.w3.org/2000/svg" id="chevron" viewBox="0 0 15 9">
+                    <path
+                      d="M7.5 7.5c-.1 0-.3-.1-.4-.1l-6-6C1 1.1 1 .8 1.1.6s.5-.2.7 0l5.6 5.6L13 .6c.2-.2.5-.2.7 0s.2.5 0 .7l-6 6c.1.1-.1.2-.2.2z"
+                      class="brandcolor" />
+                    <path
+                      d="M7.5 7.5c-.1 0-.3-.1-.4-.1l-6-6C1 1.1 1 .8 1.1.6s.5-.2.7 0l5.6 5.6L13 .6c.2-.2.5-.2.7 0s.2.5 0 .7l-6 6c.1.1-.1.2-.2.2z"
+                      class="brandcolor" />
+                  </svg>
+                </div>
+                <p [ngClass]="{'is-selected': displayedStep === oneStepData._id.step}">
+                  <br>
+                  <span class="bold-text"> Erreur(s) : </span>
+                  {{oneStepData.counts.ERROR ? oneStepData.counts.ERROR : 0}}</p>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+
+
+
+  </div>
+
+  <div class="column is-8 has-text-left">
+    <div class="table entity-list-table is-marginless" *ngIf="allErrorLogs[0] ; else noErrors">
+      <div class="header columns is-marginless is-multiline is-centered is-vcentered">
+          <div class="column is-2 has-text-left">
+             logs d'erreur pour  {{allErrorLogs[0].step}}</div>
+          <div class="column is-10 has-text-centered">
+        <app-paginator [length]="paginator.length" [pageSize]="paginator.limit"
+          [pageSizeOptions]="paginator.pageSizeOptions" [pageIndex]="paginator.pageIndex" [pagesToShow]="5"
+          [showFirstLastButtons]="true" (page)="changePagination($event)" (pageSizeChanged)="changePageSize($event)">
+        </app-paginator>
+      </div>
+      </div>
+      <div class="data-list" *ngFor="let oneErrorLog of allErrorLogs; let i=index; let odd=odd; let even=even;">
+
+        <div class="data columns is-multiline is-vcentered is-marginless" [ngClass]="{ odd: odd, even: even }">
+          <div class="columns is-marginless">
+            <div class="column is-12 has-text-left">
+              <p>
+                <span class="bold-text"> Etape : </span>
+                {{oneErrorLog.step}}</p>
+              <br>
+              <p>
+                <span class="bold-text"> Status : </span>
+                {{oneErrorLog.status}}</p>
+              <br>
+              <p>
+                <span class="bold-text"> Info : </span>
+                {{oneErrorLog.info}}</p>
+              <br>
+              <p>
+                <span class="bold-text"> Progression : </span>
+                {{oneErrorLog.progress_ratio}}</p>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+
+    <ng-template #noErrors class="is-centered is-marginless" >
+        <p>Pas d'erreurs</p>
+      </ng-template>
+
+    <div>
+    </div>
+  </div>
+</div>
diff --git a/src/app/components/logs-dashboard/report/logs-error/logs-error.component.scss b/src/app/components/logs-dashboard/report/logs-error/logs-error.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..b95e30a3a22a52ad678940ba4cc1a8854c030d65
--- /dev/null
+++ b/src/app/components/logs-dashboard/report/logs-error/logs-error.component.scss
@@ -0,0 +1,67 @@
+@import "../../../../../scss/variables.scss";
+@import "../../../../../../node_modules/bulma/sass/utilities/_all.sass";
+
+figure {
+    text-align: center;
+  }
+  figure img{
+    max-width: 9.3rem;
+    display: inline-block;
+    margin-top: 1.25rem;
+  }
+
+  .card-header-title {
+    justify-content: center;
+  }
+
+  .mini-info-card{
+    margin-top: 1.25rem;
+    padding: 1.25rem;
+    background-color: yellow;
+
+  }
+  .mini-log-card{
+    margin-top: 1.25rem;
+    padding: 1.25rem;
+    background-color: greenyellow;
+
+  }
+
+  .mini-error-card{
+    margin-top: 1.25rem;
+    padding: 1.25rem;
+    background-color: red;
+
+  }
+  .input-field{
+    width: 200%;
+    margin-top: 1.25rem;
+  }
+
+  .hidden {
+    display: none;
+  }
+
+  .arrows {
+    svg {
+      width: 1rem;
+      fill: $grey-dark-color;
+      transform: rotate(0deg);
+    }
+  }
+
+  .arrows.is-displayed svg {
+    transform: rotate(-90deg);
+  }
+  .paginator-container{
+    background-color: yellowgreen;
+  }
+
+  .is-selected{
+    background-color: $tomato-color !important; //to override odd or even class css
+    color: white;
+  }
+  .bold-text {
+    font-weight:bold;
+    display:inline;
+  }
diff --git a/src/app/components/logs-dashboard/report/logs-error/logs-error.component.ts b/src/app/components/logs-dashboard/report/logs-error/logs-error.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..55f17fe75f94b2b0e38126198f362681ed434879
--- /dev/null
+++ b/src/app/components/logs-dashboard/report/logs-error/logs-error.component.ts
@@ -0,0 +1,161 @@
+import { Component, OnChanges, SimpleChanges, Input, OnDestroy } from '@angular/core';
+import { DataLogsService } from 'src/app/services/data-logs.service';
+import { ActivatedRoute } from '@angular/router';
+import { ILogs } from 'src/app/models/logs.model';
+import { PaginatorOptions } from 'src/app/models/paginator-options.model';
+import { Subscription } from 'rxjs';
+
+@Component({
+  selector: 'app-logs-error',
+  templateUrl: './logs-error.component.html',
+  styleUrls: ['./logs-error.component.scss'],
+})
+export class LogsErrorComponent implements OnChanges, OnDestroy {
+  @Input() childUuid: string = '';
+  @Input() childSessionId: string = '';
+  @Input() childSlug: string;
+  errorLogChangeSub: Subscription;
+  selectedStep  = {
+    step:'main',
+    nbErrors: 0,
+    nbInfos: 0,
+  };
+
+  displayedStep: string = 'none';
+  logTypes: any = [
+    {
+      name : 'erreurs',
+      selected : true,
+    },
+    {
+      name : 'infos',
+      selected : false,
+    },
+  ];
+  uuid: string;
+  sessionId: string;
+  slug: string;
+  stepData: ILogs;
+  allLogs: any;
+  data: any = [];
+  allStepsData: any = [];
+  allErrorLogs: any = [];
+  // Paginator options
+  paginator: PaginatorOptions;
+  pageSizeOptions = [5, 10, 25, 100];
+  constructor(
+    private _dataLogsService: DataLogsService,
+    private route: ActivatedRoute,
+  ) {
+    this.paginator = {
+      pageIndex: this._dataLogsService.pageNumber,
+      length: 1,
+      limit: this._dataLogsService.limit,
+      pageSizeOptions: [5, 10, 20],
+    };
+  }
+
+  ngOnChanges(changes: SimpleChanges): void {
+    this.slug = this.childSlug;
+    this.getLogsSteps();
+    this.errorLogChangeSub = this._dataLogsService.errorLogChange$.subscribe(
+      () => {
+        this.paginator.limit = this._dataLogsService.limit;
+        this.paginator.pageIndex = this._dataLogsService.pageNumber;
+        if (this.logTypes[0].selected === true) {
+          this.getErrorStepsLogs(this.selectedStep.step, this.selectedStep.nbErrors, this.paginator.pageIndex);
+        }
+        if (this.logTypes[1].selected === true) {
+          this.getSimpleStepsLogs(this.selectedStep.step, this.selectedStep.nbInfos, this.paginator.pageIndex);
+        }
+      },
+    );
+  }
+  ngOnDestroy() {
+    this.errorLogChangeSub.unsubscribe();
+  }
+  getLogsSteps() {
+    if ((this.childUuid != null) && (this.childSessionId != null)) {
+      // console.log('parameters', this.childUuid, this.childSessionId);
+
+      this._dataLogsService.getAllStepsDuration(
+        this.childUuid,
+        this.childSessionId,
+      ).subscribe((results) => {
+        this.allStepsData = results;
+        for (const result of results) {
+          if (result.counts.ERROR > 0) {
+            this.getLogsSelector(result._id.step, result.counts);
+            break;
+          }
+        }
+      },
+      );
+    }
+  }
+  getLogsSelector(step, counts) {
+    this.selectedStep.step = step;
+    this.selectedStep.nbErrors = counts.ERROR;
+    this.selectedStep.nbInfos = counts.INFO;
+    this.allErrorLogs = [];
+    this.allLogs = [];
+    this.displayedStep = step;
+    if (this.logTypes[0].selected === true) {
+      this.getErrorStepsLogs(step, this.selectedStep.nbErrors, 1);
+    }
+    if (this.logTypes[1].selected === true) {
+      this.getSimpleStepsLogs(step, this.selectedStep.nbInfos, 1);
+    }
+  }
+  getErrorStepsLogs(step, nbErrors, pageNumber) {
+    if ((this.childUuid != null) && (this.childSessionId != null)) {
+      // console.log('parameters', this.childUuid, this.childSessionId);
+
+      this._dataLogsService.getLogsStepsLogs(
+        step,
+        this.childSessionId,
+        this.childUuid,
+        'ERROR',
+        nbErrors,
+        pageNumber,
+      ).subscribe((results) => {
+        // console.log('results', results);
+        this.allErrorLogs = results;
+        this.paginator.limit = this._dataLogsService.limit;
+        this.paginator.pageIndex = this._dataLogsService.pageNumber;
+        this.paginator.length = this._dataLogsService.length;
+      },
+      );
+    }
+  }
+  getSimpleStepsLogs(step, nbInfos, pageNumber) {
+    if ((this.childUuid != null) && (this.childSessionId != null)) {
+      // console.log('parameters', this.childUuid, this.childSessionId);
+
+      this._dataLogsService.getLogsStepsLogs(
+        step,
+        this.childSessionId,
+        this.childUuid,
+        'INFO',
+        nbInfos,
+        pageNumber,
+      ).subscribe((results) => {
+        // console.log('results', results);
+        this.allLogs = results;
+        this.paginator.limit = this._dataLogsService.limit;
+        this.paginator.pageIndex = this._dataLogsService.pageNumber;
+        this.paginator.length = this._dataLogsService.length;
+      },
+      );
+    }
+  }
+
+    // When pagination is changed by user, we update datasetList with new pagination options
+  changePagination(pageIndex) {
+    this._dataLogsService.paginationChanged(this.paginator.limit, pageIndex, 'ERROR');
+  }
+
+  changePageSize(pageSize) {
+    this._dataLogsService.paginationChanged(pageSize, 1, 'ERROR');
+  }
+}
diff --git a/src/app/components/logs-dashboard/report/logs-graph/logs-graph.component.html b/src/app/components/logs-dashboard/report/logs-graph/logs-graph.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..481d25f57f903c987d750e021abfbdad132afbc2
--- /dev/null
+++ b/src/app/components/logs-dashboard/report/logs-graph/logs-graph.component.html
@@ -0,0 +1,23 @@
+
+<div class="section" *ngIf="isNoError; else errorGraph">
+  <div class="graph-component">
+
+    <div class="columns is-centered">
+      <div class="graph-div-canvas">
+        <canvas id="graphCanvas">{{ chart }}</canvas>
+      </div>
+    </div>
+    <div  class="columns is-centered">
+      <a class="button button-gl is-centered" (click)="replotGraph()">
+          Recharger le graphique
+      </a>
+  </div>
+  </div>
+</div>
+<ng-template #errorGraph>
+  <h3>
+    Pas de données pour ce couple: slug-session    
+    <br>
+    Veuillez vérifier vos paramètre de recherche SVP.
+  </h3>
+</ng-template>
\ No newline at end of file
diff --git a/src/app/components/logs-dashboard/report/logs-graph/logs-graph.component.scss b/src/app/components/logs-dashboard/report/logs-graph/logs-graph.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..81e12ca709368a7309dbf7d9663108997ba8538d
--- /dev/null
+++ b/src/app/components/logs-dashboard/report/logs-graph/logs-graph.component.scss
@@ -0,0 +1,4 @@
+.graph-div-canvas{
+    width: 80%;
+    left: 10%;
+}
\ No newline at end of file
diff --git a/src/app/components/logs-dashboard/report/logs-graph/logs-graph.component.ts b/src/app/components/logs-dashboard/report/logs-graph/logs-graph.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3010b22202aa812c209fd0fc8b6f506576600027
--- /dev/null
+++ b/src/app/components/logs-dashboard/report/logs-graph/logs-graph.component.ts
@@ -0,0 +1,126 @@
+import { Component, OnChanges, SimpleChanges, Input } from '@angular/core';
+import { Chart } from 'chart.js';
+import { DataLogsService } from 'src/app/services/data-logs.service';
+
+@Component({
+  selector: 'app-logs-graph',
+  templateUrl: './logs-graph.component.html',
+  styleUrls: ['./logs-graph.component.scss'],
+})
+export class LogsGraphComponent implements OnChanges {
+  @Input() childUuid: string;
+  @Input() childSessionId: string;
+  @Input() childSlug: string;
+
+  public myChart: Chart;
+  uuid: string;
+  sessionId: string;
+  isNoError: boolean = true;
+  graphData: any = [0.2, 0.3];
+  dataset = [];
+
+  constructor(
+    private dataLogsService: DataLogsService,
+  ) { }
+  ngOnChanges(changes: SimpleChanges): void {
+    if ((this.childUuid != null) && (this.childSessionId != null)) {
+      this.getData();
+    }
+  }
+
+  plotGraph() {
+
+    this.myChart = new Chart(document.getElementById('graphCanvas'), {
+      type: 'scatter',
+      data: {
+        datasets: this.dataset,
+      },
+      options: {
+        title: {
+          display: true,
+          text: "Rapport d'avancement des scripts d'indexation.",
+        },
+        legend: {
+          display: true,
+          position: 'right',
+        },
+        elements: {
+          line: {
+            tension: 0, // disables bezier curves
+          },
+        },
+        responsive: true,
+        scales: {
+          xAxes: [{
+            type: 'time',
+            display: true,
+            scaleLabel: {
+              display: true,
+              labelString: 'temps',
+            },
+            ticks: {
+              major: {
+                fontStyle: 'bold',
+                fontColor: '#FF0000',
+              },
+            },
+          }],
+          yAxes: [{
+            display: true,
+            scaleLabel: {
+              display: true,
+              labelString: 'progression',
+            },
+          }],
+        },
+      },
+
+    });
+  }
+
+  getXYValuesFromData() {
+    const color = ['red', 'yellow', 'blue', 'black', 'pink', 'green', 'orange'];
+    let c = 0;
+    this.dataset = [];
+    for (const data of this.graphData) {
+      const arrayOfXY = [];
+      for (const i in data.values) {
+        const XY = {
+          x: data.timestamps[i].$date,
+
+          y: data.values[i],
+        };
+        arrayOfXY.push(XY);
+      }
+      const tempObj = {
+        label: data._id.step,
+        showLine: true,
+        backgroundColor: color[c],
+        borderColor: color[c],
+        fill: false,
+        data: arrayOfXY,
+        lineTension: 0.3,
+      };
+      this.dataset.push(tempObj);
+      c += 1;
+    }
+  }
+  replotGraph() {
+    this.getData();
+    this.myChart.update();
+  }
+  getData() {
+    this.dataLogsService.getData(this.childUuid, this.childSessionId).subscribe((results) => {
+      try {
+        const dataLenght = results[0].lenght;
+      } catch (e) {
+        console.log('error message :', e);
+        this.isNoError = false;
+      }
+      this.graphData = results;
+      this.getXYValuesFromData();
+      this.plotGraph();
+    },
+    );
+  }
+}
diff --git a/src/app/components/logs-dashboard/report/logs-info/logs-info.component.html b/src/app/components/logs-dashboard/report/logs-info/logs-info.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..c60db205b68efa558877650f4f4b9aae52c37188
--- /dev/null
+++ b/src/app/components/logs-dashboard/report/logs-info/logs-info.component.html
@@ -0,0 +1,101 @@
+<div class="columns is-centered is-marginless">
+
+  <div class="column is-4 has-text-left is-marginless">
+
+    <div class="table entity-list-table is-marginless" *ngIf="allLogs; else noLogs">
+      <div class="header columns is-marginless is-multiline is-vcentered">
+        <div class="column is-6 has-text-left">
+          <p> Toutes les étapes:</p>
+        </div>
+      </div>
+      <div class="data-list">
+        <div *ngFor="let oneStepData of allStepsData; let i=index; let odd=odd; let even=even;">
+          <div *ngIf="oneStepData" [ngClass]="{odd: odd, even: even}"  (click)='getLogsSelector(oneStepData._id.step, oneStepData.counts);'>
+            <div class="data columns is-multiline is-vcentered is-marginless" [ngClass]="{ odd: odd, even: even, 'is-selected': displayedStep === oneStepData._id.step }" >
+              <div class="column is-6 has-text-left">
+                <br>
+                <p  [ngClass]="{'is-selected': displayedStep === oneStepData._id.step}">
+                  <span class="bold-text">Etape : </span>
+                  {{oneStepData._id.step}}</p>
+                <br>
+                <p [ngClass]="{'is-selected': displayedStep === oneStepData._id.step}" >
+                 <span class="bold-text">Durée : </span>
+                  {{oneStepData.totalHoursSpent}}h : {{oneStepData.totalMinutesSpent}}m :
+                  {{oneStepData.totalSecondsSpent}}s
+                </p>
+                <br>
+                <p [ngClass]="{'is-selected': displayedStep === oneStepData._id.step}">
+                  <span class="bold-text">Info(s) : </span>
+                  {{oneStepData.counts.INFO ? oneStepData.counts.INFO : 0 }}</p>
+                <br>
+              </div>
+              <div class="column is-6 has-text-right">
+                <br><br>
+                <div class="arrows" [ngClass]="{'is-displayed': displayedStep === oneStepData._id.step}">
+                  <svg xmlns="https://www.w3.org/2000/svg" id="chevron" viewBox="0 0 15 9">
+                    <path
+                      d="M7.5 7.5c-.1 0-.3-.1-.4-.1l-6-6C1 1.1 1 .8 1.1.6s.5-.2.7 0l5.6 5.6L13 .6c.2-.2.5-.2.7 0s.2.5 0 .7l-6 6c.1.1-.1.2-.2.2z"
+                      class="brandcolor" />
+                    <path
+                      d="M7.5 7.5c-.1 0-.3-.1-.4-.1l-6-6C1 1.1 1 .8 1.1.6s.5-.2.7 0l5.6 5.6L13 .6c.2-.2.5-.2.7 0s.2.5 0 .7l-6 6c.1.1-.1.2-.2.2z"
+                      class="brandcolor" />
+                  </svg>
+                </div>
+                <p [ngClass]="{'is-selected': displayedStep === oneStepData._id.step}">
+                  <br>
+                  <span class="bold-text">Erreur(s) : </span>
+                  {{ oneStepData.counts.ERROR ? oneStepData.counts.ERROR : 0}}
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+
+    <ng-template class="is-centered is-marginless" #noLogs>
+      <p>Pas de Logs</p>
+    </ng-template>
+
+  </div>
+
+  <div class="column is-8 has-text-left">
+    <div class="table entity-list-table is-marginless" *ngIf="allLogs">
+      <div class="header columns is-marginless is-multiline is-centered is-vcentered">
+          <div class="column is-2 has-text-left">
+              logs pour {{allLogs[0].step}}</div>
+           <div class="column is-10 has-text-left">
+        <app-paginator [length]="paginator.length" [pageSize]="paginator.limit"
+          [pageSizeOptions]="paginator.pageSizeOptions" [pageIndex]="paginator.pageIndex" [pagesToShow]="5"
+          [showFirstLastButtons]="true" (page)="changePagination($event)" (pageSizeChanged)="changePageSize($event)">
+        </app-paginator>
+        </div>
+      </div>
+      <div class="data-list" *ngFor="let oneLog of allLogs; let i=index; let odd=odd; let even=even;">
+
+        <div class="data columns is-multiline is-vcentered is-marginless" [ngClass]="{ odd: odd, even: even }">
+          <div class="columns is-marginless">
+            <div class="column is-12 has-text-left">
+              <p>
+                <span class="bold-text">Step : </span>
+                {{oneLog.step}}</p>
+              <br>
+              <p>
+                <span class="bold-text">Status : </span>
+                {{oneLog.status}}</p>
+              <br>
+              <p>
+                <span class="bold-text">Info : </span>
+                {{oneLog.info}}</p>
+              <br>
+              <p>
+                <span class="bold-text">Progression : </span>
+                {{oneLog.progress_ratio}}</p>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+    <div>
+    </div>
+  </div>
+</div>
diff --git a/src/app/components/logs-dashboard/report/logs-info/logs-info.component.scss b/src/app/components/logs-dashboard/report/logs-info/logs-info.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..cc915ee76b3916f7afca907a7ef4a96dc2c818bf
--- /dev/null
+++ b/src/app/components/logs-dashboard/report/logs-info/logs-info.component.scss
@@ -0,0 +1,71 @@
+@import "../../../../../scss/variables.scss";
+@import "../../../../../../node_modules/bulma/sass/utilities/_all.sass";
+
+figure {
+    text-align: center;
+  }
+  figure img{
+    max-width: 9.3rem;
+    display: inline-block;
+    margin-top: 1.25rem;
+  }
+
+  .card-header-title {
+    justify-content: center;
+  }
+
+  .mini-info-card{
+    margin-top: 1.25rem;
+    padding: 1.25rem;
+    background-color: yellow;
+
+  }
+  .color-green{
+
+    background-color: greenyellow;
+
+  }
+
+  .mini-error-card{
+    margin-top: 1.25rem;
+    padding: 1.25rem;
+    background-color: red;
+
+  }
+  .input-field{
+    width: 200%;
+    margin-top: 1.25rem;
+  }
+
+  .hidden {
+    display: none;
+  }
+
+  .arrows {
+    svg {
+      width: 1rem;
+      fill: $grey-dark-color;
+      transform: rotate(0deg);
+    }
+  }
+
+  .arrows.is-displayed svg {
+    transform: rotate(-90deg);
+  }
+  .paginator-container{
+    background-color: yellowgreen;
+  }
+
+  .steps-header{
+    padding: 0;
+    margin:0;
+  }
+  .is-selected{
+    background-color: $tomato-color !important; //to override odd or even class css
+    color: white;
+  }
+  .bold-text {
+    font-weight:bold;
+    display:inline;
+  }
+
diff --git a/src/app/components/logs-dashboard/report/logs-info/logs-info.component.ts b/src/app/components/logs-dashboard/report/logs-info/logs-info.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b608ac5ee36ee10f5c1ade6b66ee10be2cd04988
--- /dev/null
+++ b/src/app/components/logs-dashboard/report/logs-info/logs-info.component.ts
@@ -0,0 +1,115 @@
+import { Component, OnChanges, SimpleChanges, Input, OnDestroy } from '@angular/core';
+import { DataLogsService } from 'src/app/services/data-logs.service';
+import { ActivatedRoute } from '@angular/router';
+import { ILogs } from 'src/app/models/logs.model';
+import { PaginatorOptions } from 'src/app/models/paginator-options.model';
+import { Subscription } from 'rxjs';
+
+@Component({
+  selector: 'app-logs-info',
+  templateUrl: './logs-info.component.html',
+  styleUrls: ['./logs-info.component.scss'],
+})
+export class LogsInfoComponent implements OnChanges, OnDestroy {
+  @Input() childUuid: string = '';
+  @Input() childSessionId: string = '';
+  @Input() childSlug: string;
+  infoChangeSub: Subscription;
+  selectedStep = {
+    step: 'main',
+    nbErrors: 1,
+    nbInfos: 0,
+  };
+
+  displayedStep: string = 'none';
+  uuid: string;
+  sessionId: string;
+  slug: string;
+  allLogs: any;
+  data:any = [];
+  allStepsData:any = [];
+
+  // Paginator options
+  paginator: PaginatorOptions;
+  pageSizeOptions = [5, 10, 25, 100];
+  constructor(
+    private _dataLogsService: DataLogsService,
+    private route: ActivatedRoute,
+  ) {
+    this.paginator = {
+      pageIndex: this._dataLogsService.pageNumber,
+      length: 1,
+      limit: this._dataLogsService.limit,
+      pageSizeOptions: [5, 10, 20],
+    };
+  }
+
+  ngOnChanges(changes: SimpleChanges): void {
+    this.slug = this.childSlug;
+    this.getLogsSteps();
+    this.infoChangeSub = this._dataLogsService.infoLogChange$.subscribe(
+      () => {
+        this.paginator.limit = this._dataLogsService.limit;
+        this.paginator.pageIndex = this._dataLogsService.pageNumber;
+        this.paginator.length = this._dataLogsService.length;
+        this.getSimpleStepsLogs(this.selectedStep.step, this.selectedStep.nbInfos, this.paginator.pageIndex);
+      },
+    );
+  }
+  ngOnDestroy() {
+    this.infoChangeSub.unsubscribe();
+  }
+
+  getLogsSteps() {
+    if ((this.childUuid != null) && (this.childSessionId != null)) {
+      // console.log('parameters', this.childUuid, this.childSessionId);
+
+      this._dataLogsService.getAllStepsDuration(
+        this.childUuid,
+        this.childSessionId,
+      ).subscribe((results) => {
+        this.allStepsData = results;
+        this.getLogsSelector(results[0]._id.step, results[0].counts);
+      },
+      );
+    }
+  }
+  getLogsSelector(step, counts) {
+    this.selectedStep.step = step;
+    this.selectedStep.nbInfos = counts.INFO;
+    this.allLogs = [];
+    this.displayedStep = step;
+    this.getSimpleStepsLogs(step, this.selectedStep.nbInfos, 1);
+  }
+
+  getSimpleStepsLogs(step, nbInfos, pageNumber) {
+    if ((this.childUuid != null) && (this.childSessionId != null)) {
+      // console.log('parameters', this.childUuid, this.childSessionId);
+
+      this._dataLogsService.getLogsStepsLogs(
+        step,
+        this.childSessionId,
+        this.childUuid,
+        'INFO',
+        nbInfos,
+        pageNumber,
+      ).subscribe((results) => {
+        // console.log('results', results);
+        this.allLogs = results;
+        this.paginator.limit = this._dataLogsService.limit;
+        this.paginator.pageIndex = this._dataLogsService.pageNumber;
+        this.paginator.length = this._dataLogsService.length;
+      },
+      );
+    }
+  }
+
+  // When pagination is changed by user, we update datasetList with new pagination options
+  changePagination(pageIndex) {
+    this._dataLogsService.paginationChanged(this.paginator.limit, pageIndex, 'INFO');
+  }
+
+  changePageSize(pageSize) {
+    this._dataLogsService.paginationChanged(pageSize, 1, 'INFO');
+  }
+}
diff --git a/src/app/components/logs-dashboard/report/logs-pre-report/logs-pre-report.component.html b/src/app/components/logs-dashboard/report/logs-pre-report/logs-pre-report.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..2ca3f7a7050304923a0f23a16dfe1d80d4565c88
--- /dev/null
+++ b/src/app/components/logs-dashboard/report/logs-pre-report/logs-pre-report.component.html
@@ -0,0 +1,295 @@
+<div class="section page-container" *ngIf="nbObjects!==0">
+  <app-page-header [pageInfo]="pageHeaderInfo" [goToThisUrl]="previousUrl" [hideBackButton]="false"></app-page-header>
+
+  <div [ngClass]="{'hidden': isSession}">
+    <div class="section">
+      <div class="columns is-left is-marginless">
+        <div class="column has-text-left">
+          <span>{{nbObjects}} sessions(s) trouvées pour:</span>
+          <br>
+          <span class="bold-text">slug : </span>
+          <span> {{id}} </span>
+          <br>
+          <span class="bold-text">uuid : </span>
+          <span> {{uuid}} </span>
+          <br>
+          <br>
+          <span> Pour chaque session, les données affichées concernent le slug en question (ici pas le slug le plus récent ou la durée cumulée de tous les slugs). </span>
+          <br>
+        </div>
+      </div>
+
+      <div class="table entity-list-table">
+        <div class="header columns is-marginless">
+          <div class="column is-4 has-text-left">
+            <span class="is-sortable">
+              <span class="column-title">SessionId</span>
+              <p></p>
+            </span>
+          </div>
+          <div class="column is-1 has-text-left">
+            <span class="is-sortable">
+<!--              <span class="sort-icons">-->
+<!--                <span class="icon">-->
+<!--                  <i class="fas fa-sort-up"></i>-->
+<!--                </span>-->
+<!--                <span class="icon">-->
+<!--                  <i class="fas fa-sort-down"></i>-->
+<!--                </span>-->
+<!--              </span>-->
+              <span class="column-title">Slugs</span>
+            </span>
+          </div>
+          <div class="column is-2 has-text-left">
+            <span class="is-sortable">
+              <span class="sort-icons">
+                <span class="icon">
+                  <i class="fas fa-sort-up"></i>
+                </span>
+                <span class="icon">
+                  <i class="fas fa-sort-down"></i>
+                </span>
+              </span>
+              <span class="column-title">Date d'exécution</span>
+            </span>
+          </div>
+          <div class="column is-1 has-text-left">
+            <span class="is-sortable">
+<!--              <span class="sort-icons">-->
+<!--                <span class="icon">-->
+<!--                  <i class="fas fa-sort-up"></i>-->
+<!--                </span>-->
+<!--                <span class="icon">-->
+<!--                  <i class="fas fa-sort-down"></i>-->
+<!--                </span>-->
+<!--              </span>-->
+              <span class="column-title">Durée</span>
+            </span>
+          </div>
+          <div class="column  is-1 has-text-left">
+              <span class="is-sortable">
+<!--                <span class="sort-icons">-->
+<!--                  <span class="icon">-->
+<!--                    <i class="fas fa-sort-up"></i>-->
+<!--                  </span>-->
+<!--                  <span class="icon">-->
+<!--                    <i class="fas fa-sort-down"></i>-->
+<!--                  </span>-->
+<!--                </span>-->
+                <span class="column-title">Infos</span>
+              </span>
+            </div>
+          <div class="column  is-1 has-text-left">
+            <span class="is-sortable">
+<!--              <span class="sort-icons">-->
+<!--                <span class="icon">-->
+<!--                  <i class="fas fa-sort-up"></i>-->
+<!--                </span>-->
+<!--                <span class="icon">-->
+<!--                  <i class="fas fa-sort-down"></i>-->
+<!--                </span>-->
+<!--              </span>-->
+              <span class="column-title">Erreurs</span>
+            </span>
+          </div>
+          <div class="column is-1 has-text-left">
+            <span class="column-title"></span>
+          </div>
+        </div>
+        <div class="data-list">
+          <div *ngFor="let session of allSlugSessionInfoList; let i=index; let odd=odd; let even=even;">
+            <div class="data columns is-multiline is-vcentered is-marginless"
+            *ngIf="session._id.slug_list"
+            [ngClass]="{ odd: odd, even: even }">
+              <div class="column is-4 has-text-left">
+                {{ session._id.session_id }}
+              </div>
+              <div class="column is-1 has-text-left">
+                {{session._id.slug_list.length}}
+              </div>
+              <div class="column is-2 has-text-left">
+                {{ session._id.completionDate.$date | date: 'EE dd/MM/yyyy hh:mm:ss' }}
+              </div>
+              <div class="column is-1 has-text-left">
+                {{session._id.duration.hours}}:{{session._id.duration.minutes}}:{{session._id.duration.seconds}}
+              </div>
+              <div class="column  is-1 has-text-left actions">
+                  {{ session._id.count.INFO ? session._id.count.INFO: 0}}
+              </div>
+              <div class="column  is-1 has-text-left actions">
+                {{ session._id.count.ERROR ? session._id.count.ERROR: 0}}
+              </div>
+              <div class="column is-1 actions">
+                <a class="button button-gl "
+                  [routerLink]="['/','datalogs','report', id,  session._id.session_id , 'slug']">
+                  <i class="fas fa-eye"></i>
+                </a>
+              </div>
+            </div>
+          </div>
+        </div>
+
+      </div>
+      <div class="columns is-marginless">
+        <div class="column">
+          <app-paginator *ngIf="paginator.length > 0" [length]="paginator.length" [pageSize]="paginator.limit"
+            [pageSizeOptions]="paginator.pageSizeOptions" [pageIndex]="paginator.pageIndex" [pagesToShow]="5"
+            [showFirstLastButtons]="true" (page)="changePagination($event)" (pageSizeChanged)="changePageSize($event)">
+          </app-paginator>
+        </div>
+      </div>
+    </div>
+
+  </div>
+
+  <div [ngClass]="{'hidden': !isSession}">
+    <div class="section">
+        <div class="input-field is-centered">
+            <div class="columns is-5 is-vcentered">
+              <div class="column is-5 has-text-left">
+                <label class="label"> Recherche par slug: </label>
+                <input class="input" type="text" [(ngModel)]="foundSlug" (keyup)="getAllInfoForOneSlug($event)" id="foundSlug" list="slugsList" />
+                <datalist id="slugsList">
+                  <option *ngFor="let slug of completeSlugsList" [value]="slug"> {{slug}}</option>
+                </datalist>
+              </div>
+            </div>
+          </div>
+      <div class="columns is-left is-marginless">
+        <div class="column has-text-left">
+          <div class="columns is-centered is-marginless">
+
+            <div class="column has-text-left">
+              <span>{{nbObjects}} slug(s) trouvés pour la session : </span>
+              <br>
+              <span class="bold-text"> {{id}} </span>
+            </div>
+
+          </div>
+        </div>
+      </div>
+
+      <div class="table entity-list-table">
+        <div class="header columns is-marginless">
+          <div class="column is-3 has-text-centered">
+            <span class="is-sortable">
+<!--              <span class="sort-icons">-->
+<!--                <span class="icon">-->
+<!--                  <i class="fas fa-sort-up"></i>-->
+<!--                </span>-->
+<!--                <span class="icon">-->
+<!--                  <i class="fas fa-sort-down"></i>-->
+<!--                </span>-->
+<!--              </span>-->
+              <span class="column-title">Slugs</span>
+            </span>
+          </div>
+          <div class="column is-1 has-text-left">
+              <span class="column-title">Donnée complète?</span>
+            </div>
+          <div class="column is-1 has-text-left">
+            <span class="column-title">Sessions</span>
+          </div>
+          <div class="column is-2 has-text-left">
+            <span class="is-sortable">
+<!--              <span class="sort-icons">-->
+<!--                <span class="icon">-->
+<!--                  <i class="fas fa-sort-up"></i>-->
+<!--                </span>-->
+<!--                <span class="icon">-->
+<!--                  <i class="fas fa-sort-down"></i>-->
+<!--                </span>-->
+<!--              </span>-->
+              <span class="column-title">Date d'exécution</span>
+            </span>
+          </div>
+          <div class="column is-1 has-text-left">
+            <span class="is-sortable">
+<!--              <span class="sort-icons">-->
+<!--                <span class="icon">-->
+<!--                  <i class="fas fa-sort-up"></i>-->
+<!--                </span>-->
+<!--                <span class="icon">-->
+<!--                  <i class="fas fa-sort-down"></i>-->
+<!--                </span>-->
+<!--              </span>-->
+              <span class="column-title">Durée</span>
+            </span> </div>
+            <div class="column  is-1 has-text-left">
+                <span class="is-sortable">
+<!--                  <span class="sort-icons">-->
+<!--                    <span class="icon">-->
+<!--                      <i class="fas fa-sort-up"></i>-->
+<!--                    </span>-->
+<!--                    <span class="icon">-->
+<!--                      <i class="fas fa-sort-down"></i>-->
+<!--                    </span>-->
+<!--                  </span>-->
+                  <span class="column-title">Infos</span>
+                </span>
+          </div>
+          <div class="column  is-1 has-text-left">
+            <span class="is-sortable">
+<!--              <span class="sort-icons">-->
+<!--                <span class="icon">-->
+<!--                  <i class="fas fa-sort-up"></i>-->
+<!--                </span>-->
+<!--                <span class="icon">-->
+<!--                  <i class="fas fa-sort-down"></i>-->
+<!--                </span>-->
+<!--              </span>-->
+              <span class="column-title">Erreurs</span>
+            </span>
+          </div>
+          <div class="column  is-1 has-text-left">
+            <span class="column-title"></span>
+          </div>
+        </div>
+        <div class="data-list">
+          <div *ngFor="let slug of allSlugSessionInfoList; let i=index; let odd=odd; let even=even;">
+            <div class="data columns is-multiline is-vcentered is-marginless"
+             *ngIf="slug._id.session_list"
+              [ngClass]="{ odd: odd, even: even }">
+              <div class="column is-3 has-text-left">
+                {{slug._id.slug}} <br>
+                <div class="small-text">
+                  <p>{{slug._id.uuid}}</p>
+                </div>
+              </div>
+              <div class="column is-1 has-text-left">
+                {{ slug._id.uuid_suffix === 'full' ? 'Oui' : 'Non'}}
+              </div>
+              <div class="column is-1 has-text-left">
+                {{slug._id.session_list.length}}
+              </div>
+              <div class="column is-2 has-text-left">
+                {{slug._id.completionDate.$date | date: 'EE dd/MM/yyyy hh:mm:ss'}}
+              </div>
+              <div class="column is-1 has-text-left">
+                {{slug._id.duration.hours}}:{{slug._id.duration.minutes}}:{{slug._id.duration.seconds}}
+              </div>
+              <div class="column  is-1 has-text-left actions">
+                  {{ slug._id.count.INFO ? slug._id.count.INFO: 0}}
+              </div>
+              <div class="column  is-1 has-text-left actions">
+                {{ slug._id.count.ERROR ? slug._id.count.ERROR: 0}}
+              </div>
+              <div class="column  is-1 has-text-left actions">
+                <a class="button button-gl is-left"
+                  [routerLink]="['/','datalogs','report', slug._id.slug, id, 'session']">
+                  <i class="fas fa-eye"></i>
+                </a>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+      <div class="column">
+        <app-paginator *ngIf="paginator.length > 0" [length]="paginator.length" [pageSize]="paginator.limit"
+          [pageSizeOptions]="paginator.pageSizeOptions" [pageIndex]="paginator.pageIndex" [pagesToShow]="5"
+          [showFirstLastButtons]="true" (page)="changePagination($event)" (pageSizeChanged)="changePageSize($event)">
+        </app-paginator>
+      </div>
+    </div>
+  </div>
+</div>
diff --git a/src/app/components/logs-dashboard/report/logs-pre-report/logs-pre-report.component.scss b/src/app/components/logs-dashboard/report/logs-pre-report/logs-pre-report.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..aaa6c7250fd49a1fdb867a75fa5d537b47935d87
--- /dev/null
+++ b/src/app/components/logs-dashboard/report/logs-pre-report/logs-pre-report.component.scss
@@ -0,0 +1,38 @@
+@import "../../../../../scss/variables.scss";
+@import "../../../../../../node_modules/bulma/sass/utilities/_all.sass";
+
+.small-text{
+    font-size:0.8em;
+}
+.full-width {
+  width: 100%;
+}
+
+h1 {
+  text-align: center
+}
+
+.icon {
+  cursor: pointer;
+  &:hover {
+    .fa-plus {
+      color: lightblue;
+    }
+    .fa-trash {
+      color: #d5232a;
+    }
+  }
+}
+
+.hidden {
+  display: none;
+}
+
+.bold-text {
+  font-weight:bold;
+  display:inline;
+}
+.italic-text {
+  font-weight:italic;
+  display:inline;
+}
\ No newline at end of file
diff --git a/src/app/components/logs-dashboard/report/logs-pre-report/logs-pre-report.component.ts b/src/app/components/logs-dashboard/report/logs-pre-report/logs-pre-report.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f665bdbeb7b68f85ce1a21d0119e1d4714ddce1c
--- /dev/null
+++ b/src/app/components/logs-dashboard/report/logs-pre-report/logs-pre-report.component.ts
@@ -0,0 +1,181 @@
+import { Component, OnInit } from '@angular/core';
+import { ActivatedRoute, ParamMap } from '@angular/router';
+import { FormGroup } from '@angular/forms';
+import { filter, switchMap } from 'rxjs/operators';
+import { DataLogsService } from 'src/app/services/data-logs.service';
+import { IPageHeaderInfo } from 'src/app/models/page.model';
+import { PaginatorOptions } from 'src/app/models/paginator-options.model';
+import { forkJoin } from 'rxjs';
+
+// in this component the pagination  and filter cannot be handled with service and http request
+// because the displayed table is from a composite object .  Two requests for one line of the table
+@Component({
+  selector: 'app-logs-pre-report',
+  templateUrl: './logs-pre-report.component.html',
+  styleUrls: ['./logs-pre-report.component.scss'],
+})
+export class LogsPreReportComponent implements OnInit {
+  pageHeaderInfo: IPageHeaderInfo = {
+    title: "Logs d'indexation: recherche affinée",
+  };
+  previousUrl: string = '/datalogs';
+  nbOfItemsPerPage: number = 10;
+  responseArray: any = [];
+  form: FormGroup;
+  uuid: any = '';
+  title: string;
+  type: string;
+  id: string;
+  foundSlug: string = '';
+  completeSlugsList: any = [];
+  nbObjects: number = 0;
+  isSession: boolean = false;
+  allSlugSessionInfoList: any = [];
+  // Paginator options
+  paginator: PaginatorOptions;
+  pageSizeOptions = [5, 10, 25, 100];
+  pageIndex: number = 1;
+
+  constructor(
+    private _dataLogsService: DataLogsService,
+    public _route: ActivatedRoute,
+  ) {
+    this.paginator = {
+      pageIndex: 1,
+      length: 1,
+      limit: 10,
+      pageSizeOptions: [5, 10, 20],
+    };
+  }
+
+  ngOnInit() {
+    this.title = this._route.snapshot.data.title;
+    this.id = this._route.snapshot.paramMap.get('id');
+    this.type = this._route.snapshot.paramMap.get('type');
+    if (this.type === 'session') {
+      this.isSession = true;
+    }
+    this.getComplementaryInformation();
+  }
+
+  completeSlugsInfos(slugList, sessionId) {
+    this.allSlugSessionInfoList = [];
+    for (let i = 0; i < slugList.length; i += 1) {
+      this.getAllInfoForOneSlugSession(slugList[i], sessionId, 'slug');
+    }
+  }
+
+  completeSessionsInfos(slug, sessionList) {
+    // console.log('slug', slug, 'sessionList', sessionList);
+    this.allSlugSessionInfoList = [];
+    for (let i = 0; i < sessionList.length; i += 1) {
+      this.getAllInfoForOneSlugSession(slug, sessionList[i], 'session');
+    }
+  }
+  getAllInfoForOneSlugSession(slug, sessionId, type) {
+    if (type === 'session') {
+      forkJoin(
+        this._dataLogsService.getAllInfoForSlugSession(slug, sessionId),
+        this._dataLogsService.getAllInfoForOneSession(sessionId),
+      )
+      .subscribe(([res1, res2]) => {
+        let sessionData = {};
+        res1[0]._id.completionDate.$date = new Date(res1[0]._id.completionDate.$date);
+        sessionData = res1[0];
+        sessionData['_id']['slug_list'] = res2[0]['all'];
+        this.allSlugSessionInfoList.push(sessionData);
+      });
+    } else {
+      forkJoin(
+        this._dataLogsService.getAllInfoForSlugSession(slug, sessionId),
+        this._dataLogsService.getAllInfoForOneSlug(slug),
+        this._dataLogsService.getSlugFromSessionId(sessionId),
+      )
+      .subscribe(([res1, res2, res3]) => {
+        let sessionData = {};
+        res1[0]._id.completionDate.$date = new Date(res1[0]._id.completionDate.$date);
+        sessionData = res1[0];
+        sessionData['_id']['session_list'] = res2[0]['all'];
+        this.allSlugSessionInfoList.push(sessionData);
+        this.completeSlugsList = res3[0]['slug_list'];
+      });
+    }
+  }
+
+  getComplementaryInformation() {
+    this._route.paramMap.pipe(
+      filter((paramMap: ParamMap) => (paramMap.get('id') !== null)),
+      filter((paramMap: ParamMap) => (paramMap.get('type') !== null)),
+      switchMap((paramMap: ParamMap) => this._dataLogsService.getComplementaryFromSlugOrSession(
+        paramMap.get('type'),
+        paramMap.get('id'))))
+      .subscribe((response) => {
+
+        // HERE WE HAVE AN ARRAY TO DEAL WITH PAGINATION, NOT WITH AN HTTP GET REQUEST WITH PAGES
+        this.responseArray = response[0];
+        if (this.type === 'session') {
+          this.nbObjects = response[0].slug_list.length;
+          this.paginator.length = response[0].slug_list.length;
+          this.completeSlugsInfos(response[0].slug_list.slice(0, this.paginator.limit), this.id);
+        } else {
+          this.nbObjects = response[0].session_id_list.length;
+          this.paginator.length = response[0].session_id_list.length;
+          this.getUuidFromSlug(this.id);
+          this.completeSessionsInfos(this.id, response[0].session_id_list.slice(0, this.paginator.limit));
+        }
+      });
+  }
+
+  // When pagination is changed by user, we update with new pagination options
+  changePagination(pageIndex) {
+    this.paginator.pageIndex = pageIndex;
+    if (this.type === 'slug') {
+      this.completeSessionsInfos(this.id, this.responseArray.session_id_list.slice(
+        (this.paginator.pageIndex - 1) * (this.paginator.limit),
+        (this.paginator.pageIndex - 1) * (this.paginator.limit) + this.paginator.limit,
+      ));
+    } else {
+      this.completeSlugsInfos(
+        this.responseArray.slug_list.slice(
+        (this.paginator.pageIndex - 1) * (this.paginator.limit),
+        (this.paginator.pageIndex - 1) * (this.paginator.limit) + this.paginator.limit,
+      ),
+        this.id);
+    }
+  }
+
+  getAllInfoForOneSlug($event) {
+    if (this.foundSlug === '') {
+      this.getComplementaryInformation();
+
+    } else {
+      this._dataLogsService.getAllInfoForOneSlug(this.foundSlug).subscribe((results) => {
+        this.allSlugSessionInfoList = [];
+        for (const result of results[0]['all']) {
+          if (result['session_id'] === this.id) {
+            this.allSlugSessionInfoList[0] = { _id : result };
+            this.allSlugSessionInfoList[0]['_id']['slug'] = results[0]['_id']['slug'];
+            this.allSlugSessionInfoList[0]['_id']['uuid'] = results[0]['_id']['uuid'];
+            this.allSlugSessionInfoList[0]['_id']['session_list'] = [this.id];
+          }
+        }
+        // this.paginator.limit = this._dataLogsService.limit;
+        this.paginator.limit = this.nbOfItemsPerPage;
+        this.paginator.length = 1; // nb total of elements, cannot use results[0]['all'].length() here
+        this.paginator.pageIndex = this._dataLogsService.pageNumber;
+      },
+    );
+    }
+  }
+  getUuidFromSlug(slug) {
+    this._dataLogsService.getUuidFromSlug(slug).subscribe((result) => {
+      this.uuid = result[0]['uuid_list'][0];
+    },
+    );
+  }
+  changePageSize(pageSize) {
+    this.paginator.pageIndex = 1;
+    this.paginator.limit = pageSize;
+  }
+
+}
diff --git a/src/app/components/logs-dashboard/report/logs-report/logs-report.component.html b/src/app/components/logs-dashboard/report/logs-report/logs-report.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..8fe82d1fb8e34cc191b2ae6655eea2a2d6d4acb1
--- /dev/null
+++ b/src/app/components/logs-dashboard/report/logs-report/logs-report.component.html
@@ -0,0 +1,28 @@
+<div class="section page-container">
+  <app-page-header [pageInfo]="pageHeaderInfo" [goToThisUrl]='previousUrl' [hideBackButton]="false"></app-page-header>
+  <span class="bold-text">slug : </span>
+  <span> {{slug}}</span>
+  <br>
+  <span class="bold-text">uuid : </span>
+  <span> {{uuid}} </span>
+  <br>
+  <span class="bold-text">sessionId : </span>
+  <span> {{sessionId}} </span>
+  <hr>
+  <ul class="tabs-container">
+    <li *ngFor="let tab of tabs" (click)="tabsToggler(tab.name)" [ngClass]="{'is-active':tab.isActive}">
+      <!-- [routerLink]="tab.fullRouterLinkPath" routerLinkActive="is-active" -->
+      <span class="tab-label">{{ tab.name }}</span>
+    </li>
+  </ul>
+  <div>
+      <h2>{{errorMessage}}</h2>
+
+  </div>
+  <app-logs-graph [hidden]="!isToggled || errorMessage!='' " [childUuid]="uuid" [childSessionId]="sessionId" [childSlug]="slug">
+  </app-logs-graph>
+  <app-logs-info [hidden]="isToggled||isToggled==undefined" [childUuid]="uuid" [childSessionId]="sessionId"
+    [childSlug]="slug"></app-logs-info>
+  <app-logs-error [hidden]="isToggled!=undefined" [childUuid]="uuid" [childSessionId]="sessionId" [childSlug]="slug">
+  </app-logs-error>
+</div>
diff --git a/src/app/components/logs-dashboard/report/logs-report/logs-report.component.scss b/src/app/components/logs-dashboard/report/logs-report/logs-report.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..82cd2bd4d3c39bf7de735608a6baad5887f52a18
--- /dev/null
+++ b/src/app/components/logs-dashboard/report/logs-report/logs-report.component.scss
@@ -0,0 +1,144 @@
+@import "../../../../../scss/variables.scss";
+@import "../../../../../../node_modules/bulma/sass/utilities/_all.sass";
+
+figure {
+    text-align: center;
+  }
+  figure img{
+    max-width: 9.3rem;
+    display: inline-block;
+    margin-top: 20px;
+  }
+
+  .card-header-title {
+    justify-content: center;
+  }
+
+  .mini-log-card{
+    margin-top: 1.25rem;
+    padding: 1.25rem;
+    background-color: yellow;
+
+  }
+  .input-field{
+    width: 200%;
+    margin-top: 1.25rem;
+  }
+
+  .hidden {
+    display: none;
+  }
+  .icon {
+    cursor: pointer;
+    &:hover {
+      .fa-plus {
+        color: lightblue;
+      }
+      .fa-trash {
+        color: #d5232a;
+      }
+    }
+  }
+
+  .bold-text {
+    font-weight:bold;
+    display:inline;
+  }
+
+  .tabs-container {
+  display: flex;
+  align-content: stretch;
+  flex-wrap: wrap;
+  border-bottom: 1px solid $grey-super-light-color;
+
+  li {
+    display: flex;
+    align-items: center;
+    padding-right: 2rem;
+    padding-bottom: 0.4rem;
+    padding-top: 0.4rem;
+    cursor: pointer;
+    position: relative;
+    margin-bottom: 1px;
+    width: 50%;
+
+    @media screen and (min-width: $tablet) {
+      width: 33%;
+      padding-right: 3rem;
+    }
+
+    @media screen and (min-width: $desktop) {
+      width: 20%;
+    }
+  }
+
+  li:last-child {
+    padding-right: 0;
+  }
+
+  .tab-label {
+    font-size: 1rem;
+    font-weight: 500;
+    color: $brand-color;
+  }
+
+  li:hover {
+    .tab-label {
+      font-weight: 700;
+    }
+  }
+
+  li:focus{
+    .tab-label {
+      color: $tomato-color;
+      font-weight: 700;
+    }
+  }
+  li.is-active {
+    .tab-label {
+      color: $tomato-color;
+      font-weight: 600;
+    }
+  }
+
+  li::after {
+    content: '';
+    display: block;
+    height: .1rem;
+    background: $tomato-color;
+    width: 0;
+    position: absolute;
+    bottom: -.1rem;
+  }
+
+  li.is-active::after {
+    transition: width 0.3s;
+    width: 100%;
+  }
+
+
+
+  .full-width {
+    width: 100%;
+  }
+
+  h1 {
+    text-align: center
+  }
+
+  .icon {
+    cursor: pointer;
+    &:hover {
+      .fa-plus {
+        color: lightblue;
+      }
+      .fa-trash {
+        color: #d5232a;
+      }
+    }
+  }
+  .hidden {
+    display: none;
+  }
+
+}
diff --git a/src/app/components/logs-dashboard/report/logs-report/logs-report.component.ts b/src/app/components/logs-dashboard/report/logs-report/logs-report.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..291c80c911729049a79e429b735899165f8b827f
--- /dev/null
+++ b/src/app/components/logs-dashboard/report/logs-report/logs-report.component.ts
@@ -0,0 +1,102 @@
+import { Component, OnInit } from '@angular/core';
+import { ActivatedRoute, Router } from '@angular/router';
+import { FormBuilder, FormGroup } from '@angular/forms';
+import { DataLogsService } from 'src/app/services/data-logs.service';
+import { IPageHeaderInfo } from 'src/app/models/page.model';
+import { ISimpleTab } from 'src/app/models/basic-tabs.model';
+
+@Component({
+  selector: 'app-logs-report',
+  templateUrl: './logs-report.component.html',
+  styleUrls: ['./logs-report.component.scss'],
+})
+export class LogsReportComponent implements OnInit {
+  pageHeaderInfo: IPageHeaderInfo = {
+    title: "Rapport final d'indexation",
+  };
+  errorMessage: string = '';
+  isToggled: boolean = true;
+  form: FormGroup;
+  title: string;
+  slug: string;
+  uuid: string;
+  sessionId: string;
+  urlCode: string = '';
+  previousUrl: string;
+  tabs: ISimpleTab[] = [
+    {
+      name: 'Graphique',
+      isActive: true,
+    },
+    {
+      name: 'Infos',
+      isActive: false,
+    },
+    {
+      name: 'Erreurs',
+      isActive: false,
+    },
+  ];
+  constructor(
+    private _dataLogsService: DataLogsService,
+    private _route: ActivatedRoute,
+    private _router: Router,
+    private _fb: FormBuilder,
+  ) {
+  }
+
+  ngOnInit() {
+    this.title = this._route.snapshot.data.title;
+    this.sessionId = this._route.snapshot.paramMap.get('sessionId');
+    this.slug = this._route.snapshot.paramMap.get('slug');
+    this.urlCode = this._route.snapshot.paramMap.get('urlCode');
+    if (this.urlCode === 'session') {
+      this.previousUrl = `/datalogs/preReport/session/${this.sessionId}`;
+
+    } else if (this.urlCode === 'slug') {
+      this.previousUrl = `/datalogs/preReport/slug/${this.slug}`;
+    } else {
+      this.previousUrl = '/datalogs';
+    }
+    this.getUuidFromSlug(this.slug);
+  }
+
+  getUuidFromSlug(slug) {
+    this._dataLogsService.getUuidFromSlug(slug).subscribe((result) => {
+      try {
+        this.uuid = result[0].uuid_list[0];
+      } catch (e) {
+        this.errorMessage = "Pas d'uuid pour ce slug";
+        console.log(this.errorMessage, e);
+      }
+    },
+    );
+  }
+
+  tabsToggler(tabName) {
+    if (tabName === 'Graphique') {
+      console.log(this.isToggled);
+      this.isToggled = true;
+      this.tabs[1].isActive = false;
+      this.tabs[0].isActive = true;
+      this.tabs[2].isActive = false;
+
+    }
+    if (tabName === 'Infos') {
+      console.log(this.isToggled);
+      this.isToggled = false;
+      this.tabs[1].isActive = true;
+      this.tabs[0].isActive = false;
+      this.tabs[2].isActive = false;
+
+    }
+    if (tabName === 'Erreurs') {
+      console.log(this.isToggled);
+      this.isToggled = undefined;
+      this.tabs[2].isActive = true;
+      this.tabs[1].isActive = false;
+      this.tabs[0].isActive = false;
+    }
+  }
+
+}
diff --git a/src/app/components/media/detail/media-detail.component.html b/src/app/components/media/detail/media-detail.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..7090aa943f775d8028fb0db21a90dd36ea6d21ce
--- /dev/null
+++ b/src/app/components/media/detail/media-detail.component.html
@@ -0,0 +1,59 @@
+<section class="section page-container" *ngIf="media">
+  <app-page-header [pageInfo]="{title: title}"></app-page-header>
+  <div class="columns is-centered">
+    <div class="column is-8">
+      <div class="card">
+        <div class="card-image">
+          <figure class="image">
+            <img [src]="media.url" alt="Preview du media" onerror="this.src='./assets/img/default-file-logo.png';">
+          </figure>
+        </div>
+        <div class="card-content">
+
+          <div class="content">
+            <div>
+              <span class="has-text-weight-bold">URL: </span>
+              <span *ngIf="media.url; else emptyProperty">{{media.url}}</span>
+            </div>
+            <br>
+            <div>
+              <span class="has-text-weight-bold">Alt: </span>
+              <span *ngIf="media.alt; else emptyProperty">{{media.alt}}</span>
+            </div>
+            <br>
+            <div>
+              <span class="has-text-weight-bold">Caption: </span>
+              <span *ngIf="media.caption; else emptyProperty">{{media.caption}}</span>
+            </div>
+            <br>
+            <div>
+              <span class="has-text-weight-bold">Date de création: </span>
+              <span
+                *ngIf="media.creationDate; else emptyProperty">{{media.creationDate | date:'EE dd/MM/yyyy HH:mm:ss'}}</span>
+            </div>
+            <br>
+            <div>
+              <span class="has-text-weight-bold">Date de modification: </span>
+              <span
+                *ngIf="media.creationDate; else emptyProperty">{{media.updateDate | date:'EE dd/MM/yyyy HH:mm:ss'}}</span>
+            </div>
+            <br>
+            <div *ngIf="isImage">
+              <span class="has-text-weight-bold">Ghost snippet: </span>
+              <span class="comment">(Ce code html est fait pour intégrer l'image en taille normal. Pour une taille
+                'wide' ajoutez la classe 'kg-width-wide' à la balise figure. Pour une taille 'full' ajoutez la classe
+                'kg-width-full' à la balise figure)</span>
+              <pre class="hljs code-preview" [innerHTML]="ghostSnippet">
+                <!-- <code ></code> -->
+              </pre>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</section>
+
+<ng-template #emptyProperty>
+  <span class="empty-property">Non renseigné</span>
+</ng-template>
\ No newline at end of file
diff --git a/src/app/components/media/detail/media-detail.component.scss b/src/app/components/media/detail/media-detail.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..92575e3163a599b02afc6efaf4d4e8ef45cfc3e1
--- /dev/null
+++ b/src/app/components/media/detail/media-detail.component.scss
@@ -0,0 +1,37 @@
+@import '../../../../scss/variables.scss';
+
+figure {
+  text-align: center;
+}
+
+figure img {
+  max-width: 150px;
+  display: inline-block;
+  margin-top: 20px;
+}
+
+.card-header-title {
+  justify-content: center;
+}
+
+.code-preview {
+  white-space: pre-wrap;
+  /* css-3 */
+  white-space: -moz-pre-wrap;
+  /* Mozilla, since 1999 */
+  white-space: -pre-wrap;
+  /* Opera 4-6 */
+  white-space: -o-pre-wrap;
+  /* Opera 7 */
+  word-wrap: break-word;
+  /* Internet Explorer 5.5+ */
+
+  padding: 0.5rem;
+  margin-top: 0.5rem;
+}
+
+.comment {
+  font-size: 12px;
+  font-style: italic;
+  color: $grey-super-light-color;
+}
diff --git a/src/app/components/media/detail/media-detail.component.ts b/src/app/components/media/detail/media-detail.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..03b0919af84fb3118ebe52143101c777a0070464
--- /dev/null
+++ b/src/app/components/media/detail/media-detail.component.ts
@@ -0,0 +1,55 @@
+
+import { switchMap } from 'rxjs/operators';
+import { Component, OnInit } from '@angular/core';
+import { ActivatedRoute, ParamMap } from '@angular/router';
+import { Media } from '../../../models/media.model';
+import { MediaService } from '../../../services';
+import { highlight } from 'highlight.js';
+import * as isImageUrl from 'is-image-url';
+@Component({
+  selector: 'app-media-detail',
+  templateUrl: './media-detail.component.html',
+  styleUrls: ['./media-detail.component.scss'],
+})
+export class MediaDetailComponent implements OnInit {
+
+  media: Media;
+  title: string;
+
+  constructor(
+    private _route: ActivatedRoute,
+    private _mediaService: MediaService,
+  ) {
+  }
+
+  ngOnInit(): void {
+    this.title = this._route.snapshot.data.title;
+    this._route.paramMap.pipe(
+      switchMap((params: ParamMap) => this._mediaService.findById(params.get('id'))))
+      .subscribe((media: Media) => {
+        this.media = media;
+      });
+  }
+
+  get isImage() {
+    return isImageUrl(this.media.url);
+  }
+
+  // [WARNING] Keep that weird indentation for correct display on the web page
+  get ghostSnippet() {
+    let html;
+    if (this.media.caption) {
+      html = `<figure class="kg-card kg-image-card kg-card-hascaption">
+  <img src="${this.media.url}" alt="${this.media.alt}" class="kg-image">
+  <figcaption>
+    ${this.media.caption}
+  </figcaption>
+</figure>`;
+    } else {
+      html = `<figure class="kg-card kg-image-card">
+  <img src="${this.media.url}" alt="${this.media.alt}" class="kg-image">
+</figure>`;
+    }
+    return highlight('html', html).value;
+  }
+}
diff --git a/src/app/components/media/edit/media-form.component.html b/src/app/components/media/edit/media-form.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..bee0a47a9583528ae7de6d3f31d192fbb8a4e362
--- /dev/null
+++ b/src/app/components/media/edit/media-form.component.html
@@ -0,0 +1,45 @@
+<section class="section page-container" *ngIf="media">
+  <app-page-header [pageInfo]="{title: title}"></app-page-header>
+  <form [formGroup]="form" (ngSubmit)="onSubmit()" class="columns is-centered is-marginless is-multiline">
+    <div class="column is-7">
+      <input type="hidden" formControlName="_id" [value]="media._id">
+
+      <app-image-upload [fieldParams]="logoFieldParams" (fileChanged)="logoChanged($event)"
+        (imageRemoved)="removeLogo()" *ngIf="!media.url; else FilePreview">
+      </app-image-upload>
+
+      <ng-template #FilePreview>
+        <div class="file-preview-container">
+          <img [src]="media.url" alt="" onerror="this.src='./assets/img/default-file-logo.png';">
+        </div>
+      </ng-template>
+
+      <div class="field" *ngIf="media._id">
+        <label class="label" for="url">URL</label>
+        <div class="control">
+          <input class="input" id="url" type="text" [value]="media.url" disabled>
+        </div>
+      </div>
+
+
+      <div class="field">
+        <label class="label" for="alt">Alt</label>
+        <div class="control">
+          <input class="input" type="text" formControlName="alt" id="alt">
+        </div>
+      </div>
+
+      <div class="field">
+        <label class="label" for="caption">Caption</label>
+        <div class="control">
+          <input class="input" type="text" formControlName="caption" id="caption">
+        </div>
+      </div>
+
+      <br>
+      <div class="has-text-right">
+        <button class="button button-gl" type="submit" [disabled]="formInvalid == true">Valider</button>
+      </div>
+    </div>
+  </form>
+</section>
\ No newline at end of file
diff --git a/src/app/components/media/edit/media-form.component.scss b/src/app/components/media/edit/media-form.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..a7d23f79fef654ceb378a90dbcf0026a70528d42
--- /dev/null
+++ b/src/app/components/media/edit/media-form.component.scss
@@ -0,0 +1,19 @@
+.full-width {
+  width: 100%;
+}
+
+.page-container {
+  position: relative;
+}
+
+h1 {
+  text-align: center
+}
+
+.file-preview-container {
+  text-align: center;
+
+  img {
+    max-height: 5rem;
+  }
+}
diff --git a/src/app/components/media/edit/media-form.component.ts b/src/app/components/media/edit/media-form.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4f2b06928bdc3db593e7f2ded4d1fa5edfcbcbba
--- /dev/null
+++ b/src/app/components/media/edit/media-form.component.ts
@@ -0,0 +1,132 @@
+import { Component, OnInit } from '@angular/core';
+import { ActivatedRoute, ParamMap, Router } from '@angular/router';
+import { FormBuilder, FormGroup } from '@angular/forms';
+import { filter, switchMap } from 'rxjs/operators';
+import { IImageUploadFieldParams } from 'src/app/models/image-upload.model';
+import { NotificationService, MediaService } from 'src/app/services';
+import { Media, MediaDTO } from '../../../models/media.model';
+
+@Component({
+  selector: 'app-media-form',
+  templateUrl: './media-form.component.html',
+  styleUrls: ['./media-form.component.scss'],
+})
+export class MediaFormComponent implements OnInit {
+
+  media: Media;
+  form: FormGroup;
+  file: File;
+  logoFieldParams: IImageUploadFieldParams = {
+    inputName: 'file',
+    label: 'File',
+    existingImageUrl: null,
+    isRequired: true,
+  };
+  title: string;
+
+  constructor(
+    private _mediaService: MediaService,
+    private _notificationService: NotificationService,
+    private _route: ActivatedRoute,
+    private _router: Router,
+    private _fb: FormBuilder,
+  ) {
+  }
+
+  ngOnInit() {
+    this.title = this._route.snapshot.data.title;
+    this.initForm();
+
+    this._route.paramMap.pipe(
+      filter((paramMap: ParamMap) => (paramMap.get('id') !== null)),
+      switchMap((paramMap: ParamMap) => this._mediaService.findById(paramMap.get('id'))))
+      .subscribe((media: Media) => {
+
+        this.media = media;
+
+        this.logoFieldParams.existingImageUrl = media.url;
+
+        this.form = this._fb.group(
+          {
+            _id: [this.media._id],
+            alt: [media.alt ? media.alt : null],
+            caption: [media.caption ? media.caption : null],
+          });
+      });
+
+  }
+
+  initForm() {
+    this.media = new Media();
+    this.logoFieldParams.existingImageUrl = null;
+    this.form = this._fb.group(
+      {
+        _id: [this.media._id],
+        alt: [this.media.alt ? this.media.alt : null],
+        caption: [this.media.caption ? this.media.caption : null],
+      });
+  }
+
+  onSubmit() {
+    if (!this.formInvalid) {
+
+      if (this.media._id) {
+        this._mediaService.update(new MediaDTO(this.form.value)).subscribe(
+          (mediaCreated) => {
+            this._notificationService.notify({
+              message: 'Le média a été mis à jour avec succès.',
+              type: 'success',
+            });
+            this._router.navigate(['/media', mediaCreated._id]);
+          },
+          (err) => {
+            this._notificationService.notify({
+              message: 'Une erreur est survenue lors de la mise à jour du média',
+              type: 'error',
+            });
+          },
+        );
+      } else {
+        this._mediaService.uploadFile(this.file, new MediaDTO(this.form.value)).subscribe(
+          (mediaCreated) => {
+            this._notificationService.notify({
+              message: 'Le fichier a été uploadé avec succès.',
+              type: 'success',
+            });
+            this._router.navigate(['/media', mediaCreated._id]);
+          },
+          (err) => {
+            this._notificationService.notify({
+              message: 'Une erreur est survenue lors de l\'upload du fichier',
+              type: 'error',
+            });
+          },
+        );
+      }
+    }
+  }
+
+  // Getters for each property
+  get alt() {
+    return this.form.controls['alt'];
+  }
+
+  get caption() {
+    return this.form.controls['caption'];
+  }
+
+  get formInvalid() {
+    return this.form.invalid ||
+      (!this.media.url && !this.file);
+  }
+
+  logoChanged(fileList: FileList) {
+    if (fileList && fileList.length > 0) {
+      this.file = fileList[0];
+    }
+  }
+
+  removeLogo() {
+    this.form.get('file').setValue(null);
+  }
+}
diff --git a/src/app/components/media/list/media.component.html b/src/app/components/media/list/media.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..5de5ffb98aa89ef2d7d4364b0aaca60b9c752cf6
--- /dev/null
+++ b/src/app/components/media/list/media.component.html
@@ -0,0 +1,161 @@
+<div class="section page-container">
+  <app-page-header [pageInfo]="pageHeaderInfo" [hideBackButton]="true"></app-page-header>
+  <div class="add-item-link has-text-right">
+    <a class="button button-gl" [routerLink]="['new']">
+      Ajouter
+    </a>
+  </div>
+  <div class="table entity-list-table" *ngIf="media">
+    <div class="header columns is-marginless">
+      <div class="column is-1 has-text-centered">
+        <span class="column-title">Logo</span>
+      </div>
+      <div class="column is-4">
+        <span (click)="sortBy('url')" class="is-sortable">
+          <span class="sort-icons">
+            <span class="icon">
+              <i class="fas fa-sort-up"
+                [ngClass]="{'icon-red': sortOptions.value === 'url' && sortOptions.order === 'desc'}"></i>
+            </span>
+            <span class="icon">
+              <i class="fas fa-sort-down"
+                [ngClass]="{'icon-red': sortOptions.value === 'url' && sortOptions.order === 'asc'}"></i>
+            </span>
+          </span>
+          <span class="column-title" [ngClass]="{'active': sortOptions.value === url}">URL</span>
+        </span>
+      </div>
+      <div class="column is-1">
+        <span (click)="sortBy('alt')" class="is-sortable">
+          <span class="sort-icons">
+            <span class="icon">
+              <i class="fas fa-sort-up"
+                [ngClass]="{'icon-red': sortOptions.value === 'alt' && sortOptions.order === 'desc'}"></i>
+            </span>
+            <span class="icon">
+              <i class="fas fa-sort-down"
+                [ngClass]="{'icon-red': sortOptions.value === 'alt' && sortOptions.order === 'asc'}"></i>
+            </span>
+          </span>
+          <span class="column-title" [ngClass]="{'active': sortOptions.value === alt}">Alt</span>
+        </span>
+      </div>
+      <div class="column is-1">
+        <span (click)="sortBy('caption')" class="is-sortable">
+          <span class="sort-icons">
+            <span class="icon">
+              <i class="fas fa-sort-up"
+                [ngClass]="{'icon-red': sortOptions.value === 'caption' && sortOptions.order === 'desc'}"></i>
+            </span>
+            <span class="icon">
+              <i class="fas fa-sort-down"
+                [ngClass]="{'icon-red': sortOptions.value === 'caption' && sortOptions.order === 'asc'}"></i>
+            </span>
+          </span>
+          <span class="column-title" [ngClass]="{'active': sortOptions.value === caption}">Légende</span>
+        </span>
+      </div>
+      <div class="column is-2">
+        <span (click)="sortBy('creationDate')" class="is-sortable">
+          <span class="sort-icons">
+            <span class="icon">
+              <i class="fas fa-sort-up"
+                [ngClass]="{'icon-red': sortOptions.value === 'creationDate' && sortOptions.order === 'desc'}"></i>
+            </span>
+            <span class="icon">
+              <i class="fas fa-sort-down"
+                [ngClass]="{'icon-red': sortOptions.value === 'creationDate' && sortOptions.order === 'asc'}"></i>
+            </span>
+          </span>
+          <span class="column-title">Date de création</span>
+        </span>
+      </div>
+      <div class="column is-3 has-text-centered">
+        <span class="column-title">Actions</span>
+      </div>
+    </div>
+    <div class="data-list">
+      <div class="data columns is-multiline is-vcentered is-marginless"
+        *ngFor="let item of media; let i=index; let odd=odd; let even=even;" [ngClass]="{ odd: odd, even: even }">
+        <div class="column is-1 has-text-centered">
+          <img class="entity-logo-in-list" [src]="item.url" alt=""
+            onerror="this.src='./assets/img/default-file-logo.png';">
+        </div>
+        <div class="column is-4">
+          <span>{{ item.url}}</span>
+        </div>
+        <div class="column is-1">
+          <span>{{ item.alt}}</span>
+        </div>
+        <div class="column is-1">
+          <span>{{ item.caption}}</span>
+        </div>
+        <div class="column is-2">
+          <span>{{ item.creationDate | date:'EE dd/MM/yyyy HH:mm:ss'}}</span>
+        </div>
+        <div class="column is-3 has-text-centered actions">
+          <button
+            title="Copier dans le presse-papier le snippet html à copier dans un article/page ghost pour embarquer l'image en mode normal"
+            class="button copy-button" *ngIf="isImage(item.url)" [ngClass]="{'copied': copiedIndex === i}"
+            (click)="copyGhostHTMLSnippet(item, '', i)">
+            <span *ngIf="copiedIndex === i && copiedSize === ''; else imageNormal">Copié !</span>
+            <ng-template #imageNormal>
+              <svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
+                <g fill="#242b3f" fill-rule="nonzero">
+                  <path d="M2 3a1 1 0 0 1 0-2h12a1 1 0 0 1 0 2H2zm0 12a1 1 0 0 1 0-2h12a1 1 0 0 1 0 2H2z" opacity=".6">
+                  </path>
+                  <path d="M2 5h12a1 1 0 0 1 1 1v4.001a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V6a1 1 0 0 1 1-1z"></path>
+                </g>
+              </svg>
+            </ng-template>
+          </button>
+          <button
+            title="Copier dans le presse-papier le snippet html à copier dans un article/page ghost pour embarquer l'image en mode wide"
+            class="button copy-button" *ngIf="isImage(item.url)" [ngClass]="{'copied': copiedIndex === i}"
+            (click)="copyGhostHTMLSnippet(item, 'kg-width-wide', i)">
+            <span *ngIf="copiedIndex === i && copiedSize === 'kg-width-wide'; else imageWide">Copié !</span>
+            <ng-template #imageWide>
+              <svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
+                <g fill="#242b3f" fill-rule="nonzero">
+                  <path d="M6 3a1 1 0 0 1 0-2h4a1 1 0 0 1 0 2H6zm0 12a1 1 0 0 1 0-2h4a1 1 0 0 1 0 2H6z" opacity=".6">
+                  </path>
+                  <path d="M2 5h12a1 1 0 0 1 1 1v4.001a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V6a1 1 0 0 1 1-1z"></path>
+                </g>
+              </svg>
+            </ng-template>
+          </button>
+          <button
+            title="Copier dans le presse-papier le snippet html à copier dans un article/page ghost pour embarquer l'image en mode full"
+            class="button copy-button" *ngIf="isImage(item.url)" [ngClass]="{'copied': copiedIndex === i}"
+            (click)="copyGhostHTMLSnippet(item, 'kg-width-full', i)">
+            <span *ngIf="copiedIndex === i && copiedSize === 'kg-width-full'; else imageFull">Copié !</span>
+            <ng-template #imageFull>
+              <svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
+                <g fill="#242b3f" fill-rule="nonzero">
+                  <path d="M0 2a1 1 0 0 1 2 0v12a1 1 0 0 1-2 0V2zm14 0a1 1 0 0 1 2 0v12a1 1 0 0 1-2 0V2z" opacity=".6">
+                  </path>
+                  <path
+                    d="M10.626 7L9.312 5.691a1 1 0 1 1 1.411-1.417l3.029 3.017c.39.389.392 1.02.005 1.412l-3.029 3.059a1 1 0 0 1-1.421-1.407L10.648 9H5.415l1.342 1.355a1 1 0 0 1-1.422 1.407L2.307 8.703a1 1 0 0 1 .005-1.412L5.34 4.274a1 1 0 0 1 1.412 1.417L5.438 7h5.188z">
+                  </path>
+                </g>
+              </svg>
+            </ng-template>
+          </button>
+          <app-crud-buttons [id]="item._id" (delete)="displayDeletePopup($event)"></app-crud-buttons>
+        </div>
+      </div>
+    </div>
+    <div class="columns is-marginless paginator">
+      <div class="column">
+        <app-paginator *ngIf="paginator.length > 0" [length]="paginator.length" [pageSize]="paginator.limit"
+          [pageSizeOptions]="paginator.pageSizeOptions" [pageIndex]="paginator.pageIndex" [pagesToShow]="5"
+          [showFirstLastButtons]="true" (page)="changePagination($event)" (pageSizeChanged)="changePageSize($event)">
+        </app-paginator>
+      </div>
+    </div>
+  </div>
+</div>
+
+<app-confirmation-modal (cancel)="objectToBeDeletedId=null" (continue)="deleteMedia()" [texts]="deleteModalTexts"
+  [isOpened]="objectToBeDeletedId !== null">
+</app-confirmation-modal>
\ No newline at end of file
diff --git a/src/app/components/media/list/media.component.scss b/src/app/components/media/list/media.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..bab3d05913a7d0f533ad657e13fc02e135df5c46
--- /dev/null
+++ b/src/app/components/media/list/media.component.scss
@@ -0,0 +1,30 @@
+img {
+  max-width: 100px;
+}
+
+.data span {
+  word-break: break-all;
+}
+
+.copy-button {
+  background-color: transparent;
+  border: none;
+  padding-left: 0.25rem;
+  padding-right: 0.25rem;
+
+  svg {
+    height: 15px;
+  }
+
+  svg:hover {
+    g {
+      fill: #3273dc;
+    }
+  }
+}
+
+.actions {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+}
diff --git a/src/app/components/media/list/media.component.ts b/src/app/components/media/list/media.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..da2a33251212bb8b777985c465d1fb7eebf9b4ad
--- /dev/null
+++ b/src/app/components/media/list/media.component.ts
@@ -0,0 +1,173 @@
+import { Component, OnInit, OnDestroy } from '@angular/core';
+import { Subscription } from 'rxjs';
+import { PaginatorOptions } from 'src/app/models/paginator-options.model';
+import { IPageHeaderInfo } from '../../../models/page.model';
+import { NotificationService, MediaService } from '../../../services';
+import { Media, MediaRO } from '../../../models/media.model';
+import * as isImageUrl from 'is-image-url';
+
+@Component({
+  selector: 'app-media',
+  templateUrl: './media.component.html',
+  styleUrls: ['./media.component.scss'],
+})
+export class MediaComponent implements OnInit, OnDestroy {
+
+  pageHeaderInfo: IPageHeaderInfo = {
+    title: '',
+  };
+  objectToBeDeletedId = null;
+  deleteModalTexts = {
+    main: 'Si vous poursuivez, le média sera définitivement supprimé.',
+    cancel: 'Annuler',
+    continue: 'Supprimer',
+  };
+  copiedIndex = null;
+  copiedSize = null;
+  media: Media[] = [];
+  searchChangeSub: Subscription;
+
+  // Paginator options
+  paginator: PaginatorOptions;
+
+  sortValue: string;
+
+  totalElement: number;
+  pageSize = 10;
+  pageSizeOptions = [5, 10, 25, 100];
+  filters = {
+    name: '',
+  };
+  where = {};
+
+  constructor(
+    private _mediaService: MediaService,
+    private _notificationService: NotificationService,
+  ) {
+    this.paginator = {
+      pageIndex: this._mediaService.pageNumber,
+      length: 0,
+      limit: this._mediaService.limit,
+      pageSizeOptions: [5, 10, 20],
+    };
+  }
+
+  ngOnInit(): void {
+    this._mediaService.sortOptions = {
+      value: 'name',
+      order: 'asc',
+    };
+    this.search();
+
+    this.searchChangeSub = this._mediaService.searchChange$.subscribe(
+      () => {
+        this.search();
+      },
+    );
+  }
+
+  private search() {
+    this._mediaService.getMedia()
+      .subscribe(
+        (items: MediaRO) => {
+          this.media = items.media;
+          this.totalElement = items.mediaCount;
+
+          this.pageHeaderInfo.title = items.mediaCount ?
+            `${this.totalElement} médias trouvés` : `${this.totalElement} média trouvé`;
+
+          this.paginator.limit = this._mediaService.limit;
+          this.paginator.pageIndex = this._mediaService.pageNumber;
+          this.paginator.length = items.mediaCount;
+        },
+        () => {
+          this.pageHeaderInfo.title = '0 média trouvé';
+          this._notificationService.notify({
+            type: 'error',
+            message: 'Une erreur est survenue lors du chargement des médias.',
+          });
+        },
+      );
+  }
+
+  isImage(url) {
+    return isImageUrl(url);
+  }
+
+  copyGhostHTMLSnippet(media: Media, size: string, id) {
+    if (media.caption) {
+      navigator['clipboard'].writeText(`<figure class="kg-card kg-image-card kg-card-hascaption ${size}">
+  <img src="${media.url}" alt="${media.alt}" class="kg-image">
+  <figcaption>
+    ${media.caption}
+  </figcaption>
+</figure>`);
+    } else {
+      navigator['clipboard'].writeText(`<figure class="kg-card kg-image-card ${size}">
+  <img src="${media.url}" alt="${media.alt}" class="kg-image">
+</figure>`);
+    }
+    this.copiedIndex = id;
+    this.copiedSize = size;
+    setTimeout(
+      () => {
+        this.copiedIndex = null;
+        this.copiedSize = null;
+      },
+      3000,
+    );
+  }
+
+  // When pagination is changed by user, we update datasetList with new pagination options
+  changePagination(pageIndex) {
+    this._mediaService.paginationChanged(this.paginator.limit, pageIndex);
+  }
+
+  changePageSize(pageSize) {
+    this._mediaService.paginationChanged(pageSize, 1);
+  }
+
+  sortBy(key: string) {
+    if (this._mediaService.sortOptions.value === key) {
+      this._mediaService.reverseSortOrder();
+    } else {
+      this._mediaService.sortOptions.value = key;
+      this._mediaService.sortOptions.order = 'asc';
+    }
+    this.search();
+  }
+
+  get sortOptions() {
+    return this._mediaService.sortOptions;
+  }
+
+  displayDeletePopup(id) {
+    this.objectToBeDeletedId = id;
+  }
+
+  deleteMedia() {
+    this._mediaService.delete(this.objectToBeDeletedId).subscribe(
+      () => {
+        this._notificationService.notify({
+          type: 'success',
+          message: 'Le média a été supprimé avec succès.',
+        });
+        this._mediaService.pageNumber = 1;
+        this.search();
+      },
+      () => {
+        this._notificationService.notify({
+          type: 'error',
+          message: 'Une erreur est survenue lors de la suppression du média.',
+        });
+      },
+      () => {
+        this.objectToBeDeletedId = null;
+      },
+    );
+  }
+
+  ngOnDestroy() {
+    this.searchChangeSub.unsubscribe();
+  }
+}
diff --git a/src/app/components/menu/menu.component.html b/src/app/components/menu/menu.component.html
index f0b484894ad4d17f26478e1cf4342647ca1cfecd..eba774f49829c69f7b2e76702b927a899d8b2ba1 100644
--- a/src/app/components/menu/menu.component.html
+++ b/src/app/components/menu/menu.component.html
@@ -10,7 +10,21 @@
             </g>
           </svg>
         </span>
-        <span class="label-menu">Producteurs de données</span>
+        <span class="label-menu">Partenaires</span>
+      </a>
+    </li>
+    <li><a [routerLink]="['/', 'credits']" routerLinkActive="active-link">
+        <span class="icon">
+          <i class="fas fa-dollar-sign"></i>
+        </span>
+        <span class="label-menu">Crédits</span>
+      </a>
+    </li>
+    <li><a [routerLink]="['/', 'reutilisations']" routerLinkActive="active-link">
+        <span class="icon">
+          <i class="fas fa-recycle"></i>
+        </span>
+        <span class="label-menu">Réutilisations</span>
       </a>
     </li>
     <li><a [routerLink]="['/', 'resources']" routerLinkActive="active-link">
@@ -27,5 +41,33 @@
         <span class="label-menu">Formats</span>
       </a>
     </li>
+    <li><a [routerLink]="['/', 'projections']" routerLinkActive="active-link">
+        <span class="icon">
+          <i class="fas fa-map-marked-alt"></i>
+        </span>
+        <span class="label-menu">Projections</span>
+      </a>
+    </li>
+    <li><a [routerLink]="['/', 'changelog']" routerLinkActive="active-link">
+        <span class="icon">
+          <i class="fas fa-clipboard-list"></i>
+        </span>
+        <span class="label-menu">Changelog</span>
+      </a>
+    </li>
+    <li><a [routerLink]="['/', 'media']" routerLinkActive="active-link">
+        <span class="icon">
+          <i class="far fa-images"></i>
+        </span>
+        <span class="label-menu">Médias</span>
+      </a>
+    </li>
+    <li><a [routerLink]="['/', 'datalogs']" routerLinkActive="active-link">
+        <span class="icon">
+          <i class="far fa-chart-bar"></i>
+        </span>
+        <span class="label-menu">Logs d'indexation</span>
+      </a>
+    </li>
   </ul>
 </aside>
\ No newline at end of file
diff --git a/src/app/components/menu/menu.component.scss b/src/app/components/menu/menu.component.scss
index abe3f4fa0a50204dbd6df6b16495961d7f709354..5542d59ed389b9fbe7d17cd11a0351ab2ff729db 100644
--- a/src/app/components/menu/menu.component.scss
+++ b/src/app/components/menu/menu.component.scss
@@ -22,6 +22,7 @@
     width: 0;
     opacity: 0;
     overflow: hidden;
+    line-height: 1.3;
   }
 }
 
diff --git a/src/app/components/organizations/detail/organization-detail.component.html b/src/app/components/organizations/detail/organization-detail.component.html
index fcde85111da378bd3530e34756395e9fa74f2396..07d17543f8a43a148d7d126a17da7bc0368d0467 100644
--- a/src/app/components/organizations/detail/organization-detail.component.html
+++ b/src/app/components/organizations/detail/organization-detail.component.html
@@ -10,7 +10,7 @@
         </header>
         <div class="card-image">
           <figure class="image">
-            <img [src]="organization.logo" alt="Logo du producteur de données">
+            <img [src]="organization.logo" alt="Logo du partenaire">
           </figure>
         </div>
         <div class="card-content">
@@ -20,6 +20,11 @@
               <p>{{organization.description}}</p>
             </div>
             <br>
+            <div>
+              <p><span class="has-text-weight-bold">Statut:</span> {{ organization.published ? 'Publié' : 'Brouillon' }}
+              </p>
+            </div>
+            <br>
             <div>
               <p><span class="has-text-weight-bold">Id:</span> {{ organization.id}}</p>
             </div>
diff --git a/src/app/components/organizations/detail/organization-detail.component.scss b/src/app/components/organizations/detail/organization-detail.component.scss
index dff286c3a01ee11891ef45a0378e9fa7e35f4172..63a36307795f8cebd94c864bbbaa3deba1ae7da7 100644
--- a/src/app/components/organizations/detail/organization-detail.component.scss
+++ b/src/app/components/organizations/detail/organization-detail.component.scss
@@ -1,7 +1,8 @@
 figure {
   text-align: center;
 }
-figure img{
+
+figure img {
   max-width: 150px;
   display: inline-block;
   margin-top: 20px;
@@ -9,4 +10,4 @@ figure img{
 
 .card-header-title {
   justify-content: center;
-}
\ No newline at end of file
+}
diff --git a/src/app/components/organizations/edit/organization-form.component.html b/src/app/components/organizations/edit/organization-form.component.html
index dc8ea2d64fa1c4dad0e86a157b1f452201085088..840ba6a7347d4fb853b78c8bb110d2c6682fa340 100644
--- a/src/app/components/organizations/edit/organization-form.component.html
+++ b/src/app/components/organizations/edit/organization-form.component.html
@@ -1,18 +1,26 @@
 <section class="section page-container" *ngIf="organization">
-  <app-page-header [pageInfo]="{title: title}"></app-page-header>
-
-  <form [formGroup]="form" (ngSubmit)="onSubmit()" class="columns is-centered is-marginless">
+  <form [formGroup]="form" (ngSubmit)="onSubmit()" class="columns is-centered is-marginless is-multiline">
+    <div class="column is-12 header-with-publication-status">
+      <app-page-header [pageInfo]="{title: title}"></app-page-header>
+      <div class="field status-field">
+        <span class="fake-label" *ngIf="form.get('published').value === true">Publié</span>
+        <span class="fake-label" *ngIf="form.get('published').value === false">Brouillon</span>
+        <input id="published" type="checkbox" formControlName="published" class="switch is-rounded">
+        <label for="published"></label>
+      </div>
+    </div>
     <div class="column is-7">
-      <input type="hidden" formControlName="id" value="{{organization.id}}">
+      <input type="hidden" formControlName="id" [value]="organization.id">
+
 
       <div class="field">
         <label class="label required" for="name">Nom</label>
         <div class="control">
-          <input class="input" type="text" [value]="organization.name" formControlName="name" id="name" required>
+          <input class="input" type="text" formControlName="name" id="name" required>
         </div>
         <div *ngIf="name.invalid && (name.dirty || name.touched)" class="alert alert-danger">
           <p *ngIf="name.errors['required']" class="help is-danger">
-            Le nom du producteur de données est obligatoire.
+            Le nom du partenaire est obligatoire.
           </p>
         </div>
       </div>
@@ -30,16 +38,15 @@
         </div>
         <div *ngIf="description.invalid && (description.dirty || description.touched)" class="alert alert-danger">
           <p *ngIf="description.errors['required']" class="help is-danger">
-            La description du producteur de données est obligatoire.
+            La description du partenaire est obligatoire.
           </p>
         </div>
       </div>
 
       <div class="field">
-        <label class="label" for="elasticSearchName">Nom ElasticSearch du producteur de données</label>
+        <label class="label" for="elasticSearchName">Nom ElasticSearch du partenaire</label>
         <div class="control">
-          <input class="input" type="text" [value]="organization.elasticSearchName" formControlName="elasticSearchName"
-            id="elasticSearchName">
+          <input class="input" type="text" formControlName="elasticSearchName" id="elasticSearchName">
         </div>
       </div>
 
diff --git a/src/app/components/organizations/edit/organization-form.component.scss b/src/app/components/organizations/edit/organization-form.component.scss
index 5bea26fe55b4ab7bb07da39d6140df0c988864d4..0762aa33dcef6109e5521239a03190ee9c3670a1 100644
--- a/src/app/components/organizations/edit/organization-form.component.scss
+++ b/src/app/components/organizations/edit/organization-form.component.scss
@@ -2,18 +2,24 @@
   width: 100%;
 }
 
+.page-container {
+  position: relative;
+}
+
 h1 {
   text-align: center
 }
 
 .icon {
   cursor: pointer;
+
   &:hover {
     .fa-plus {
       color: lightblue;
     }
+
     .fa-trash {
       color: #d5232a;
     }
   }
-}
\ No newline at end of file
+}
diff --git a/src/app/components/organizations/edit/organization-form.component.ts b/src/app/components/organizations/edit/organization-form.component.ts
index b8a22a5f7fee592f7a91fbfb7863edc7f4d49832..27945895ec6262a9ef666b20209a435ce79f3747 100644
--- a/src/app/components/organizations/edit/organization-form.component.ts
+++ b/src/app/components/organizations/edit/organization-form.component.ts
@@ -3,9 +3,11 @@ import { ActivatedRoute, ParamMap, Router } from '@angular/router';
 import { Organization } from 'src/app/models/organization.model';
 import { OrganizationService } from 'src/app/services/organization.service';
 import { FormBuilder, FormGroup, Validators, FormArray } from '@angular/forms';
-import { filter, switchMap, mergeMap } from 'rxjs/operators';
+import { filter, switchMap, mergeMap, catchError } from 'rxjs/operators';
 import { IImageUploadFieldParams } from 'src/app/models/image-upload.model';
-import { NotificationService } from 'src/app/services';
+import { NotificationService, MediaService } from 'src/app/services';
+import { throwError } from 'rxjs';
+import { Media } from '../../../models/media.model';
 
 @Component({
   selector: 'app-organization-form',
@@ -23,11 +25,11 @@ export class OrganizationFormComponent implements OnInit {
     existingImageUrl: null,
     isRequired: true,
   };
-  logo: File;
   title: string;
 
   constructor(
     private _organizationService: OrganizationService,
+    private _mediaService: MediaService,
     private _notificationService: NotificationService,
     private _route: ActivatedRoute,
     private _router: Router,
@@ -64,6 +66,7 @@ export class OrganizationFormComponent implements OnInit {
             elasticSearchName: [organization.elasticSearchName],
             logo: [organization.logo],
             links: arr,
+            published: [this.organization.published],
           });
       });
 
@@ -81,6 +84,7 @@ export class OrganizationFormComponent implements OnInit {
         elasticSearchName: [this.organization.elasticSearchName],
         logo: [this.organization.logo],
         links: arr,
+        published: [this.organization.published],
       });
   }
 
@@ -115,29 +119,100 @@ export class OrganizationFormComponent implements OnInit {
   onSubmit() {
     if (!this.formInvalid) {
       this.organization = new Organization(this.form.value);
-      let action;
 
-      if (this.logoFile) {
-        action = this._organizationService.uploadLogoAndSaveOrganization(this.logoFile, this.organization);
+      if (this.organization.id) {
+        if (this.logoFile) {
+          this._mediaService.uploadFile(this.logoFile).pipe(
+            catchError(() => {
+              return throwError('Une erreur est survenue lors de l\'upload du logo du partenaire.');
+            }),
+            mergeMap((response: Media) => {
+              this.organization.logo = response.url;
+              return this._organizationService.update(this.organization).pipe(
+                catchError(() => {
+                  return throwError('Une erreur est survenue lors de la mise à jour du partenaire.');
+                }),
+              );
+            }),
+          ).subscribe(
+            (organizationUpdated) => {
+              this._notificationService.notify({
+                message: 'Le partenaire a été mis à jour avec succès.',
+                type: 'success',
+              });
+              this._router.navigate(['/producteur-de-donnees', organizationUpdated.id]);
+            },
+            (err) => {
+              this._notificationService.notify({
+                message: err,
+                type: 'error',
+              });
+            },
+          );
+        } else {
+          return this._organizationService.update(this.organization).subscribe(
+            (organizationUpdated) => {
+              this._notificationService.notify({
+                message: 'Le partenaire a été mis à jour avec succès.',
+                type: 'success',
+              });
+              this._router.navigate(['/producteur-de-donnees', organizationUpdated.id]);
+            },
+            () => {
+              this._notificationService.notify({
+                message: 'Une erreur est survenue lors de la mise à jour du partenaire.',
+                type: 'error',
+              });
+            },
+          );
+        }
       } else {
-        action = this._organizationService.replaceOrCreate(this.organization);
+        if (this.logoFile) {
+          this._mediaService.uploadFile(this.logoFile).pipe(
+            catchError(() => {
+              return throwError('Une erreur est survenue lors de l\'upload du logo du partenaire.');
+            }),
+            mergeMap((response: Media) => {
+              this.organization.logo = response.url;
+              return this._organizationService.create(this.organization).pipe(
+                catchError(() => {
+                  return throwError('Une erreur est survenue lors de la création du partenaire.');
+                }),
+              );
+            }),
+          ).subscribe(
+            (organizationCreated) => {
+              this._notificationService.notify({
+                message: 'Le partenaire a été créé avec succès.',
+                type: 'success',
+              });
+              this._router.navigate(['/producteur-de-donnees', organizationCreated.id]);
+            },
+            (err) => {
+              this._notificationService.notify({
+                message: err,
+                type: 'error',
+              });
+            },
+          );
+        } else {
+          return this._organizationService.create(this.organization).subscribe(
+            (organizationUpdated) => {
+              this._notificationService.notify({
+                message: 'Le partenaire a été créé avec succès.',
+                type: 'success',
+              });
+              this._router.navigate(['/producteur-de-donnees', organizationUpdated.id]);
+            },
+            () => {
+              this._notificationService.notify({
+                message: 'Une erreur est survenue lors de la création du partenaire.',
+                type: 'error',
+              });
+            },
+          );
+        }
       }
-
-      action.subscribe(
-        (organizationCreated) => {
-          this._notificationService.notify({
-            message: 'Le producteur de données a été créée avec succès.',
-            type: 'success',
-          });
-          this._router.navigate(['/producteur-de-donnees', organizationCreated.id]);
-        },
-        () => {
-          this._notificationService.notify({
-            message: 'Une erreur est survenue lors de la création du producteur de données.',
-            type: 'error',
-          });
-        },
-      );
     }
   }
 
diff --git a/src/app/components/organizations/list/organizations.component.html b/src/app/components/organizations/list/organizations.component.html
index f30fe6a126772c926a0eefc3db29202345127685..93d566e69541748f83c81a16e33974593e4f976f 100644
--- a/src/app/components/organizations/list/organizations.component.html
+++ b/src/app/components/organizations/list/organizations.component.html
@@ -1,11 +1,11 @@
-<div class="section page-container" *ngIf="organizations">
+<div class="section page-container">
   <app-page-header [pageInfo]="pageHeaderInfo" [hideBackButton]="true"></app-page-header>
   <div class="add-item-link has-text-right">
     <a class="button button-gl" [routerLink]="['new']">
       Ajouter
     </a>
   </div>
-  <div class="table entity-list-table">
+  <div class="table entity-list-table" *ngIf="organizations">
     <div class="header columns is-marginless">
       <div class="column is-2">
         <span (click)="sortBy('name')" class="is-sortable">
@@ -22,7 +22,10 @@
           <span class="column-title" [ngClass]="{'active': sortOptions.value === name}">Nom</span>
         </span>
       </div>
-      <div class="column is-2 has-text-centered">
+      <div class="column is-1 has-text-centered">
+        <span class="column-title">Publié</span>
+      </div>
+      <div class="column is-1 has-text-centered">
         <span class="column-title">Logo</span>
       </div>
       <div class="column is-4">
@@ -54,8 +57,16 @@
         <div class="column is-2">
           <span>{{ organization.name}}</span>
         </div>
-        <div class="column is-2 has-text-centered">
-          <img src="{{organization.logo}}" alt="">
+        <div class="column is-1 has-text-centered">
+          <span class="icon has-text-success" *ngIf="organization.published">
+            <i class="far fa-check-circle"></i>
+          </span>
+          <span class="icon has-text-danger" *ngIf="!organization.published">
+            <i class="far fa-times-circle"></i>
+          </span>
+        </div>
+        <div class="column is-1 has-text-centered">
+          <img class="entity-logo-in-list" [src]="organization.logo" alt="">
         </div>
         <div class="column is-4">
           <span>{{ organization.description | slice:0:200}}...</span>
@@ -80,4 +91,8 @@
       </div>
     </div>
   </div>
-</div>
\ No newline at end of file
+</div>
+
+<app-confirmation-modal (cancel)="objectToBeDeletedId=null" (continue)="deleteOrganization()" [texts]="deleteModalTexts"
+  [isOpened]="objectToBeDeletedId !== null">
+</app-confirmation-modal>
\ No newline at end of file
diff --git a/src/app/components/organizations/list/organizations.component.ts b/src/app/components/organizations/list/organizations.component.ts
index c2c4551db3b71a1ab7a5903a0b57b62760762ad7..e3ce2ea799abad24b4c85f8d791154681da7d553 100644
--- a/src/app/components/organizations/list/organizations.component.ts
+++ b/src/app/components/organizations/list/organizations.component.ts
@@ -1,21 +1,28 @@
-import { Component, OnInit } from '@angular/core';
+import { Component, OnInit, OnDestroy } from '@angular/core';
 import { Organization, OrganizationRO } from 'src/app/models/organization.model';
 import { OrganizationService } from 'src/app/services/organization.service';
 import { Subscription } from 'rxjs';
 import { PaginatorOptions } from 'src/app/models/paginator-options.model';
 import { IPageHeaderInfo } from '../../../models/page.model';
+import { NotificationService } from '../../../services';
 
 @Component({
   selector: 'app-organizations',
   templateUrl: './organizations.component.html',
   styleUrls: ['./organizations.component.scss'],
 })
-export class OrganizationsComponent implements OnInit {
+export class OrganizationsComponent implements OnInit, OnDestroy {
 
   pageHeaderInfo: IPageHeaderInfo = {
     title: '',
   };
-  organizations: Organization[];
+  objectToBeDeletedId = null;
+  deleteModalTexts = {
+    main: 'Si vous poursuivez, le partenaire sera définitivement supprimé.',
+    cancel: 'Annuler',
+    continue: 'Supprimer',
+  };
+  organizations: Organization[] = [];
   searchChangeSub: Subscription;
 
   // Paginator options
@@ -32,24 +39,25 @@ export class OrganizationsComponent implements OnInit {
   where = {};
 
   constructor(
-    private organizationsService: OrganizationService,
+    private _organizationService: OrganizationService,
+    private _notificationService: NotificationService,
   ) {
     this.paginator = {
-      pageIndex: this.organizationsService.pageNumber,
+      pageIndex: this._organizationService.pageNumber,
       length: 0,
-      limit: this.organizationsService.limit,
+      limit: this._organizationService.limit,
       pageSizeOptions: [5, 10, 20],
     };
   }
 
   ngOnInit(): void {
-    this.organizationsService.sortOptions = {
+    this._organizationService.sortOptions = {
       value: 'name',
       order: 'asc',
     };
     this.search();
 
-    this.searchChangeSub = this.organizationsService.searchChange$.subscribe(
+    this.searchChangeSub = this._organizationService.searchChange$.subscribe(
       () => {
         this.search();
       },
@@ -57,49 +65,79 @@ export class OrganizationsComponent implements OnInit {
   }
 
   private search() {
-    this.organizationsService.getOrganizations()
-      .subscribe((items: OrganizationRO) => {
-        this.organizations = items.organizations;
-        this.totalElement = items.totalCount;
-
-        this.pageHeaderInfo.title = `${this.totalElement} producteurs de données trouvés`;
-
-        this.paginator.limit = this.organizationsService.limit;
-        this.paginator.pageIndex = this.organizationsService.pageNumber;
-        this.paginator.length = items.totalCount;
-      });
+    this._organizationService.getOrganizations()
+      .subscribe(
+        (items: OrganizationRO) => {
+          this.organizations = items.organizations;
+          this.totalElement = items.totalCount;
+
+          this.pageHeaderInfo.title = items.totalCount ?
+            `${this.totalElement} partenaires trouvés` : `${this.totalElement} partenaire trouvé`;
+
+          this.paginator.limit = this._organizationService.limit;
+          this.paginator.pageIndex = this._organizationService.pageNumber;
+          this.paginator.length = items.totalCount;
+        },
+        () => {
+          this.pageHeaderInfo.title = '0 partenaire trouvé';
+          this._notificationService.notify({
+            type: 'error',
+            message: 'Une erreur est survenue lors du chargement des partenaires.',
+          });
+        },
+      );
   }
 
   // When pagination is changed by user, we update datasetList with new pagination options
   changePagination(pageIndex) {
-    this.organizationsService.paginationChanged(this.paginator.limit, pageIndex);
+    this._organizationService.paginationChanged(this.paginator.limit, pageIndex);
   }
 
   changePageSize(pageSize) {
-    this.organizationsService.paginationChanged(pageSize, 1);
+    this._organizationService.paginationChanged(pageSize, 1);
   }
 
   sortBy(key: string) {
-    if (this.organizationsService.sortOptions.value === key) {
-      this.organizationsService.reverseSortOrder();
+    if (this._organizationService.sortOptions.value === key) {
+      this._organizationService.reverseSortOrder();
     } else {
-      this.organizationsService.sortOptions.value = key;
-      this.organizationsService.sortOptions.order = 'asc';
+      this._organizationService.sortOptions.value = key;
+      this._organizationService.sortOptions.order = 'asc';
     }
     this.search();
   }
 
   get sortOptions() {
-    return this.organizationsService.sortOptions;
+    return this._organizationService.sortOptions;
+  }
+
+  displayDeletePopup(id) {
+    this.objectToBeDeletedId = id;
   }
 
-  displayDeletePopup(organizationId) {
-    const pop = confirm('Etes vous sûr de vouloir supprimer ce producteur de données ?');
-    if (pop) {
-      this.organizationsService.delete(organizationId).subscribe(() => {
-        this.organizationsService.pageNumber = 1;
+  deleteOrganization() {
+    this._organizationService.delete(this.objectToBeDeletedId).subscribe(
+      () => {
+        this._notificationService.notify({
+          type: 'success',
+          message: 'Le partenaire a été supprimé avec succès.',
+        });
+        this._organizationService.pageNumber = 1;
         this.search();
-      });
-    }
+      },
+      () => {
+        this._notificationService.notify({
+          type: 'error',
+          message: 'Une erreur est survenue lors de la suppression du partenaire.',
+        });
+      },
+      () => {
+        this.objectToBeDeletedId = null;
+      },
+    );
+  }
+
+  ngOnDestroy() {
+    this.searchChangeSub.unsubscribe();
   }
 }
diff --git a/src/app/components/page-header/page-header.component.ts b/src/app/components/page-header/page-header.component.ts
index 0d824ad8570f807af5ec8d8e5de919426a609ce4..e4fdcc75c432db20f53eb3a9086828601ca02ec9 100644
--- a/src/app/components/page-header/page-header.component.ts
+++ b/src/app/components/page-header/page-header.component.ts
@@ -16,15 +16,15 @@ export class PageHeaderComponent implements OnInit {
   ) { }
 
   @Input() pageInfo: IPageHeaderInfo;
-  @Input() customGoToPreviousPage: any;
+  @Input() goToThisUrl: any;
   @Input() hideBackButton: boolean;
 
   ngOnInit() {
   }
 
   goToPreviousPage() {
-    if (this.customGoToPreviousPage) {
-      this.customGoToPreviousPage();
+    if (this.goToThisUrl) {
+      this._router.navigate([this.goToThisUrl]);
     } else {
       const index = 1; // Start to retrieve the previous element
       let url = this._navigationHistoryService.getFromLast(index);
diff --git a/src/app/components/paginator/paginator.component.ts b/src/app/components/paginator/paginator.component.ts
index 0debb3cfb0985ef9dcbd7c8d8542880645b099d7..f43573de6181223f3110e71912893664025556c3 100644
--- a/src/app/components/paginator/paginator.component.ts
+++ b/src/app/components/paginator/paginator.component.ts
@@ -58,6 +58,7 @@ export class PaginatorComponent implements OnInit {
   changePageSize(size: number) {
     this.pageSize = size;
     this.pageSizeChanged.emit(this.pageSize);
+
   }
 
   getPages(): number[] {
diff --git a/src/app/components/projections/detail/projection-detail.component.html b/src/app/components/projections/detail/projection-detail.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..490ef8dafc6dfee04ba08b542b0e4fdaae77fe21
--- /dev/null
+++ b/src/app/components/projections/detail/projection-detail.component.html
@@ -0,0 +1,34 @@
+<section class="section page-container" *ngIf="projection">
+  <app-page-header [pageInfo]="{title: title}"></app-page-header>
+
+  <div class="columns is-centered">
+    <div class="column is-8">
+      <div class="card">
+        <header class="card-header">
+          <p class="card-header-title has-text-centered">
+            {{projection.name}}
+          </p>
+        </header>
+        <div class="card-content">
+          <div class="content">
+            <p>
+              <span class="has-text-weight-bold">Id: </span>
+              <span>{{projection.id}}</span>
+            </p>
+            <p>
+              <span class="has-text-weight-bold">Nom commun: </span>
+              <span>{{projection.commonName}}</span>
+            </p>
+            <p>
+              <span class="has-text-weight-bold">Description: </span>
+              <span *ngIf="projection.description; else emptyDescription">{{projection.description}}</span>
+              <ng-template #emptyDescription>
+                <span class="empty-property">Non renseigné</span>
+              </ng-template>
+            </p>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</section>
\ No newline at end of file
diff --git a/src/app/components/projections/detail/projection-detail.component.scss b/src/app/components/projections/detail/projection-detail.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..163f99f474dc5696a1670be5273eef97a2efbd39
--- /dev/null
+++ b/src/app/components/projections/detail/projection-detail.component.scss
@@ -0,0 +1,3 @@
+.card-header-title {
+  justify-content: center;
+}
\ No newline at end of file
diff --git a/src/app/components/projections/detail/projection-detail.component.ts b/src/app/components/projections/detail/projection-detail.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..fc44993764c353fbcf3fd9d2c468198623724417
--- /dev/null
+++ b/src/app/components/projections/detail/projection-detail.component.ts
@@ -0,0 +1,30 @@
+
+import { switchMap } from 'rxjs/operators';
+import { Component, OnInit } from '@angular/core';
+import { ActivatedRoute, ParamMap } from '@angular/router';
+import { Projection } from '../../../models/projection.model';
+import { ProjectionService } from '../../../services';
+
+@Component({
+  selector: 'app-projection-detail',
+  templateUrl: './projection-detail.component.html',
+  styleUrls: ['./projection-detail.component.scss'],
+})
+export class ProjectionDetailComponent implements OnInit {
+
+  projection: Projection;
+  title: string;
+
+  constructor(
+    private _route: ActivatedRoute,
+    private _projectionService: ProjectionService,
+  ) {
+  }
+
+  ngOnInit(): void {
+    this.title = this._route.snapshot.data.title;
+    this._route.paramMap.pipe(
+      switchMap((params: ParamMap) => this._projectionService.findById(params.get('id'))))
+      .subscribe((projection: Projection) => this.projection = projection);
+  }
+}
diff --git a/src/app/components/projections/edit/projection-form.component.html b/src/app/components/projections/edit/projection-form.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..fb2960a28d12d69bb4b7357a54e8ba618ab4dcea
--- /dev/null
+++ b/src/app/components/projections/edit/projection-form.component.html
@@ -0,0 +1,45 @@
+<section class="section page-container" *ngIf="projection">
+  <app-page-header [pageInfo]="{title: title}"></app-page-header>
+
+  <form [formGroup]="form" (ngSubmit)="onSubmit()" class="columns is-centered is-marginless">
+    <div class="column is-7">
+      <input type="hidden" formControlName="id" value="{{projection.id}}">
+
+      <div class="field">
+        <label class="label required" for="name">Nom</label>
+        <div class="control">
+          <input class="input" type="text" [value]="projection.name" formControlName="name" id="name" required>
+        </div>
+        <div *ngIf="name.invalid && (name.dirty || name.touched)" class="alert alert-danger">
+          <p *ngIf="name.errors['required']" class="help is-danger">
+            Le nom de la projection est obligatoire.
+          </p>
+        </div>
+      </div>
+
+      <div class="field">
+        <label class="label required" for="mapServerType">Nom commun</label>
+        <div class="control">
+          <input class="input" type="text" [value]="projection.commonName" formControlName="commonName" id="commonName">
+        </div>
+        <div *ngIf="commonName.invalid && (commonName.dirty || commonName.touched)" class="alert alert-danger">
+          <p *ngIf="commonName.errors['required']" class="help is-danger">
+            Le nom commun est obligatoire.
+          </p>
+        </div>
+      </div>
+
+      <div class="field">
+        <label class="label" for="fileExtension">Description</label>
+        <div class="control">
+          <input class="input" type="text" [value]="projection.description" formControlName="description"
+            id="description">
+        </div>
+      </div>
+
+      <div class="has-text-right">
+        <button class="button button-gl" type="submit" [disabled]="formInvalid == true">Valider</button>
+      </div>
+    </div>
+  </form>
+</section>
\ No newline at end of file
diff --git a/src/app/components/projections/edit/projection-form.component.scss b/src/app/components/projections/edit/projection-form.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..5bea26fe55b4ab7bb07da39d6140df0c988864d4
--- /dev/null
+++ b/src/app/components/projections/edit/projection-form.component.scss
@@ -0,0 +1,19 @@
+.full-width {
+  width: 100%;
+}
+
+h1 {
+  text-align: center
+}
+
+.icon {
+  cursor: pointer;
+  &:hover {
+    .fa-plus {
+      color: lightblue;
+    }
+    .fa-trash {
+      color: #d5232a;
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/app/components/projections/edit/projection-form.component.ts b/src/app/components/projections/edit/projection-form.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..729c68d11f4174564eaf880560e248978957c2f3
--- /dev/null
+++ b/src/app/components/projections/edit/projection-form.component.ts
@@ -0,0 +1,108 @@
+import { Component, OnInit } from '@angular/core';
+import { ActivatedRoute, ParamMap, Router } from '@angular/router';
+import { FormBuilder, FormGroup, Validators } from '@angular/forms';
+import { filter, switchMap } from 'rxjs/operators';
+import { NotificationService, ProjectionService } from 'src/app/services';
+import { Projection } from '../../../models/projection.model';
+
+@Component({
+  selector: 'app-projection-form',
+  templateUrl: './projection-form.component.html',
+  styleUrls: ['./projection-form.component.scss'],
+})
+export class ProjectionFormComponent implements OnInit {
+
+  projection: Projection = new Projection();
+  form: FormGroup;
+  title: string;
+
+  constructor(
+    private _projectionService: ProjectionService,
+    private _route: ActivatedRoute,
+    private _router: Router,
+    private _fb: FormBuilder,
+    private _notificationService: NotificationService,
+  ) {
+  }
+
+  ngOnInit() {
+    this.title = this._route.snapshot.data.title;
+    this.initForm();
+
+    this._route.paramMap.pipe(
+      filter((paramMap: ParamMap) => (paramMap.get('id') !== null)),
+      switchMap((paramMap: ParamMap) => this._projectionService.findById(paramMap.get('id'))))
+      .subscribe((projection: Projection) => {
+
+        this.projection = projection;
+
+        this.initForm();
+
+      });
+  }
+
+  initForm() {
+    this.form = this._fb.group({
+      id: [this.projection.id],
+      name: [this.projection.name, Validators.required],
+      commonName: [this.projection.commonName, Validators.required],
+      description: [this.projection.description],
+    });
+  }
+
+  onSubmit() {
+    if (!this.formInvalid) {
+      this.projection = new Projection(this.form.value);
+      if (this.projection.id) {
+        this._projectionService.update(this.projection).subscribe(
+          (projectionUpdate) => {
+            this._notificationService.notify({
+              type: 'success',
+              message: 'Le projection a bien été mise à jour.',
+            });
+            this._router.navigate(['/projections', projectionUpdate.id]);
+          },
+          () => {
+            this._notificationService.notify({
+              type: 'error',
+              message: 'Une erreur est survenue lors de la mise à jour de la projection.',
+            });
+          },
+        );
+      } else {
+        this._projectionService.create(this.projection).subscribe(
+          (projectionCreated) => {
+            this._notificationService.notify({
+              type: 'success',
+              message: 'Le projection a bien été créé.',
+            });
+            this._router.navigate(['/projections', projectionCreated.id]);
+          },
+          () => {
+            this._notificationService.notify({
+              type: 'error',
+              message: 'Une erreur est survenue lors de la création de la projection.',
+            });
+          },
+        );
+      }
+    }
+  }
+
+  // Getters for each property
+  get name() {
+    return this.form.controls['name'];
+  }
+
+  get commonName() {
+    return this.form.controls['commonName'];
+  }
+
+  get description() {
+    return this.form.controls['description'];
+  }
+
+  get formInvalid() {
+    return this.form.invalid;
+  }
+}
diff --git a/src/app/components/projections/list/projections.component.html b/src/app/components/projections/list/projections.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..55daeba83054bb2aaee9752516638c1125d88161
--- /dev/null
+++ b/src/app/components/projections/list/projections.component.html
@@ -0,0 +1,90 @@
+<section class="section page-container">
+  <app-page-header [pageInfo]="pageHeaderInfo" [hideBackButton]="true"></app-page-header>
+  <div class="add-item-link has-text-right">
+    <a class="button button-gl" [routerLink]="['new']">
+      Ajouter
+    </a>
+  </div>
+  <div class="table entity-list-table" *ngIf="projections">
+    <div class="header columns is-marginless">
+      <div class="column is-2">
+        <span (click)="sortBy('name')" class="is-sortable">
+          <span class="sort-icons">
+            <span class="icon">
+              <i class="fas fa-sort-up"
+                [ngClass]="{'icon-red': sortOptions.value === 'name' && sortOptions.order === 'desc'}"></i>
+            </span>
+            <span class="icon">
+              <i class="fas fa-sort-down"
+                [ngClass]="{'icon-red': sortOptions.value === 'name' && sortOptions.order === 'asc'}"></i>
+            </span>
+          </span>
+          <span class="column-title" [ngClass]="{'active': sortOptions.value === name}">Nom</span>
+        </span>
+      </div>
+      <div class="column is-2">
+        <span (click)="sortBy('commonName')" class="is-sortable">
+          <span class="sort-icons">
+            <span class="icon">
+              <i class="fas fa-sort-up"
+                [ngClass]="{'icon-red': sortOptions.value === 'commonName' && sortOptions.order === 'desc'}"></i>
+            </span>
+            <span class="icon">
+              <i class="fas fa-sort-down"
+                [ngClass]="{'icon-red': sortOptions.value === 'commonName' && sortOptions.order === 'asc'}"></i>
+            </span>
+          </span>
+          <span class="column-title" [ngClass]="{'active': sortOptions.value === commonName}">Nom commun</span>
+        </span>
+      </div>
+      <div class="column is-2">
+        <span (click)="sortBy('description')" class="is-sortable">
+          <span class="sort-icons">
+            <span class="icon">
+              <i class="fas fa-sort-up"
+                [ngClass]="{'icon-red': sortOptions.value === 'description' && sortOptions.order === 'desc'}"></i>
+            </span>
+            <span class="icon">
+              <i class="fas fa-sort-down"
+                [ngClass]="{'icon-red': sortOptions.value === 'description' && sortOptions.order === 'asc'}"></i>
+            </span>
+          </span>
+          <span class="column-title" [ngClass]="{'active': sortOptions.value === description}">Description</span>
+        </span>
+      </div>
+      <div class="column is-offset-5 is-1 has-text-centered">
+        <span class="column-title">Actions</span>
+      </div>
+    </div>
+    <div class="data-list">
+      <div class="data columns is-multiline is-vcentered is-marginless"
+        *ngFor="let projection of projections; let i=index; let odd=odd; let even=even;"
+        [ngClass]="{ odd: odd, even: even }">
+        <div class="column is-2">
+          <span>{{ projection.name}}</span>
+        </div>
+        <div class="column is-2">
+          <span>{{ projection.commonName}}</span>
+        </div>
+        <div class="column is-2">
+          <span>{{ projection.description}}</span>
+        </div>
+        <div class="column is-offset-5 is-1 has-text-centered actions">
+          <app-crud-buttons [id]="projection.id" (delete)="displayDeletePopup($event)"></app-crud-buttons>
+        </div>
+      </div>
+    </div>
+    <div class="columns is-marginless paginator">
+      <div class="column">
+        <app-paginator *ngIf="paginator.length > 0" [length]="paginator.length" [pageSize]="paginator.limit"
+          [pageSizeOptions]="paginator.pageSizeOptions" [pageIndex]="paginator.pageIndex" [pagesToShow]="5"
+          [showFirstLastButtons]="true" (page)="changePagination($event)" (pageSizeChanged)="changePageSize($event)">
+        </app-paginator>
+      </div>
+    </div>
+  </div>
+</section>
+
+<app-confirmation-modal (cancel)="objectToBeDeletedId=null" (continue)="deleteProjection()" [texts]="deleteModalTexts"
+  [isOpened]="objectToBeDeletedId !== null">
+</app-confirmation-modal>
\ No newline at end of file
diff --git a/src/app/components/projections/list/projections.component.scss b/src/app/components/projections/list/projections.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc
--- /dev/null
+++ b/src/app/components/projections/list/projections.component.scss
@@ -0,0 +1 @@
+
diff --git a/src/app/components/projections/list/projections.component.ts b/src/app/components/projections/list/projections.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3d132afc284fade3a7113d06ad28847e5728eca4
--- /dev/null
+++ b/src/app/components/projections/list/projections.component.ts
@@ -0,0 +1,145 @@
+import { Component, OnInit, OnDestroy } from '@angular/core';
+import { Subscription } from 'rxjs';
+import { PaginatorOptions } from 'src/app/models/paginator-options.model';
+import { IPageHeaderInfo } from '../../../models/page.model';
+import { NotificationService } from '../../../services';
+import { Projection, ProjectionRO } from '../../../models/projection.model';
+import { ProjectionService } from '../../../services/projection.service';
+
+@Component({
+  selector: 'app-projections',
+  templateUrl: './projections.component.html',
+  styleUrls: ['./projections.component.scss'],
+})
+export class ProjectionsComponent implements OnInit, OnDestroy {
+
+  pageHeaderInfo: IPageHeaderInfo = {
+    title: '',
+  };
+  objectToBeDeletedId = null;
+  deleteModalTexts = {
+    main: 'Si vous poursuivez, la projection sera définitivement supprimée.',
+    cancel: 'Annuler',
+    continue: 'Supprimer',
+  };
+  projections: Projection[] = [];
+  searchChangeSub: Subscription;
+
+  // Paginator options
+  paginator: PaginatorOptions;
+
+  sortValue: string;
+
+  totalElement: number;
+  pageSize = 10;
+  pageSizeOptions = [5, 10, 25, 100];
+  filters = {
+    name: '',
+  };
+  where = {};
+
+  constructor(
+    private _projectionService: ProjectionService,
+    private _notificationService: NotificationService,
+  ) {
+    this.paginator = {
+      pageIndex: this._projectionService.pageNumber,
+      length: 0,
+      limit: this._projectionService.limit,
+      pageSizeOptions: [5, 10, 20],
+    };
+  }
+
+  ngOnInit(): void {
+    this._projectionService.sortOptions = {
+      value: 'name',
+      order: 'asc',
+    };
+    this.search();
+
+    this.searchChangeSub = this._projectionService.searchChange$.subscribe(
+      () => {
+        this.search();
+      },
+    );
+  }
+
+  private search() {
+    this._projectionService.getProjections()
+      .subscribe(
+        (items: ProjectionRO) => {
+          this.projections = items.projections;
+          this.totalElement = items.totalCount;
+
+          this.pageHeaderInfo.title = `${this.totalElement} projections trouvées`;
+          this.pageHeaderInfo.title = this.totalElement > 1 ?
+            `${this.totalElement} projections trouvées` :
+            `${this.totalElement} projection trouvée`;
+
+          this.paginator.limit = this._projectionService.limit;
+          this.paginator.pageIndex = this._projectionService.pageNumber;
+          this.paginator.length = items.totalCount;
+        },
+        () => {
+          this.pageHeaderInfo.title = '0 projection trouvée';
+          this._notificationService.notify({
+            type: 'error',
+            message: 'Une erreur est survenue lors du chargement des projections.',
+          });
+        },
+      );
+  }
+
+  // When pagination is changed by user, we update projections list with new pagination options
+  changePagination(pageIndex) {
+    this._projectionService.paginationChanged(this.paginator.limit, pageIndex);
+  }
+
+  changePageSize(pageSize) {
+    this._projectionService.paginationChanged(pageSize, 1);
+  }
+
+  sortBy(key: string) {
+    if (this._projectionService.sortOptions.value === key) {
+      this._projectionService.reverseSortOrder();
+    } else {
+      this._projectionService.sortOptions.value = key;
+      this._projectionService.sortOptions.order = 'asc';
+    }
+    this.search();
+  }
+
+  get sortOptions() {
+    return this._projectionService.sortOptions;
+  }
+
+  displayDeletePopup(id) {
+    this.objectToBeDeletedId = id;
+  }
+
+  deleteProjection() {
+    this._projectionService.delete(this.objectToBeDeletedId).subscribe(
+      () => {
+        this._notificationService.notify({
+          type: 'success',
+          message: 'La projection a été supprimée avec succès.',
+        });
+        this._projectionService.pageNumber = 1;
+        this.search();
+      },
+      () => {
+        this._notificationService.notify({
+          type: 'error',
+          message: 'Une erreur est survenue lors de la suppression de la projection.',
+        });
+      },
+      () => {
+        this.objectToBeDeletedId = null;
+      },
+    );
+  }
+
+  ngOnDestroy() {
+    this.searchChangeSub.unsubscribe();
+  }
+}
diff --git a/src/app/components/resources/detail/resource-detail.component.html b/src/app/components/resources/detail/resource-detail.component.html
index 142398b016a9d9ce69cb516509d3ad70298f52df..b3316ef37f69bbb1b54ceb2db6a829238b177c5f 100644
--- a/src/app/components/resources/detail/resource-detail.component.html
+++ b/src/app/components/resources/detail/resource-detail.component.html
@@ -73,10 +73,20 @@
                 <span class="empty-property">Non renseigné</span>
               </ng-template>
             </div>
+            <br>
 
-            <div *ngIf="resource.messageWarning">
-              <span class="has-text-weight-bold">Message d'alerte: </span>
-              <span *ngIf="resource.messageWarning; else emptyMessageWarning">{{resource.messageWarning}}</span>
+            <div>
+              <span class="has-text-weight-bold">Message d'alerte (Français): </span>
+              <span *ngIf="resource.messageWarningFr; else emptyMessageWarning">{{resource.messageWarningFr}}</span>
+              <ng-template #emptyMessageWarning>
+                <span class="empty-property">Non renseigné</span>
+              </ng-template>
+            </div>
+            <br>
+
+            <div>
+              <span class="has-text-weight-bold">Message d'alerte (Anglais): </span>
+              <span *ngIf="resource.messageWarningEn; else emptyMessageWarning">{{resource.messageWarningEn}}</span>
               <ng-template #emptyMessageWarning>
                 <span class="empty-property">Non renseigné</span>
               </ng-template>
diff --git a/src/app/components/resources/detail/resource-detail.component.scss b/src/app/components/resources/detail/resource-detail.component.scss
index 05012fa666610636fa011ff7250fcdc78764b468..d8e3c25a4e9537b0390658b05c3f7c7f81b1c550 100644
--- a/src/app/components/resources/detail/resource-detail.component.scss
+++ b/src/app/components/resources/detail/resource-detail.component.scss
@@ -1,8 +1,3 @@
 .card-header-title {
   justify-content: center;
 }
-
-.empty-property {
-  font-style: italic;
-  color: #818080;
-}
diff --git a/src/app/components/resources/edit/resource-form.component.html b/src/app/components/resources/edit/resource-form.component.html
index c41e9d4629578b708384787c031f58495eeb5a53..d19b840ea02e2a4be8343d223ec9b6baa57edeca 100644
--- a/src/app/components/resources/edit/resource-form.component.html
+++ b/src/app/components/resources/edit/resource-form.component.html
@@ -94,9 +94,16 @@
       </div>
 
       <div class="field">
-        <label class="label" for="messageWarning">Message d'alerte</label>
+        <label class="label" for="messageWarningFr">Message d'alerte (Français)</label>
         <div class="control">
-          <input class="input" type="text" formControlName="messageWarning" id="messageWarning">
+          <input class="input" type="text" formControlName="messageWarningFr" id="messageWarningFr">
+        </div>
+      </div>
+
+      <div class="field">
+        <label class="label" for="messageWarningEn">Message d'alerte (Anglais)</label>
+        <div class="control">
+          <input class="input" type="text" formControlName="messageWarningEn" id="messageWarningEn">
         </div>
       </div>
 
diff --git a/src/app/components/resources/edit/resource-form.component.ts b/src/app/components/resources/edit/resource-form.component.ts
index c379dc1024d744291b88030815c2df643de0114f..b69a143dedd91a3f8a108bfe2aa819dd889763d8 100644
--- a/src/app/components/resources/edit/resource-form.component.ts
+++ b/src/app/components/resources/edit/resource-form.component.ts
@@ -3,10 +3,10 @@ import { ActivatedRoute, ParamMap, Router } from '@angular/router';
 import { Resource, IResource } from 'src/app/models/resource.model';
 import { ResourceService } from 'src/app/services/resource.service';
 import { FormBuilder, FormGroup, Validators, FormArray } from '@angular/forms';
-import { filter, switchMap, mergeMap, merge, concatMap, map } from 'rxjs/operators';
+import { filter, switchMap, mergeMap, merge, concatMap, map, tap, catchError } from 'rxjs/operators';
 import { Format } from 'src/app/models/format.model';
-import { FormatService } from 'src/app/services';
-import { from, forkJoin, Observable, of } from 'rxjs';
+import { FormatService, NotificationService } from 'src/app/services';
+import { from, forkJoin, Observable, of, throwError } from 'rxjs';
 
 @Component({
   selector: 'app-resource-form',
@@ -26,6 +26,7 @@ export class ResourceFormComponent implements OnInit {
     private _route: ActivatedRoute,
     private _router: Router,
     private _fb: FormBuilder,
+    private _notificationService: NotificationService,
   ) {
   }
 
@@ -70,7 +71,8 @@ export class ResourceFormComponent implements OnInit {
         isStandard: [this.resource.isStandard, Validators.required],
         description: [this.resource.description],
         parametersUrl: [this.resource.parametersUrl],
-        messageWarning: [this.resource.messageWarning],
+        messageWarningFr: [this.resource.messageWarningFr],
+        messageWarningEn: [this.resource.messageWarningEn],
       });
   }
 
@@ -79,10 +81,36 @@ export class ResourceFormComponent implements OnInit {
       const resourceFormats = this.form.controls.resourceFormats['controls'].filter(e => e.dirty).map(e => e.value);
       const newResource = new Resource(this.form.value);
       let savedResource: Resource;
-      this._resourceService.replaceOrCreate(newResource).pipe(
+      let action = this._resourceService.create(newResource).pipe(
+        tap(() => {
+          this._notificationService.notify({
+            type: 'success',
+            message: 'La ressource a été créé avec succès.',
+          });
+        }),
+        catchError(() => {
+          return throwError('Une erreur s\'est produite lors de la création de la ressource');
+        }),
+      );
+
+      if (newResource.id) {
+        action = this._resourceService.update(newResource).pipe(
+          tap(() => {
+            this._notificationService.notify({
+              type: 'success',
+              message: 'La ressource a été mise à jour avec succès.',
+            });
+          }),
+          catchError(() => {
+            return throwError('Une erreur s\'est produite lors de la mise à jour de la ressource');
+          }),
+        );
+      }
+
+      action.pipe(
         mergeMap((resource) => {
           savedResource = resource;
-          let actions: Observable<any>[] = [];
+          const actions: Observable<any>[] = [];
 
           const toAdd = resourceFormats.filter(e => !e.id);
           const toUpdate = resourceFormats.filter(e => e.id)
@@ -107,12 +135,19 @@ export class ResourceFormComponent implements OnInit {
 
           return res;
         }),
-        map(() => savedResource.id),
+        map(() => { return savedResource.id; }),
+        catchError(() => {
+          return throwError('Une erreur s\'est produite lors de l\'association des formats à la ressource.');
+        }),
       ).subscribe(
         (resourceCreatedId) => {
           this._router.navigate(['/resources', resourceCreatedId]);
         },
         (err) => {
+          this._notificationService.notify({
+            type: 'error',
+            message: err,
+          });
         },
       );
     }
@@ -127,9 +162,6 @@ export class ResourceFormComponent implements OnInit {
   }
 
   addResourceFormat() {
-    // if (!this.form.controls.links) {
-    //   this.organization.links = [];
-    // }
     (this.form.controls.resourceFormats as FormArray).push(this._fb.group({
       formatId: [null, Validators.required],
       isProjectable: 0,
@@ -166,10 +198,6 @@ export class ResourceFormComponent implements OnInit {
     return this.form.controls.resourceFormats as FormArray;
   }
 
-  // get outputFormats() {
-  //   return this.form.controls['outputFormats'];
-  // }
-
   get formInvalid() {
     return this.form.invalid;
   }
diff --git a/src/app/components/resources/list/resources.component.html b/src/app/components/resources/list/resources.component.html
index f544a69abeaaa7d31ac75a5bf05f4ade9253d221..f3ce83f4c529dd0730cc9c33a23d3aefde493ac9 100644
--- a/src/app/components/resources/list/resources.component.html
+++ b/src/app/components/resources/list/resources.component.html
@@ -1,186 +1,188 @@
-<ng-container *ngIf="resources">
-  <div class="section page-container">
-    <app-page-header [pageInfo]="pageHeaderInfo" [hideBackButton]="true"></app-page-header>
-    <div class="add-item-link has-text-right">
-      <a class="button button-gl" [routerLink]="['new']">
-        Ajouter
-      </a>
+<div class="section page-container">
+  <app-page-header [pageInfo]="pageHeaderInfo" [hideBackButton]="true"></app-page-header>
+  <div class="add-item-link has-text-right">
+    <a class="button button-gl" [routerLink]="['new']">
+      Ajouter
+    </a>
+  </div>
+  <div class="table entity-list-table" *ngIf="resources">
+    <div class="header columns is-marginless">
+      <div class="column is-2">
+        <span (click)="sortBy('name')" class="is-sortable">
+          <span class="sort-icons">
+            <span class="icon">
+              <i class="fas fa-sort-up"
+                [ngClass]="{'icon-red': sortOptions.value === 'name' && sortOptions.order === 'desc'}"></i>
+            </span>
+            <span class="icon">
+              <i class="fas fa-sort-down"
+                [ngClass]="{'icon-red': sortOptions.value === 'name' && sortOptions.order === 'asc'}"></i>
+            </span>
+          </span>
+          <span class="column-title" [ngClass]="{'active': sortOptions.value === name}">Nom</span>
+        </span>
+      </div>
+      <div class="column is-1">
+        <span (click)="sortBy('acronym')" class="is-sortable">
+          <span class="sort-icons">
+            <span class="icon">
+              <i class="fas fa-sort-up"
+                [ngClass]="{'icon-red': sortOptions.value === 'acronym' && sortOptions.order === 'desc'}"></i>
+            </span>
+            <span class="icon">
+              <i class="fas fa-sort-down"
+                [ngClass]="{'icon-red': sortOptions.value === 'acronym' && sortOptions.order === 'asc'}"></i>
+            </span>
+          </span>
+          <span class="column-title" [ngClass]="{'active': sortOptions.value === acronym}">Acronyme</span>
+        </span>
+      </div>
+      <div class="column is-1">
+        <span (click)="sortBy('type')" class="is-sortable">
+          <span class="sort-icons">
+            <span class="icon">
+              <i class="fas fa-sort-up"
+                [ngClass]="{'icon-red': sortOptions.value === 'type' && sortOptions.order === 'desc'}"></i>
+            </span>
+            <span class="icon">
+              <i class="fas fa-sort-down"
+                [ngClass]="{'icon-red': sortOptions.value === 'type' && sortOptions.order === 'asc'}"></i>
+            </span>
+          </span>
+          <span class="column-title" [ngClass]="{'active': sortOptions.value === type}">Type</span>
+        </span>
+      </div>
+      <div class="column is-4">
+        <span (click)="sortBy('description')" class="is-sortable">
+          <span class="sort-icons">
+            <span class="icon">
+              <i class="fas fa-sort-up"
+                [ngClass]="{'icon-red': sortOptions.value === 'description' && sortOptions.order === 'desc'}"></i>
+            </span>
+            <span class="icon">
+              <i class="fas fa-sort-down"
+                [ngClass]="{'icon-red': sortOptions.value === 'description' && sortOptions.order === 'asc'}"></i>
+            </span>
+          </span>
+          <span class="column-title" [ngClass]="{'active': sortOptions.value === description}">Description</span>
+        </span>
+      </div>
+      <div class="column is-1 has-text-centered">
+        <span (click)="sortBy('isQueryable')" class="is-sortable">
+          <span class="sort-icons">
+            <span class="icon">
+              <i class="fas fa-sort-up"
+                [ngClass]="{'icon-red': sortOptions.value === 'isQueryable' && sortOptions.order === 'desc'}"></i>
+            </span>
+            <span class="icon">
+              <i class="fas fa-sort-down"
+                [ngClass]="{'icon-red': sortOptions.value === 'isQueryable' && sortOptions.order === 'asc'}"></i>
+            </span>
+          </span>
+          <span class="column-title" [ngClass]="{'active': sortOptions.value === isQueryable}">Requêtable</span>
+        </span>
+      </div>
+      <div class="column is-1 has-text-centered">
+        <span (click)="sortBy('isDownloadable')" class="is-sortable">
+          <span class="sort-icons">
+            <span class="icon">
+              <i class="fas fa-sort-up"
+                [ngClass]="{'icon-red': sortOptions.value === 'isDownloadable' && sortOptions.order === 'desc'}"></i>
+            </span>
+            <span class="icon">
+              <i class="fas fa-sort-down"
+                [ngClass]="{'icon-red': sortOptions.value === 'isDownloadable' && sortOptions.order === 'asc'}"></i>
+            </span>
+          </span>
+          <span class="column-title" [ngClass]="{'active': sortOptions.value === isDownloadable}">Téléchargeable</span>
+        </span>
+      </div>
+      <div class="column is-1 has-text-centered">
+        <span (click)="sortBy('isStandard')" class="is-sortable">
+          <span class="sort-icons">
+            <span class="icon">
+              <i class="fas fa-sort-up"
+                [ngClass]="{'icon-red': sortOptions.value === 'isStandard' && sortOptions.order === 'desc'}"></i>
+            </span>
+            <span class="icon">
+              <i class="fas fa-sort-down"
+                [ngClass]="{'icon-red': sortOptions.value === 'isStandard' && sortOptions.order === 'asc'}"></i>
+            </span>
+          </span>
+          <span class="column-title" [ngClass]="{'active': sortOptions.value === isStandard}">Standard</span>
+        </span>
+      </div>
+      <div class="column is-1 has-text-centered">
+        <span class="column-title">Actions</span>
+      </div>
     </div>
-    <div class="table entity-list-table">
-      <div class="header columns is-marginless">
+    <div class="data-list">
+      <div class="data columns is-multiline is-vcentered is-marginless"
+        *ngFor="let resource of resources; let i=index; let odd=odd; let even=even;"
+        [ngClass]="{ odd: odd, even: even }">
         <div class="column is-2">
-          <span (click)="sortBy('name')" class="is-sortable">
-            <span class="sort-icons">
-              <span class="icon">
-                <i class="fas fa-sort-up"
-                  [ngClass]="{'icon-red': sortOptions.value === 'name' && sortOptions.order === 'desc'}"></i>
-              </span>
-              <span class="icon">
-                <i class="fas fa-sort-down"
-                  [ngClass]="{'icon-red': sortOptions.value === 'name' && sortOptions.order === 'asc'}"></i>
-              </span>
-            </span>
-            <span class="column-title" [ngClass]="{'active': sortOptions.value === name}">Nom</span>
+          <span>
+            {{ resource.name}}
           </span>
+
         </div>
         <div class="column is-1">
-          <span (click)="sortBy('acronym')" class="is-sortable">
-            <span class="sort-icons">
-              <span class="icon">
-                <i class="fas fa-sort-up"
-                  [ngClass]="{'icon-red': sortOptions.value === 'acronym' && sortOptions.order === 'desc'}"></i>
-              </span>
-              <span class="icon">
-                <i class="fas fa-sort-down"
-                  [ngClass]="{'icon-red': sortOptions.value === 'acronym' && sortOptions.order === 'asc'}"></i>
-              </span>
-            </span>
-            <span class="column-title" [ngClass]="{'active': sortOptions.value === acronym}">Acronyme</span>
+          <span>
+            {{ resource.acronym}}
           </span>
+
         </div>
         <div class="column is-1">
-          <span (click)="sortBy('type')" class="is-sortable">
-            <span class="sort-icons">
-              <span class="icon">
-                <i class="fas fa-sort-up"
-                  [ngClass]="{'icon-red': sortOptions.value === 'type' && sortOptions.order === 'desc'}"></i>
-              </span>
-              <span class="icon">
-                <i class="fas fa-sort-down"
-                  [ngClass]="{'icon-red': sortOptions.value === 'type' && sortOptions.order === 'asc'}"></i>
-              </span>
-            </span>
-            <span class="column-title" [ngClass]="{'active': sortOptions.value === type}">Type</span>
+          <span>
+            {{ resource.type}}
           </span>
+
         </div>
         <div class="column is-4">
-          <span (click)="sortBy('description')" class="is-sortable">
-            <span class="sort-icons">
-              <span class="icon">
-                <i class="fas fa-sort-up"
-                  [ngClass]="{'icon-red': sortOptions.value === 'description' && sortOptions.order === 'desc'}"></i>
-              </span>
-              <span class="icon">
-                <i class="fas fa-sort-down"
-                  [ngClass]="{'icon-red': sortOptions.value === 'description' && sortOptions.order === 'asc'}"></i>
-              </span>
-            </span>
-            <span class="column-title" [ngClass]="{'active': sortOptions.value === description}">Description</span>
+          <span>
+            {{ resource.description | slice:0:300}}
           </span>
         </div>
         <div class="column is-1 has-text-centered">
-          <span (click)="sortBy('isQueryable')" class="is-sortable">
-            <span class="sort-icons">
-              <span class="icon">
-                <i class="fas fa-sort-up"
-                  [ngClass]="{'icon-red': sortOptions.value === 'isQueryable' && sortOptions.order === 'desc'}"></i>
-              </span>
-              <span class="icon">
-                <i class="fas fa-sort-down"
-                  [ngClass]="{'icon-red': sortOptions.value === 'isQueryable' && sortOptions.order === 'asc'}"></i>
-              </span>
-            </span>
-            <span class="column-title" [ngClass]="{'active': sortOptions.value === isQueryable}">Requêtable</span>
+          <span class="icon has-text-success" *ngIf="resource.isQueryable">
+            <i class="far fa-check-circle"></i>
+          </span>
+          <span class="icon has-text-danger" *ngIf="!resource.isQueryable">
+            <i class="far fa-times-circle"></i>
           </span>
         </div>
         <div class="column is-1 has-text-centered">
-          <span (click)="sortBy('isDownloadable')" class="is-sortable">
-            <span class="sort-icons">
-              <span class="icon">
-                <i class="fas fa-sort-up"
-                  [ngClass]="{'icon-red': sortOptions.value === 'isDownloadable' && sortOptions.order === 'desc'}"></i>
-              </span>
-              <span class="icon">
-                <i class="fas fa-sort-down"
-                  [ngClass]="{'icon-red': sortOptions.value === 'isDownloadable' && sortOptions.order === 'asc'}"></i>
-              </span>
-            </span>
-            <span class="column-title"
-              [ngClass]="{'active': sortOptions.value === isDownloadable}">Téléchargeable</span>
+          <span class="icon has-text-success" *ngIf="resource.isDownloadable">
+            <i class="far fa-check-circle"></i>
+          </span>
+          <span class="icon has-text-danger" *ngIf="!resource.isDownloadable">
+            <i class="far fa-times-circle"></i>
           </span>
         </div>
         <div class="column is-1 has-text-centered">
-          <span (click)="sortBy('isStandard')" class="is-sortable">
-            <span class="sort-icons">
-              <span class="icon">
-                <i class="fas fa-sort-up"
-                  [ngClass]="{'icon-red': sortOptions.value === 'isStandard' && sortOptions.order === 'desc'}"></i>
-              </span>
-              <span class="icon">
-                <i class="fas fa-sort-down"
-                  [ngClass]="{'icon-red': sortOptions.value === 'isStandard' && sortOptions.order === 'asc'}"></i>
-              </span>
-            </span>
-            <span class="column-title" [ngClass]="{'active': sortOptions.value === isStandard}">Standard</span>
+          <span class="icon has-text-success" *ngIf="resource.isStandard">
+            <i class="far fa-check-circle"></i>
+          </span>
+          <span class="icon has-text-danger" *ngIf="!resource.isStandard">
+            <i class="far fa-times-circle"></i>
           </span>
         </div>
         <div class="column is-1 has-text-centered">
-          <span class="column-title">Actions</span>
+          <app-crud-buttons [id]="resource.id" (delete)="displayDeletePopup($event)"></app-crud-buttons>
         </div>
       </div>
-      <div class="data-list">
-        <div class="data columns is-multiline is-vcentered is-marginless"
-          *ngFor="let resource of resources; let i=index; let odd=odd; let even=even;"
-          [ngClass]="{ odd: odd, even: even }">
-          <div class="column is-2">
-            <span>
-              {{ resource.name}}
-            </span>
-
-          </div>
-          <div class="column is-1">
-            <span>
-              {{ resource.acronym}}
-            </span>
-
-          </div>
-          <div class="column is-1">
-            <span>
-              {{ resource.type}}
-            </span>
-
-          </div>
-          <div class="column is-4">
-            <span>
-              {{ resource.description | slice:0:300}}
-            </span>
-          </div>
-          <div class="column is-1 has-text-centered">
-            <span class="icon has-text-success" *ngIf="resource.isQueryable">
-              <i class="far fa-check-circle"></i>
-            </span>
-            <span class="icon has-text-danger" *ngIf="!resource.isQueryable">
-              <i class="far fa-times-circle"></i>
-            </span>
-          </div>
-          <div class="column is-1 has-text-centered">
-            <span class="icon has-text-success" *ngIf="resource.isDownloadable">
-              <i class="far fa-check-circle"></i>
-            </span>
-            <span class="icon has-text-danger" *ngIf="!resource.isDownloadable">
-              <i class="far fa-times-circle"></i>
-            </span>
-          </div>
-          <div class="column is-1 has-text-centered">
-            <span class="icon has-text-success" *ngIf="resource.isStandard">
-              <i class="far fa-check-circle"></i>
-            </span>
-            <span class="icon has-text-danger" *ngIf="!resource.isStandard">
-              <i class="far fa-times-circle"></i>
-            </span>
-          </div>
-          <div class="column is-1 has-text-centered">
-            <app-crud-buttons [id]="resource.id" (delete)="displayDeletePopup($event)"></app-crud-buttons>
-          </div>
-        </div>
-      </div>
-      <div class="columns is-marginless paginator">
-        <div class="column">
-          <app-paginator *ngIf="paginator.length > 0" [length]="paginator.length" [pageSize]="paginator.limit"
-            [pageSizeOptions]="paginator.pageSizeOptions" [pageIndex]="paginator.pageIndex" [pagesToShow]="5"
-            [showFirstLastButtons]="true" (page)="changePagination($event)" (pageSizeChanged)="changePageSize($event)">
-          </app-paginator>
-        </div>
+    </div>
+    <div class="columns is-marginless paginator">
+      <div class="column">
+        <app-paginator *ngIf="paginator.length > 0" [length]="paginator.length" [pageSize]="paginator.limit"
+          [pageSizeOptions]="paginator.pageSizeOptions" [pageIndex]="paginator.pageIndex" [pagesToShow]="5"
+          [showFirstLastButtons]="true" (page)="changePagination($event)" (pageSizeChanged)="changePageSize($event)">
+        </app-paginator>
       </div>
     </div>
-  </div>
\ No newline at end of file
+  </div>
+</div>
+
+<app-confirmation-modal (cancel)="objectToBeDeletedId=null" (continue)="deleteResource()" [texts]="deleteModalTexts"
+  [isOpened]="objectToBeDeletedId !== null">
+</app-confirmation-modal>
\ No newline at end of file
diff --git a/src/app/components/resources/list/resources.component.ts b/src/app/components/resources/list/resources.component.ts
index b19fe3e458a99f7a4a7a0818d03fe2929d81b218..f37b37a6e0e1874407b906986921ae87570c2d44 100644
--- a/src/app/components/resources/list/resources.component.ts
+++ b/src/app/components/resources/list/resources.component.ts
@@ -1,21 +1,28 @@
-import { Component, OnInit } from '@angular/core';
+import { Component, OnInit, OnDestroy } from '@angular/core';
 import { Resource, ResourceRO } from 'src/app/models/resource.model';
 import { ResourceService } from 'src/app/services/resource.service';
 import { Subscription } from 'rxjs';
 import { PaginatorOptions } from 'src/app/models/paginator-options.model';
 import { IPageHeaderInfo } from '../../../models/page.model';
+import { NotificationService } from '../../../services';
 
 @Component({
   selector: 'app-resources',
   templateUrl: './resources.component.html',
   styleUrls: ['./resources.component.scss'],
 })
-export class ResourcesComponent implements OnInit {
+export class ResourcesComponent implements OnInit, OnDestroy {
 
   pageHeaderInfo: IPageHeaderInfo = {
     title: '',
   };
-  resources: Resource[];
+  objectToBeDeletedId = null;
+  deleteModalTexts = {
+    main: 'Si vous poursuivez, la ressource sera définitivement supprimée.',
+    cancel: 'Annuler',
+    continue: 'Supprimer',
+  };
+  resources: Resource[] = [];
   searchChangeSub: Subscription;
 
   // Paginator options
@@ -32,24 +39,25 @@ export class ResourcesComponent implements OnInit {
   where = {};
 
   constructor(
-    private resourcesService: ResourceService,
+    private _resourceService: ResourceService,
+    private _notificationService: NotificationService,
   ) {
     this.paginator = {
-      pageIndex: this.resourcesService.pageNumber,
+      pageIndex: this._resourceService.pageNumber,
       length: 0,
-      limit: this.resourcesService.limit,
+      limit: this._resourceService.limit,
       pageSizeOptions: [5, 10, 20],
     };
   }
 
   ngOnInit(): void {
-    this.resourcesService.sortOptions = {
+    this._resourceService.sortOptions = {
       value: 'name',
       order: 'asc',
     };
     this.search();
 
-    this.searchChangeSub = this.resourcesService.searchChange$.subscribe(
+    this.searchChangeSub = this._resourceService.searchChange$.subscribe(
       () => {
         this.search();
       },
@@ -57,49 +65,81 @@ export class ResourcesComponent implements OnInit {
   }
 
   private search() {
-    this.resourcesService.getResources()
-      .subscribe((items: ResourceRO) => {
-        this.resources = items.resources;
-        this.totalElement = items.totalCount;
-
-        this.pageHeaderInfo.title = `${this.totalElement} ressources trouvées`;
-
-        this.paginator.limit = this.resourcesService.limit;
-        this.paginator.pageIndex = this.resourcesService.pageNumber;
-        this.paginator.length = items.totalCount;
-      });
+    this._resourceService.getResources()
+      .subscribe(
+        (items: ResourceRO) => {
+          this.resources = items.resources;
+          this.totalElement = items.totalCount;
+
+          this.pageHeaderInfo.title = `${this.totalElement} ressources trouvées`;
+          this.pageHeaderInfo.title = this.totalElement > 1 ?
+            `${this.totalElement} ressources trouvées` :
+            `${this.totalElement} ressource trouvée`;
+
+          this.paginator.limit = this._resourceService.limit;
+          this.paginator.pageIndex = this._resourceService.pageNumber;
+          this.paginator.length = items.totalCount;
+        },
+        () => {
+          this.pageHeaderInfo.title = '0 ressource trouvée';
+          this._notificationService.notify({
+            type: 'error',
+            message: 'Une erreur est survenue lors du chargement des ressources.',
+          });
+        },
+      );
   }
 
   // When pagination is changed by user, we update datasetList with new pagination options
   changePagination(pageIndex) {
-    this.resourcesService.paginationChanged(this.paginator.limit, pageIndex);
+    this._resourceService.paginationChanged(this.paginator.limit, pageIndex);
   }
 
   changePageSize(pageSize) {
-    this.resourcesService.paginationChanged(pageSize, 1);
+    this._resourceService.paginationChanged(pageSize, 1);
   }
 
   sortBy(key: string) {
-    if (this.resourcesService.sortOptions.value === key) {
-      this.resourcesService.reverseSortOrder();
+    if (this._resourceService.sortOptions.value === key) {
+      this._resourceService.reverseSortOrder();
     } else {
-      this.resourcesService.sortOptions.value = key;
-      this.resourcesService.sortOptions.order = 'asc';
+      this._resourceService.sortOptions.value = key;
+      this._resourceService.sortOptions.order = 'asc';
     }
     this.search();
   }
 
   get sortOptions() {
-    return this.resourcesService.sortOptions;
+    return this._resourceService.sortOptions;
+  }
+
+  displayDeletePopup(id) {
+    this.objectToBeDeletedId = id;
   }
 
-  displayDeletePopup(resourceId) {
-    const pop = confirm('Etes vous sûr de vouloir supprimer cette ressource ?');
-    if (pop) {
-      this.resourcesService.delete(resourceId).subscribe(() => {
-        this.resourcesService.pageNumber = 1;
+  deleteResource() {
+    this._resourceService.delete(this.objectToBeDeletedId).subscribe(
+      () => {
+        this._notificationService.notify({
+          type: 'success',
+          message: 'La ressource a été supprimée avec succès.',
+        });
+        this._resourceService.pageNumber = 1;
         this.search();
-      });
-    }
+      },
+      () => {
+        this._notificationService.notify({
+          type: 'error',
+          message: 'Une erreur est survenue lors de la suppression de la ressource.',
+        });
+      },
+      () => {
+        this.objectToBeDeletedId = null;
+      },
+    );
+  }
+
+  ngOnDestroy() {
+    this.searchChangeSub.unsubscribe();
   }
 }
diff --git a/src/app/components/reuses/detail/reuse-detail.component.html b/src/app/components/reuses/detail/reuse-detail.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..def3b4638daf1cf80179be0aa615741d63343e61
--- /dev/null
+++ b/src/app/components/reuses/detail/reuse-detail.component.html
@@ -0,0 +1,67 @@
+<section class="section page-container" *ngIf="reuse">
+  <app-page-header [pageInfo]="{title: title}"></app-page-header>
+
+  <div class="columns is-centered">
+    <div class="column is-8">
+      <div class="card">
+        <header class="card-header">
+          <p class="card-header-title has-text-centered">
+            {{reuse.name}}
+          </p>
+        </header>
+        <div class="card-image">
+          <figure class="image">
+            <img [src]="reuse.logo" alt="Logo de la réutilisation">
+          </figure>
+        </div>
+        <div class="card-content">
+          <div class="content">
+            <p>
+              <span class="has-text-weight-bold">Id: </span>
+              <span>{{reuse._id}}</span>
+            </p>
+            <p>
+              <span class="has-text-weight-bold">Créateur: </span>
+              <span>{{reuse.creator}}</span>
+            </p>
+            <p>
+              <span class="has-text-weight-bold">Date de création: </span>
+              <span>{{reuse.createDate | date:'dd-LL-yyyy HH:mm:ss'}}</span>
+            </p>
+            <p>
+              <span class="has-text-weight-bold">Date de dernière mise à jour: </span>
+              <span>{{reuse.updateDate | date:'dd-LL-yyyy HH:mm:ss'}}</span>
+            </p>
+
+            <p>
+              <span class="has-text-weight-bold">Statut:</span> {{ reuse.published ? 'Publié' : 'Brouillon' }}
+            </p>
+
+            <p>
+              <span class="has-text-weight-bold">Site web: </span>
+              <span><a [href]="reuse.website">{{reuse.website}}</a></span>
+            </p>
+
+            <div class="list-container" *ngIf="reuse.reuseTypes && reuse.reuseTypes.length > 0">
+              <span class="has-text-weight-bold">Type(s) de réutilisation: </span>
+              <ul>
+                <li *ngFor="let reuseType of reuseTypes">
+                  {{ reuseType }}
+                </li>
+              </ul>
+            </div>
+
+            <div class="list-container" *ngIf="reuse.datasetsUsed && reuse.datasetsUsed.length > 0">
+              <span class="has-text-weight-bold">Jeu(x) de données réutilisé(s): </span>
+              <ul>
+                <li *ngFor="let dataset of reuse.datasetsUsed">
+                  {{ dataset }}
+                </li>
+              </ul>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</section>
\ No newline at end of file
diff --git a/src/app/components/reuses/detail/reuse-detail.component.scss b/src/app/components/reuses/detail/reuse-detail.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..369170463fc9b68f9075aca173452f0c23f1765d
--- /dev/null
+++ b/src/app/components/reuses/detail/reuse-detail.component.scss
@@ -0,0 +1,21 @@
+.card-header-title {
+  justify-content: center;
+}
+
+figure {
+  text-align: center;
+}
+
+figure img {
+  max-width: 150px;
+  display: inline-block;
+  margin-top: 20px;
+}
+
+.list-container:not(:last-of-type) {
+  margin-bottom: 1rem;
+
+  ul {
+    margin-top: 0.5rem;
+  }
+}
diff --git a/src/app/components/reuses/detail/reuse-detail.component.ts b/src/app/components/reuses/detail/reuse-detail.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..22d807d39a94a065d9317fcd328333769f9e21f3
--- /dev/null
+++ b/src/app/components/reuses/detail/reuse-detail.component.ts
@@ -0,0 +1,34 @@
+
+import { switchMap } from 'rxjs/operators';
+import { Component, OnInit } from '@angular/core';
+import { ActivatedRoute, ParamMap } from '@angular/router';
+import { Reuse, ReuseTypes } from '../../../models/reuse.model';
+import { ReuseService } from '../../../services';
+
+@Component({
+  selector: 'app-reuse-detail',
+  templateUrl: './reuse-detail.component.html',
+  styleUrls: ['./reuse-detail.component.scss'],
+})
+export class ReuseDetailComponent implements OnInit {
+
+  reuse: Reuse;
+  title: string;
+
+  constructor(
+    private _route: ActivatedRoute,
+    private _reuseService: ReuseService,
+  ) {
+  }
+
+  ngOnInit(): void {
+    this.title = this._route.snapshot.data.title;
+    this._route.paramMap.pipe(
+      switchMap((params: ParamMap) => this._reuseService.findById(params.get('id'))))
+      .subscribe((reuse: Reuse) => this.reuse = reuse);
+  }
+
+  get reuseTypes() {
+    return this.reuse.reuseTypes.map(e => ReuseTypes[e]);
+  }
+}
diff --git a/src/app/components/reuses/edit/reuse-form.component.html b/src/app/components/reuses/edit/reuse-form.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..331d49d63dc3b9f4ad9ac1a66d5548509c3451b6
--- /dev/null
+++ b/src/app/components/reuses/edit/reuse-form.component.html
@@ -0,0 +1,126 @@
+<section class="section page-container" *ngIf="reuse">
+  <form [formGroup]="form" (ngSubmit)="onSubmit()" class="columns is-centered is-marginless is-multiline">
+    <div class="column is-12 header-with-publication-status">
+      <app-page-header [pageInfo]="{title: title}"></app-page-header>
+      <div class="field status-field">
+        <span class="fake-label" *ngIf="form.get('published').value === true">Publié</span>
+        <span class="fake-label" *ngIf="form.get('published').value === false">Brouillon</span>
+        <input id="published" type="checkbox" formControlName="published" class="switch is-rounded">
+        <label for="published"></label>
+      </div>
+    </div>
+    <div class="column is-7">
+      <input type="hidden" formControlName="_id">
+
+      <div class="field">
+        <label class="label required" for="name">Nom</label>
+        <div class="control">
+          <input class="input" type="text" formControlName="name" id="name" required>
+        </div>
+        <div *ngIf="name.invalid && (name.dirty || name.touched)" class="alert alert-danger">
+          <p *ngIf="name.errors['required']" class="help is-danger">
+            Le nom de la réutilisation est obligatoire.
+          </p>
+        </div>
+      </div>
+
+      <app-image-upload [fieldParams]="logoFieldParams" (fileChanged)="logoChanged($event)"
+        (imageRemoved)="removeLogo()">
+      </app-image-upload>
+
+      <div class="field">
+        <label class="label required" for="name">Créateur</label>
+        <div class="control">
+          <input class="input" type="text" formControlName="creator" id="creator" required>
+        </div>
+        <div *ngIf="creator.invalid && (creator.dirty || creator.touched)" class="alert alert-danger">
+          <p *ngIf="creator.errors['required']" class="help is-danger">
+            Le créateur est obligatoire.
+          </p>
+        </div>
+      </div>
+
+      <div class="field">
+        <label class="label required" for="name">Site web</label>
+        <div class="control">
+          <input class="input" type="text" formControlName="website" id="website" required>
+        </div>
+        <div *ngIf="website.invalid && (website.dirty || website.touched)" class="alert alert-danger">
+          <p *ngIf="website.errors['required']" class="help is-danger">
+            Le site web est obligatoire.
+          </p>
+        </div>
+      </div>
+
+      <div class="field">
+        <div class="form-array-header">
+          <label class="label">Type de réutilisation</label>
+          <span class="icon" tabindex=0 (click)="addReuseType()" (keyup.enter)="addReuseType()"
+            title="Ajouter un type de réutilisation">
+            <i class="fas fa-plus"></i>
+          </span>
+        </div>
+
+        <div formArrayName="reuseTypes">
+          <div *ngFor="let reuseType of reuseTypes.controls; let i = index;" class="form-array-item">
+            <div class="form-array-input-wrapper">
+              <div class="field">
+                <div class="control">
+                  <div class="select">
+                    <select type="text" [formControlName]="i" required>
+                      <option hidden value="" disabled selected>Selectionnez un type</option>
+                      <option *ngFor="let key of objectKeys(reuseTypesList)" [value]="key">{{reuseTypesList[key]}}
+                      </option>
+                    </select>
+                  </div>
+                </div>
+              </div>
+              <div *ngIf="reuseType.invalid && (reuseType.dirty || reuseType.touched)" class="alert alert-danger">
+                <p *ngIf="reuseType.hasError('required')" class="help is-danger">
+                  Vous devez saisir le type de réutilisation.
+                </p>
+              </div>
+            </div>
+            <span class="icon" tabindex=0 (click)="removeReuseType(i)" (keyup.enter)="removeReuseType(i)"
+              title="Supprimer le type de réutilisation">
+              <i class="fas fa-trash"></i>
+            </span>
+          </div>
+        </div>
+      </div>
+
+      <div class="field">
+        <div class="form-array-header">
+          <label class="label">Jeu(x) de données réutilisé(s)</label>
+          <span class="icon" tabindex=0 (click)="addDatasetUsed()" (keyup.enter)="addDatasetUsed()"
+            title="Ajouter un jeu de données réutilisé">
+            <i class="fas fa-plus"></i>
+          </span>
+        </div>
+
+        <div formArrayName="datasetsUsed">
+          <div *ngFor="let datasetUsed of datasetsUsed.controls; let i = index;" class="form-array-item">
+            <div class="form-array-input-wrapper">
+              <div class="control">
+                <input class="input" type="text" [formControlName]="i" required>
+              </div>
+              <div *ngIf="datasetUsed.invalid && (datasetUsed.dirty || datasetUsed.touched)" class="alert alert-danger">
+                <p *ngIf="datasetUsed.hasError('required')" class="help is-danger">
+                  Vous devez saisir le slug du jeu de données réutilisé.
+                </p>
+              </div>
+            </div>
+            <span class="icon" tabindex=0 (click)="removeDatasetUsed(i)" (keyup.enter)="removeDatasetUsed(i)"
+              title="Supprimer le jeu de données réutilisé">
+              <i class="fas fa-trash"></i>
+            </span>
+          </div>
+        </div>
+      </div>
+
+      <div class="has-text-right">
+        <button class="button button-gl" type="submit" [disabled]="formInvalid == true">Valider</button>
+      </div>
+    </div>
+  </form>
+</section>
\ No newline at end of file
diff --git a/src/app/components/reuses/edit/reuse-form.component.scss b/src/app/components/reuses/edit/reuse-form.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..4bc5c21221ab9f7ba636ca989a4038e8b3db6af0
--- /dev/null
+++ b/src/app/components/reuses/edit/reuse-form.component.scss
@@ -0,0 +1,43 @@
+.full-width {
+  width: 100%;
+}
+
+h1 {
+  text-align: center
+}
+
+.icon {
+  cursor: pointer;
+
+  &:hover {
+    .fa-plus {
+      color: lightblue;
+    }
+
+    .fa-trash {
+      color: #d5232a;
+    }
+  }
+}
+
+.form-array-header {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  margin-bottom: 0.5em;
+
+  label {
+    margin-bottom: 0;
+  }
+}
+
+.form-array-item {
+  display: flex;
+  align-items: center;
+  margin-bottom: 0.5em;
+}
+
+.form-array-input-wrapper {
+  flex-grow: 1;
+  margin-right: 0.5rem;
+}
diff --git a/src/app/components/reuses/edit/reuse-form.component.ts b/src/app/components/reuses/edit/reuse-form.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..632866a762cb96e9d055d3a4386b19d3260194df
--- /dev/null
+++ b/src/app/components/reuses/edit/reuse-form.component.ts
@@ -0,0 +1,232 @@
+import { Component, OnInit } from '@angular/core';
+import { ActivatedRoute, ParamMap, Router } from '@angular/router';
+import { FormBuilder, FormGroup, Validators, FormArray, FormControl, MaxLengthValidator } from '@angular/forms';
+import { filter, switchMap, catchError, mergeMap } from 'rxjs/operators';
+import { NotificationService, ReuseService, MediaService } from 'src/app/services';
+import { Reuse, ReuseTypes } from '../../../models/reuse.model';
+import { IImageUploadFieldParams } from '../../../models/image-upload.model';
+import { throwError } from 'rxjs';
+import { Media } from '../../../models/media.model';
+
+@Component({
+  selector: 'app-reuse-form',
+  templateUrl: './reuse-form.component.html',
+  styleUrls: ['./reuse-form.component.scss'],
+})
+export class ReuseFormComponent implements OnInit {
+
+  reuse: Reuse = new Reuse();
+  form: FormGroup;
+  logoFile: File;
+  logoFieldParams: IImageUploadFieldParams = {
+    inputName: 'logo',
+    label: 'Logo',
+    existingImageUrl: null,
+    isRequired: true,
+  };
+  title: string;
+  objectKeys = Object.keys; // User to iterate over the keys of an object in the template html
+
+  constructor(
+    private _reuseService: ReuseService,
+    private _mediaService: MediaService,
+    private _route: ActivatedRoute,
+    private _router: Router,
+    private _fb: FormBuilder,
+    private _notificationService: NotificationService,
+  ) {
+  }
+
+  ngOnInit() {
+    this.title = this._route.snapshot.data.title;
+    this.initForm();
+
+    this._route.paramMap.pipe(
+      filter((paramMap: ParamMap) => (paramMap.get('id') !== null)),
+      switchMap((paramMap: ParamMap) => this._reuseService.findById(paramMap.get('id'))))
+      .subscribe((reuse: Reuse) => {
+
+        this.reuse = reuse;
+
+        this.logoFieldParams.existingImageUrl = reuse.logo;
+
+        this.initForm();
+
+      });
+  }
+
+  initForm() {
+    this.form = this._fb.group({
+      _id: [this.reuse._id],
+      name: [this.reuse.name, Validators.required],
+      creator: [this.reuse.creator, Validators.required],
+      logo: [this.reuse.logo],
+      website: [this.reuse.website, Validators.required],
+      published: [this.reuse.published, Validators.required],
+      reuseTypes: new FormArray(this.reuse.reuseTypes.map(major => new FormControl(major)), Validators.maxLength(3)),
+      datasetsUsed: new FormArray(this.reuse.datasetsUsed.map(minor => new FormControl(minor))),
+    });
+  }
+
+  onSubmit() {
+    if (!this.formInvalid) {
+      this.reuse = new Reuse(this.form.value);
+
+      if (this.reuse._id) {
+        if (this.logoFile) {
+          this._mediaService.uploadFile(this.logoFile).pipe(
+            catchError(() => {
+              return throwError('Une erreur est survenue lors de l\'upload du logo de la réutilisation.');
+            }),
+            mergeMap((response: Media) => {
+              this.reuse.logo = response.url;
+              return this._reuseService.update(this.reuse).pipe(
+                catchError(() => {
+                  return throwError('Une erreur est survenue lors de la mise à jour de la réutilisation.');
+                }),
+              );
+            }),
+          ).subscribe(
+            (reuseUpdated) => {
+              this._notificationService.notify({
+                message: 'La réutilisation a été mise à jour avec succès.',
+                type: 'success',
+              });
+              this._router.navigate(['/reutilisations', this.reuse._id]);
+            },
+            (err) => {
+              this._notificationService.notify({
+                message: err,
+                type: 'error',
+              });
+            },
+          );
+        } else {
+          return this._reuseService.update(this.reuse).subscribe(
+            (reuseUpdated) => {
+              this._notificationService.notify({
+                message: 'La réutilisation a été mise à jour avec succès.',
+                type: 'success',
+              });
+              this._router.navigate(['/reutilisations', this.reuse._id]);
+            },
+            () => {
+              this._notificationService.notify({
+                message: 'Une erreur est survenue lors de la mise à jour de la réutilisation.',
+                type: 'error',
+              });
+            },
+          );
+        }
+      } else {
+        if (this.logoFile) {
+          this._mediaService.uploadFile(this.logoFile).pipe(
+            catchError(() => {
+              return throwError('Une erreur est survenue lors de l\'upload du logo de la réutilisation.');
+            }),
+            mergeMap((response: Media) => {
+              this.reuse.logo = response.url;
+              return this._reuseService.create(this.reuse).pipe(
+                catchError(() => {
+                  return throwError('Une erreur est survenue lors de la création de la réutilisation.');
+                }),
+              );
+            }),
+          ).subscribe(
+            (reutilisationCreated) => {
+              this._notificationService.notify({
+                message: 'La réutilisation a été créée avec succès.',
+                type: 'success',
+              });
+              this._router.navigate(['/reutilisations', reutilisationCreated._id]);
+            },
+            (err) => {
+              this._notificationService.notify({
+                message: err,
+                type: 'error',
+              });
+            },
+          );
+        } else {
+          return this._reuseService.create(this.reuse).subscribe(
+            (reuseCreated) => {
+              this._notificationService.notify({
+                message: 'La réutilisation a été créée avec succès.',
+                type: 'success',
+              });
+              this._router.navigate(['/reutilisations', reuseCreated._id]);
+            },
+            () => {
+              this._notificationService.notify({
+                message: 'Une erreur est survenue lors de la création de la réutilisation.',
+                type: 'error',
+              });
+            },
+          );
+        }
+      }
+    }
+  }
+
+  addReuseType() {
+    if (this.reuseTypes.length < Object.keys(this.reuseTypesList).length) {
+      this.reuseTypes.push(new FormControl(''));
+    }
+  }
+
+  addDatasetUsed() {
+    this.datasetsUsed.push(new FormControl());
+  }
+
+  removeReuseType(index) {
+    this.reuseTypes.removeAt(index);
+  }
+
+  removeDatasetUsed(index) {
+    this.datasetsUsed.removeAt(index);
+  }
+
+  // Getters for each property
+  get name() {
+    return this.form.controls['name'];
+  }
+
+  get creator() {
+    return this.form.controls['creator'];
+  }
+
+  get website() {
+    return this.form.controls['website'];
+  }
+
+  get published() {
+    return this.form.controls['published'];
+  }
+
+  get reuseTypes(): FormArray {
+    return this.form.get('reuseTypes') as FormArray;
+  }
+
+  get datasetsUsed(): FormArray {
+    return this.form.get('datasetsUsed') as FormArray;
+  }
+
+  get formInvalid() {
+    return this.form.invalid;
+  }
+
+  get reuseTypesList() {
+    return ReuseTypes;
+  }
+
+  logoChanged(fileList: FileList) {
+    if (fileList && fileList.length > 0) {
+      this.logoFile = fileList[0];
+    }
+  }
+
+  removeLogo() {
+    this.form.get('logo').setValue(null);
+  }
+
+}
diff --git a/src/app/components/reuses/list/reuses.component.html b/src/app/components/reuses/list/reuses.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..98c61f33cb9010a27295a02527db69794d72df7a
--- /dev/null
+++ b/src/app/components/reuses/list/reuses.component.html
@@ -0,0 +1,112 @@
+<section class="section page-container">
+  <app-page-header [pageInfo]="pageHeaderInfo" [hideBackButton]="true"></app-page-header>
+  <div class="add-item-link has-text-right">
+    <a class="button button-gl" [routerLink]="['new']">
+      Ajouter
+    </a>
+  </div>
+  <div class="table entity-list-table" *ngIf="reuses">
+    <div class="header columns is-marginless">
+      <div class="column is-2">
+        <span (click)="sortBy('name')" class="is-sortable">
+          <span class="sort-icons">
+            <span class="icon">
+              <i class="fas fa-sort-up"
+                [ngClass]="{'icon-red': sortOptions.value === 'name' && sortOptions.order === 'desc'}"></i>
+            </span>
+            <span class="icon">
+              <i class="fas fa-sort-down"
+                [ngClass]="{'icon-red': sortOptions.value === 'name' && sortOptions.order === 'asc'}"></i>
+            </span>
+          </span>
+          <span class="column-title" [ngClass]="{'active': sortOptions.value === 'name'}">Nom</span>
+        </span>
+      </div>
+      <div class="column is-1 has-text-centered">
+        <span (click)="sortBy('published')" class="is-sortable">
+          <span class="sort-icons">
+            <span class="icon">
+              <i class="fas fa-sort-up"
+                [ngClass]="{'icon-red': sortOptions.value === 'published' && sortOptions.order === 'desc'}"></i>
+            </span>
+            <span class="icon">
+              <i class="fas fa-sort-down"
+                [ngClass]="{'icon-red': sortOptions.value === 'published' && sortOptions.order === 'asc'}"></i>
+            </span>
+          </span>
+          <span class="column-title" [ngClass]="{'active': sortOptions.value === published}">Publié</span>
+        </span>
+      </div>
+      <div class="column is-1 has-text-centered">
+        <span class="column-title">Logo</span>
+      </div>
+      <div class="column is-2">
+        <span (click)="sortBy('creator')" class="is-sortable">
+          <span class="sort-icons">
+            <span class="icon">
+              <i class="fas fa-sort-up"
+                [ngClass]="{'icon-red': sortOptions.value === 'creator' && sortOptions.order === 'desc'}"></i>
+            </span>
+            <span class="icon">
+              <i class="fas fa-sort-down"
+                [ngClass]="{'icon-red': sortOptions.value === 'creator' && sortOptions.order === 'asc'}"></i>
+            </span>
+          </span>
+          <span class="column-title" [ngClass]="{'active': sortOptions.value === creator}">Créateur</span>
+        </span>
+      </div>
+      <div class="column is-2">
+        <span class="column-title">Site web</span>
+      </div>
+      <div class="column is-2">
+        <span class="column-title">Type de réutilisation</span>
+      </div>
+      <div class="column is-offset-1 is-1 has-text-centered">
+        <span class="column-title">Actions</span>
+      </div>
+    </div>
+    <div class="data-list">
+      <div class="data columns is-multiline is-vcentered is-marginless"
+        *ngFor="let reuse of reuses; let i=index; let odd=odd; let even=even;" [ngClass]="{ odd: odd, even: even }">
+        <div class="column is-2">
+          <span>{{ reuse.name }}</span>
+        </div>
+        <div class="column is-1 has-text-centered">
+          <span class="icon has-text-success" *ngIf="reuse.published">
+            <i class="far fa-check-circle"></i>
+          </span>
+          <span class="icon has-text-danger" *ngIf="!reuse.published">
+            <i class="far fa-times-circle"></i>
+          </span>
+        </div>
+        <div class="column is-1 has-text-centered">
+          <img class="entity-logo-in-list" [src]="reuse.logo" alt="Logo de la réutilisation">
+        </div>
+        <div class="column is-2">
+          <span>{{ reuse.creator }}</span>
+        </div>
+        <div class="column is-2">
+          <span>{{ reuse.website }}</span>
+        </div>
+        <div class="column is-2">
+          <span>{{ formatReuseTypes(reuse.reuseTypes) }}</span>
+        </div>
+        <div class="column is-offset-1 is-1 has-text-centered actions">
+          <app-crud-buttons [id]="reuse._id" (delete)="displayDeletePopup($event)"></app-crud-buttons>
+        </div>
+      </div>
+    </div>
+    <div class="columns is-marginless paginator">
+      <div class="column">
+        <app-paginator *ngIf="paginator.length > 0" [length]="paginator.length" [pageSize]="paginator.limit"
+          [pageSizeOptions]="paginator.pageSizeOptions" [pageIndex]="paginator.pageIndex" [pagesToShow]="5"
+          [showFirstLastButtons]="true" (page)="changePagination($event)">
+        </app-paginator>
+      </div>
+    </div>
+  </div>
+</section>
+
+<app-confirmation-modal (cancel)="objectToBeDeletedId=null" (continue)="deleteChangelog()" [texts]="deleteModalTexts"
+  [isOpened]="objectToBeDeletedId !== null">
+</app-confirmation-modal>
\ No newline at end of file
diff --git a/src/app/components/reuses/list/reuses.component.scss b/src/app/components/reuses/list/reuses.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..a499549dab89fa427fa767977d8722ef1158eb67
--- /dev/null
+++ b/src/app/components/reuses/list/reuses.component.scss
@@ -0,0 +1,3 @@
+.has-text-centered .is-sortable {
+  justify-content: center;
+}
diff --git a/src/app/components/reuses/list/reuses.component.ts b/src/app/components/reuses/list/reuses.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..17f361c7d22cf538c6da21297336a6d62693c8ae
--- /dev/null
+++ b/src/app/components/reuses/list/reuses.component.ts
@@ -0,0 +1,147 @@
+import { Component, OnInit, OnDestroy } from '@angular/core';
+import { PaginatorOptions } from 'src/app/models/paginator-options.model';
+import { IPageHeaderInfo } from '../../../models/page.model';
+import { Subscription } from 'rxjs';
+import { ReuseService, NotificationService } from '../../../services';
+import { Reuse, ReuseRO, ReuseTypes } from '../../../models/reuse.model';
+
+@Component({
+  selector: 'app-reuses',
+  templateUrl: './reuses.component.html',
+  styleUrls: ['./reuses.component.scss'],
+})
+export class ReusesComponent implements OnInit, OnDestroy {
+
+  pageHeaderInfo: IPageHeaderInfo = {
+    title: '',
+  };
+  objectToBeDeletedId = null;
+  deleteModalTexts = {
+    main: 'Si vous poursuivez, la réutilisation sera définitivement supprimée.',
+    cancel: 'Annuler',
+    continue: 'Supprimer',
+  };
+  reuses: Reuse[] = [];
+  searchChangeSub: Subscription;
+
+  // Paginator options
+  paginator: PaginatorOptions;
+  pageSize = 10;
+  pageSizeOptions = [5, 10, 25, 100];
+
+  sortValue: string;
+
+  totalElement: number;
+  filters = {
+    name: '',
+  };
+  where = {};
+
+  constructor(
+    private _reuseService: ReuseService,
+    private _notificationService: NotificationService,
+  ) {
+    this.paginator = {
+      pageIndex: this._reuseService.pageNumber,
+      length: 0,
+      limit: this._reuseService.limit,
+      pageSizeOptions: [5, 10, 20],
+    };
+  }
+
+  ngOnInit(): void {
+    this._reuseService.sortOptions = {
+      value: 'name',
+      order: 'desc',
+    };
+    this.search();
+
+    this.searchChangeSub = this._reuseService.searchChange$.subscribe(
+      () => {
+        this.search();
+      },
+    );
+  }
+
+  private search() {
+    this._reuseService.getReuses()
+      .subscribe(
+        (items: ReuseRO) => {
+          this.reuses = items.reuses;
+          this.totalElement = items.totalCount;
+
+          this.pageHeaderInfo.title = this.totalElement > 1 ?
+            `${this.totalElement} réutilisations trouvées` :
+            `${this.totalElement} réutilisation trouvée`;
+
+          this.paginator.limit = this._reuseService.limit;
+          this.paginator.pageIndex = this._reuseService.pageNumber;
+          this.paginator.length = items.totalCount;
+        },
+        () => {
+          this.pageHeaderInfo.title = '0 réutilisation trouvée';
+          this._notificationService.notify({
+            type: 'error',
+            message: 'Une erreur est survenue lors du chargement des réutilisations.',
+          });
+        },
+      );
+  }
+
+  changePagination(pageIndex) {
+    this._reuseService.paginationChanged(this.paginator.limit, pageIndex);
+  }
+
+  changePageSize(pageSize) {
+    this._reuseService.paginationChanged(pageSize, 1);
+  }
+
+  sortBy(key: string) {
+    if (this._reuseService.sortOptions.value === key) {
+      this._reuseService.reverseSortOrder();
+    } else {
+      this._reuseService.sortOptions.value = key;
+      this._reuseService.sortOptions.order = 'asc';
+    }
+    this.search();
+  }
+
+  get sortOptions() {
+    return this._reuseService.sortOptions;
+  }
+
+  formatReuseTypes(types: string[]) {
+    return types.map(e => ReuseTypes[e]).join(', ');
+  }
+
+  displayDeletePopup(id) {
+    this.objectToBeDeletedId = id;
+  }
+
+  deleteChangelog() {
+    this._reuseService.delete(this.objectToBeDeletedId).subscribe(
+      () => {
+        this._notificationService.notify({
+          type: 'success',
+          message: 'La réutilisation a été supprimé avec succès.',
+        });
+        this._reuseService.pageNumber = 1;
+        this.search();
+      },
+      () => {
+        this._notificationService.notify({
+          type: 'error',
+          message: 'Une erreur est survenue lors de la suppression de la réutilisation.',
+        });
+      },
+      () => {
+        this.objectToBeDeletedId = null;
+      },
+    );
+
+  }
+
+  ngOnDestroy() {
+    this.searchChangeSub.unsubscribe();
+  }
+}
diff --git a/src/app/models/basic-tabs.model.ts b/src/app/models/basic-tabs.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..93caa24433e70b5e3a73a421f703dc654ef653c4
--- /dev/null
+++ b/src/app/models/basic-tabs.model.ts
@@ -0,0 +1,8 @@
+export interface IBasicTab {
+  name: string;
+  fullRouterLinkPath: string;
+}
+export interface ISimpleTab {
+  name: string;
+  isActive: boolean;
+}
diff --git a/src/app/models/changelog.model.ts b/src/app/models/changelog.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..081b9c7d6b4b58454e46490fe741ee7175591388
--- /dev/null
+++ b/src/app/models/changelog.model.ts
@@ -0,0 +1,49 @@
+export class ChangelogRO {
+  changelogs: Changelog[];
+  totalCount: number;
+
+  constructor(changelogs, totalCount) {
+    this.changelogs = changelogs;
+    this.totalCount = totalCount;
+  }
+}
+
+export class Changelog {
+  _id: string;
+  version: string;
+  createDate?: string; // bon type ?
+  updateDate?: string;
+  language: string;
+  majorImprovements: string[];
+  minorImprovements: string[];
+  bugFixes: string[];
+
+  constructor(changelog?: IChangelog) {
+    this._id = changelog && changelog._id ? changelog._id : null;
+    this.version = changelog && changelog.version ? changelog.version : null;
+
+    if (changelog && changelog.createDate) {
+      this.createDate = changelog.createDate;
+    }
+
+    if (changelog && changelog.updateDate) {
+      this.updateDate = changelog.updateDate;
+    }
+
+    this.language = changelog && changelog.language ? changelog.language : null;
+    this.majorImprovements = changelog && changelog.majorImprovements ? changelog.majorImprovements : [];
+    this.minorImprovements = changelog && changelog.minorImprovements ? changelog.minorImprovements : [];
+    this.bugFixes = changelog && changelog.bugFixes ? changelog.bugFixes : [];
+  }
+}
+
+export interface IChangelog {
+  _id: string;
+  version: string;
+  createDate: string; // bon type ?
+  updateDate: string;
+  language: string;
+  majorImprovements: string[];
+  minorImprovements: string[];
+  bugFixes: string[];
+}
diff --git a/src/app/models/credit.model.ts b/src/app/models/credit.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e6bb192f0bef644ef3263ed8dbd99eda10617b15
--- /dev/null
+++ b/src/app/models/credit.model.ts
@@ -0,0 +1,60 @@
+export class Credit {
+  id: string;
+  name: string;
+  description: string;
+  logo?: string;
+  links?: ILink[];
+  published: boolean;
+
+  constructor(organization?: ICredit) {
+    if (organization) {
+      if (organization.id) {
+        this.id = organization.id;
+      }
+      this.name = organization.name;
+      this.description = organization.description;
+      this.links = organization.links;
+      this.published = organization.published;
+
+      this.links.forEach((link) => {
+        if (!link.id) {
+          delete link.id;
+        }
+      });
+      this.logo = organization.logo;
+    } else {
+      this.name = null;
+      this.description = null;
+      this.logo = null;
+      this.links = [];
+      this.published = false;
+    }
+
+  }
+}
+
+export class CreditRO {
+  credits: Credit[];
+  totalCount: number;
+
+  constructor(credits, totalCount) {
+    this.credits = credits;
+    this.totalCount = totalCount;
+  }
+}
+
+export interface ICredit {
+  id: string;
+  name: string;
+  description: string;
+  logo: string;
+  links: ILink[];
+  published: boolean;
+}
+
+interface ILink {
+  id?: number;
+  name: string;
+  url: string;
+  creditId: number;
+}
diff --git a/src/app/models/logs.model.ts b/src/app/models/logs.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..58e59348431bf7083fa76564e5fdb0efe09dfca8
--- /dev/null
+++ b/src/app/models/logs.model.ts
@@ -0,0 +1,8 @@
+export interface ILogs {
+  step: string;
+  uuid: string;
+  sessionId: string;
+  totalHoursSpent: number;
+  totalMinutesSpent: number;
+  totalSecondsSpent: number;
+}
diff --git a/src/app/models/media.model.ts b/src/app/models/media.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3c6da00bdeeb9ac1558777669eb0636ddb9bd5bc
--- /dev/null
+++ b/src/app/models/media.model.ts
@@ -0,0 +1,65 @@
+export class Media {
+  _id: string;
+  alt: string;
+  caption: string;
+  url: string;
+  creationDate?: string;
+  updateDate?: string;
+
+  constructor(media?: IMedia) {
+    if (media) {
+      this.url = media.url;
+      if (media._id) {
+        this._id = media._id;
+      }
+
+      this.alt = media.alt ? media.alt : '';
+      this.caption = media.caption ? media.caption : '';
+
+      if (media.creationDate) {
+        this.creationDate = media.creationDate;
+      }
+      if (media.updateDate) {
+        this.updateDate = media.updateDate;
+      }
+    } else {
+      this.alt = '';
+      this.caption = '';
+      this.url = null;
+      this.creationDate = null;
+      this.updateDate = null;
+    }
+
+  }
+}
+
+export class MediaDTO {
+  _id: string;
+  alt: string;
+  caption: string;
+
+  constructor(media) {
+    this._id = media._id;
+    this.alt = media.alt ? media.alt : '';
+    this.caption = media.caption ? media.caption : '';
+  }
+}
+
+export class MediaRO {
+  media: Media[];
+  mediaCount: number;
+
+  constructor(media, mediaCount) {
+    this.media = media;
+    this.mediaCount = mediaCount;
+  }
+}
+
+export interface IMedia {
+  _id: string;
+  alt: string;
+  caption: string;
+  url: string;
+  creationDate: string;
+  updateDate: string;
+}
diff --git a/src/app/models/organization.model.ts b/src/app/models/organization.model.ts
index 54074cb77548abbd77d3452fe686f213512f5f73..88e89c6de4ca9512351f7adfff52b37144d74505 100644
--- a/src/app/models/organization.model.ts
+++ b/src/app/models/organization.model.ts
@@ -1,10 +1,11 @@
 export class Organization {
-  id: number;
+  id: string;
   name: string;
   description: string;
   elasticSearchName?: string;
   logo?: string;
   links?: ILink[];
+  published: boolean;
 
   constructor(organization?: IOrganization) {
     if (organization) {
@@ -15,6 +16,7 @@ export class Organization {
       this.description = organization.description;
       this.elasticSearchName = organization.elasticSearchName;
       this.links = organization.links;
+      this.published = organization.published;
 
       this.links.forEach((link) => {
         if (!link.id) {
@@ -28,6 +30,7 @@ export class Organization {
       this.logo = null;
       this.elasticSearchName = null;
       this.links = [];
+      this.published = false;
     }
 
   }
@@ -44,12 +47,13 @@ export class OrganizationRO {
 }
 
 export interface IOrganization {
-  id: number;
+  id: string;
   name: string;
   description: string;
   elasticSearchName: string;
   logo: string;
   links: ILink[];
+  published: boolean;
 }
 
 interface ILink {
diff --git a/src/app/models/projection.model.ts b/src/app/models/projection.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3be7e304342e762913d47595b04b8bfd81485b49
--- /dev/null
+++ b/src/app/models/projection.model.ts
@@ -0,0 +1,38 @@
+export class Projection {
+  id?: number;
+  name: string;
+  commonName: string;
+  description: string;
+
+  constructor(projection?: IProjection) {
+    if (projection) {
+      if (projection.id) {
+        this.id = projection.id;
+      }
+      this.name = projection.name;
+      this.commonName = projection.commonName;
+      this.description = projection.description;
+    } else {
+      this.name = '';
+      this.commonName = '';
+      this.description = '';
+    }
+  }
+}
+
+export class ProjectionRO {
+  projections: Projection[];
+  totalCount: number;
+
+  constructor(projections, totalCount) {
+    this.projections = projections;
+    this.totalCount = totalCount;
+  }
+}
+
+export interface IProjection {
+  id: number;
+  name: string;
+  commonName: string;
+  description: string;
+}
diff --git a/src/app/models/resource.model.ts b/src/app/models/resource.model.ts
index 2d28cdda3fc135178cc0515efd361079a1f286ed..bbbf4ebd692b73742c059f4571b810b94cfc7260 100644
--- a/src/app/models/resource.model.ts
+++ b/src/app/models/resource.model.ts
@@ -10,7 +10,8 @@ export class Resource {
   isDownloadable: number;
   isStandard: number;
   parametersUrl: string;
-  messageWarning: string;
+  messageWarningFr: string;
+  messageWarningEn: string;
   resourceFormats: IResourceFormat[];
 
   constructor(resource?: IResource) {
@@ -27,7 +28,8 @@ export class Resource {
       this.isDownloadable = resource.isDownloadable;
       this.isStandard = resource.isStandard;
       this.parametersUrl = resource.parametersUrl;
-      this.messageWarning = resource.messageWarning;
+      this.messageWarningFr = resource.messageWarningFr;
+      this.messageWarningEn = resource.messageWarningEn;
       this.resourceFormats = resource.resourceFormats ? resource.resourceFormats : [];
     } else {
       this.name = '';
@@ -38,7 +40,8 @@ export class Resource {
       this.isDownloadable = 0;
       this.isStandard = 0;
       this.parametersUrl = '';
-      this.messageWarning = '';
+      this.messageWarningFr = '';
+      this.messageWarningEn = '';
       this.resourceFormats = [];
     }
   }
@@ -64,7 +67,8 @@ export interface IResource {
   isDownloadable: number;
   isStandard: number;
   parametersUrl: string;
-  messageWarning: string;
+  messageWarningFr: string;
+  messageWarningEn: string;
   resourceFormats: IResourceFormat[];
 }
 
diff --git a/src/app/models/reuse.model.ts b/src/app/models/reuse.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..394cab32519a7623c0f726d75f8079aa6f2be50d
--- /dev/null
+++ b/src/app/models/reuse.model.ts
@@ -0,0 +1,60 @@
+export class ReuseRO {
+  reuses: Reuse[];
+  totalCount: number;
+
+  constructor(reuses, totalCount) {
+    this.reuses = reuses;
+    this.totalCount = totalCount;
+  }
+}
+
+export class Reuse {
+  _id: string;
+  name: string;
+  creator: string;
+  logo: string;
+  website: string;
+  reuseTypes: string[];
+  datasetsUsed: string[];
+  createDate: Date;
+  updateDate: Date;
+  published: boolean;
+
+  constructor(reuse?: IReuse) {
+    this._id = reuse && reuse._id ? reuse._id : null;
+    this.name = reuse && reuse.name ? reuse.name : null;
+    this.creator = reuse && reuse.creator ? reuse.creator : null;
+    this.logo = reuse && reuse.logo ? reuse.logo : null;
+    this.website = reuse && reuse.website ? reuse.website : null;
+    this.reuseTypes = reuse && reuse.reuseTypes ? reuse.reuseTypes : [];
+    this.datasetsUsed = reuse && reuse.datasetsUsed ? reuse.datasetsUsed : [];
+    if (reuse && reuse.createDate) {
+      this.createDate = reuse.createDate;
+    }
+
+    if (reuse && reuse.updateDate) {
+      this.updateDate = reuse.updateDate;
+    }
+    this.published = reuse && reuse.published ? reuse.published : false;
+  }
+}
+
+export interface IReuse {
+  _id: string;
+  name: string;
+  creator: string;
+  logo: string;
+  website: string;
+  reuseTypes: string[];
+  datasetsUsed: string[];
+  createDate: Date;
+  updateDate: Date;
+  published: boolean;
+}
+
+// tslint:disable-next-line: variable-name
+export const ReuseTypes = {
+  app: 'Application mobile',
+  web: 'Site web',
+  article: 'Article',
+};
diff --git a/src/app/services/app-config.service.ts b/src/app/services/app-config.service.ts
index c8fb34321e489120210a882aa6c17b576b2fe8ae..6159c08faea8c65b8a57e16c47d649300fd162d6 100644
--- a/src/app/services/app-config.service.ts
+++ b/src/app/services/app-config.service.ts
@@ -16,6 +16,18 @@ export class AppConfig {
   middlewareLegacyAuth: {
     url: string;
   };
+  restHeartAggregations:{
+    url: string;
+  };
+  changelog: {
+    url: string;
+  };
+  credits: {
+    url: string;
+  };
+  reuses: {
+    url: string;
+  };
 }
 
 export let APP_CONFIG: AppConfig;
diff --git a/src/app/services/changelog.service.ts b/src/app/services/changelog.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3728700299dcb7dee4db1449210342206a8936e2
--- /dev/null
+++ b/src/app/services/changelog.service.ts
@@ -0,0 +1,92 @@
+import { Injectable } from '@angular/core';
+import { Observable, Subject } from 'rxjs';
+import { map } from 'rxjs/operators';
+import { HttpClient } from '@angular/common/http';
+import { APP_CONFIG } from './app-config.service';
+import { ChangelogRO, IChangelog, Changelog } from '../models/changelog.model';
+
+@Injectable()
+export class ChangelogService {
+
+  changelogServiceUrl: string;
+  limit: number;
+  pageNumber: number;
+  sortOptions: {
+    value: string,
+    order: string,
+  };
+
+  private _searchChangeSubject: Subject<any>;
+
+  constructor(
+    private _httpClient: HttpClient) {
+    this.changelogServiceUrl = `${APP_CONFIG.changelog.url}changelog/`;
+    this._searchChangeSubject = new Subject<any>();
+    this.limit = 10;
+    this.pageNumber = 1;
+  }
+
+  getChangelogs(): Observable<ChangelogRO> {
+    let query = '?';
+    query += `limit=${(this.limit ? this.limit : 20)}`;
+    query += `&offset=${(this.pageNumber ? (this.pageNumber - 1) * this.limit : 0)}`;
+    query += `&sort_by=${this.sortOptions.value}.${this.sortOptions.order}`;
+
+    return this._httpClient.get<IChangelog[]>(this.changelogServiceUrl + query, { observe: 'response' }).pipe(
+      map((response) => {
+        const totalCount = response.headers.get('Content-Range');
+        const changelogs = [];
+        response.body.forEach((changelog) => {
+          changelogs.push(new Changelog(changelog));
+        });
+        return new ChangelogRO(changelogs, parseInt(totalCount, 10));
+      }));
+  }
+
+  findById(id): Observable<Changelog> {
+    return this._httpClient.get<IChangelog>(this.changelogServiceUrl + id).pipe(
+      map((response) => {
+        return new Changelog(response);
+      }),
+    );
+  }
+
+  delete(id) {
+    return this._httpClient.delete(this.changelogServiceUrl + id, { withCredentials: true });
+  }
+
+  create(data) {
+    return this._httpClient.post<IChangelog>(this.changelogServiceUrl, data, { withCredentials: true }).pipe(
+      map((response) => {
+        return new Changelog(response);
+      }),
+    );
+  }
+
+  update(data) {
+    return this._httpClient.put<IChangelog>(this.changelogServiceUrl + data._id, data, { withCredentials: true }).pipe(
+      map((response) => {
+        return new Changelog(response);
+      }),
+    );
+  }
+
+  /* PAGINATION */
+  paginationChanged(limit: number, pageNumber: number) {
+    this.limit = limit;
+    this.pageNumber = pageNumber;
+    this._searchChangeSubject.next();
+  }
+
+  reverseSortOrder(): void {
+    if (this.sortOptions.order === 'asc') {
+      this.sortOptions.order = 'desc';
+    } else {
+      this.sortOptions.order = 'asc';
+    }
+  }
+
+  get searchChange$(): Observable<string> {
+    return this._searchChangeSubject.asObservable();
+  }
+}
diff --git a/src/app/services/credit.service.ts b/src/app/services/credit.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f80797180b2430ceb13fc72aa29202b5c42d4370
--- /dev/null
+++ b/src/app/services/credit.service.ts
@@ -0,0 +1,91 @@
+import { Injectable } from '@angular/core';
+import { Subject, Observable } from 'rxjs';
+import { HttpClient } from '@angular/common/http';
+import { CreditRO, ICredit, Credit } from '../models/credit.model';
+import { APP_CONFIG } from './app-config.service';
+import { map } from 'rxjs/operators';
+
+@Injectable()
+export class CreditService {
+
+  limit: number;
+  pageNumber: number;
+  sortOptions: {
+    value: string,
+    order: string,
+  };
+
+  private _searchChangeSubject: Subject<any>;
+
+  constructor(
+    private _httpClient: HttpClient) {
+    this._searchChangeSubject = new Subject<any>();
+    this.limit = 10;
+    this.pageNumber = 1;
+  }
+
+  getCredits(options?): Observable<CreditRO> {
+    let query = '?';
+    query += `limit=${(this.limit ? this.limit : 20)}`;
+    query += `&offset=${(this.pageNumber ? (this.pageNumber - 1) * this.limit : 0)}`;
+    query += `&sort_by=${this.sortOptions.value}.${this.sortOptions.order}`;
+
+    return this._httpClient.get<ICredit[]>(APP_CONFIG.credits.url + query, { withCredentials: true, observe: 'response' }).pipe(
+      map((response) => {
+        const totalCount = response.headers.get('Content-Range');
+        const credits = [];
+        response.body.forEach((credit) => {
+          credits.push(new Credit(credit));
+        });
+        return new CreditRO(credits, parseInt(totalCount, 10));
+      }));
+  }
+
+  findById(id): Observable<Credit> {
+    return this._httpClient.get<ICredit>(APP_CONFIG.credits.url + id, { withCredentials: true }).pipe(
+      map((response) => {
+        return new Credit(response);
+      }),
+    );
+  }
+
+  delete(id) {
+    return this._httpClient.delete(APP_CONFIG.credits.url + id, { withCredentials: true });
+  }
+
+  create(data: Credit): Observable<Credit> {
+    return this._httpClient.post<ICredit>(APP_CONFIG.credits.url, data, { withCredentials: true }).pipe(
+      map((response) => {
+        return new Credit(response);
+      }),
+    );
+  }
+
+  update(data: Credit): Observable<Credit> {
+    return this._httpClient.put<ICredit>(APP_CONFIG.credits.url + data.id, data, { withCredentials: true }).pipe(
+      map((response) => {
+        return new Credit(response);
+      }),
+    );
+  }
+
+  /* PAGINATION */
+  paginationChanged(limit: number, pageNumber: number) {
+    this.limit = limit;
+    this.pageNumber = pageNumber;
+    this._searchChangeSubject.next();
+  }
+
+  reverseSortOrder(): void {
+    if (this.sortOptions.order === 'asc') {
+      this.sortOptions.order = 'desc';
+    } else {
+      this.sortOptions.order = 'asc';
+    }
+  }
+
+  get searchChange$(): Observable<string> {
+    return this._searchChangeSubject.asObservable();
+  }
+
+}
diff --git a/src/app/services/data-logs.service.ts b/src/app/services/data-logs.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..eebf9ea17fedcdc631765f8cccd62659fb6f7040
--- /dev/null
+++ b/src/app/services/data-logs.service.ts
@@ -0,0 +1,277 @@
+import { Injectable } from '@angular/core';
+import { Observable, Subject } from 'rxjs';
+import { map } from 'rxjs/operators';
+import { HttpClient } from '@angular/common/http';
+import { APP_CONFIG } from './app-config.service';
+
+@Injectable()
+export class DataLogsService {
+
+  resourceServiceUrl: string;
+  limit: number;
+  pageNumber: number;
+  length: number;
+  sortOptions: {
+    value: string,
+    order: string,
+  };
+
+  private _infoLogSubject: Subject<any>;
+  private _errorLogSubject: Subject<any>;
+  private _sessionLogSubject: Subject<any>;
+  private _slugLogSubject: Subject<any>;
+
+  constructor(
+    private _httpClient: HttpClient) {
+    this.resourceServiceUrl = `${APP_CONFIG.restHeartAggregations.url}`;
+    this._infoLogSubject = new Subject<any>();
+    this._errorLogSubject = new Subject<any>();
+    this._sessionLogSubject = new Subject<any>();
+    this._slugLogSubject = new Subject<any>();
+    this.limit = 10; // number of items per pages
+    this.pageNumber = 1;
+    this.length = 10; // number total of items in all pages
+  }
+
+  uuidToSessionId(uuid): Observable<any[]> {
+    const query = { uuid };
+    const aggregationEndPoint = 'indexer_logs/_aggrs/uuidToSessionId?avars=';
+    const stringQuery = JSON.stringify(query);
+    return this._httpClient.get<any[]>(this.resourceServiceUrl + aggregationEndPoint + stringQuery).pipe(
+      map((response) => {
+        return response;
+      }),
+    );
+  }
+
+  getAllStepsDuration(uuid, sessionId): Observable<any[]> {
+    const query = { uuid, session_id: sessionId };
+    const aggregationEndPoint = 'indexer_logs/_aggrs/stepTimeAggr?avars=';
+    const stringQuery = JSON.stringify(query);
+    return this._httpClient.get<any[]>(this.resourceServiceUrl + aggregationEndPoint + stringQuery).pipe(
+      map((response) => {
+        return response;
+      }),
+    );
+  }
+  getAllUniqueFields(): Observable<any[]> {
+    const aggregationEndPoint = 'indexer_logs/_aggrs/getAllUniqueFields';
+    return this._httpClient.get<any[]>(this.resourceServiceUrl + aggregationEndPoint).pipe(
+      map((response) => {
+        return response;
+      }),
+    );
+  }
+  getAllInfoForAllSlugs(sortFilter1, order1, sortFilter2, order2, nbSlugs, pageNumber): Observable<any[]> {
+    let aggregationEndPoint = 'indexer_logs/_aggrs/allFullSlugAggr?avars=';
+    // this.length = nbSlugs;
+    this.pageNumber = pageNumber;
+    const query = { sortExpr1: { [sortFilter1] : order1 }, sortExpr2 :{ [sortFilter2] : order2 } };
+    const stringQuery = JSON.stringify(query);
+    aggregationEndPoint += stringQuery;
+    aggregationEndPoint += `&pagesize=${(this.limit)}`;
+    aggregationEndPoint += `&page=${(pageNumber)}`;
+    return this._httpClient.get<any[]>(this.resourceServiceUrl + aggregationEndPoint).pipe(
+      map((response) => {
+        return response;
+      }),
+    );
+  }
+
+  getAllInfoForAllSessions(sortFilter1, order1, sortFilter2, order2, nbSessions, pageNumber): Observable<any[]> {
+    let aggregationEndPoint = 'indexer_logs/_aggrs/allFullSessionAggr?avars=';
+    const query = { sortExpr1: { [sortFilter1] : order1 }, sortExpr2 :{ [sortFilter2] : order2 } };
+    const stringQuery = JSON.stringify(query);
+    // this.length = nbSessions;
+    this.pageNumber = pageNumber;
+    aggregationEndPoint += stringQuery;
+    aggregationEndPoint += `&pagesize=${(this.limit)}`;
+    aggregationEndPoint += `&page=${(pageNumber)}`;
+    return this._httpClient.get<any[]>(this.resourceServiceUrl + aggregationEndPoint).pipe(
+      map((response) => {
+        if (!response) { return [{ 'server is down': true }]; }
+        return response;
+      }),
+    );
+  }
+  getAllInfoForOneSlug(slug): Observable<any[]> {
+    const query = { slug, sortExpr1: { _id : 1 }, sortExpr2 :{ _id : 1 } };
+    const stringQuery = JSON.stringify(query);
+    const aggregationEndPoint = 'indexer_logs/_aggrs/oneFullSlugAggr?avars=';
+    return this._httpClient.get<any[]>(this.resourceServiceUrl + aggregationEndPoint + stringQuery).pipe(
+      map((response) => {
+        return response;
+      }),
+    );
+  }
+
+  getAllInfoForOneSession(sessionId): Observable<any[]> {
+    const query = { session_id: sessionId, sortExpr1: { _id : 1 }, sortExpr2 :{ _id : 1 } };
+    const stringQuery = JSON.stringify(query);
+    const aggregationEndPoint = 'indexer_logs/_aggrs/oneFullSessionAggr?avars=';
+    return this._httpClient.get<any[]>(this.resourceServiceUrl + aggregationEndPoint + stringQuery).pipe(
+      map((response) => {
+        return response;
+      }),
+    );
+  }
+
+  getAllInfoForSlugSession(slug, sessionId): Observable<any[]> {
+    const query = { slug, session_id: sessionId , sortExpr1: { _id : 1 }, sortExpr2 :{ _id : 1 } };
+    const stringQuery = JSON.stringify(query);
+    const aggregationEndPoint = 'indexer_logs/_aggrs/oneFullSessionSlugAggr?avars=';
+    return this._httpClient.get<any[]>(this.resourceServiceUrl + aggregationEndPoint + stringQuery).pipe(
+      map((response) => {
+        return response;
+      }),
+    );
+  }
+
+  getData(thisUuid, sessionId): Observable<any[]> {
+    const query = { uuid: thisUuid, session_id: sessionId };
+    const stringQuery = JSON.stringify(query);
+    const aggregationEndPoint = 'indexer_logs/_aggrs/reportDataAggr?avars=';
+    return this._httpClient.get<any[]>(this.resourceServiceUrl + aggregationEndPoint + stringQuery).pipe(
+      map((response) => {
+        return response;
+      }),
+    );
+  }
+  getProcessTimeAggr(sessionId): Observable<any[]> {
+    const query = { session_id: sessionId };
+    const stringQuery = JSON.stringify(query);
+    const aggregationEndPoint = 'indexer_logs/_aggrs/processTimeAggr?avars=';
+    return this._httpClient.get<any[]>(this.resourceServiceUrl + aggregationEndPoint + stringQuery).pipe(
+      map((response) => {
+        return response;
+      }),
+    );
+  }
+
+  getSlugFromSessionId(sessionId): Observable<any[]> {
+    const query = { session_id: sessionId };
+    const stringQuery = JSON.stringify(query);
+    const aggregationEndPoint = 'indexer_logs/_aggrs/sessionIdToSlug?avars=';
+    return this._httpClient.get<any[]>(this.resourceServiceUrl + aggregationEndPoint + stringQuery).pipe(
+      map((response) => {
+        return response;
+      }),
+    );
+  }
+
+  getUuidFromSlug(slug): Observable<any[]> {
+    const query = { slug };
+    const stringQuery = JSON.stringify(query);
+    const aggregationEndPoint = 'indexer_logs/_aggrs/slugToUuid?avars=';
+    return this._httpClient.get<any[]>(this.resourceServiceUrl + aggregationEndPoint + stringQuery).pipe(
+      map((response) => {
+        return response;
+      }),
+    );
+  }
+
+  getSlugFromUuid(uuid): Observable<any[]> {
+    const query = { uuid };
+    const stringQuery = JSON.stringify(query);
+    const aggregationEndPoint = 'indexer_logs/_aggrs/uuidToSlug?avars=';
+    return this._httpClient.get<any[]>(this.resourceServiceUrl + aggregationEndPoint + stringQuery).pipe(
+      map((response) => {
+        return response[0]['slug_list'][0];
+      }),
+    );
+  }
+  getSessionIdFromSlug(slug): Observable<any[]> {
+    const query = { slug };
+    const stringQuery = JSON.stringify(query);
+    const aggregationEndPoint = 'indexer_logs/_aggrs/slugToSessionId?avars=';
+    return this._httpClient.get<any[]>(this.resourceServiceUrl + aggregationEndPoint + stringQuery).pipe(
+      map((response) => {
+        return response;
+      }),
+    );
+  }
+
+  getLogsFromSessionIdAndSlug(sessionId, slug, loglevel): Observable<any[]> {
+    const query = { slug, loglevel, session_id : sessionId };
+    const stringQuery = JSON.stringify(query);
+    const aggregationEndPoint = 'indexer_logs?filter=';
+    return this._httpClient.get<any[]>(this.resourceServiceUrl + aggregationEndPoint + stringQuery).pipe(
+      map((response) => {
+        return response;
+      }),
+    );
+  }
+
+  getLogsStepsLogs(step, sessionId, uuid, loglevel, nbLogs, pageNumber): Observable<any[]> {
+    const query = { step, uuid, loglevel, session_id : sessionId };
+    let stringQuery = JSON.stringify(query);
+    this.length = nbLogs;
+    this.pageNumber = pageNumber;
+    stringQuery += `&pagesize=${(this.limit)}`;
+    stringQuery += `&page=${(pageNumber)}`;
+    const aggregationEndPoint = 'indexer_logs?filter=';
+    return this._httpClient.get<any>(this.resourceServiceUrl + aggregationEndPoint + stringQuery).pipe(
+      map((response) => {
+        return response;
+      }),
+    );
+  }
+
+  getComplementaryFromSlugOrSession(type, typeInfo): Observable<any[]> {
+    if (type === 'slug') {
+      const query = { slug: typeInfo };
+      const stringQuery = JSON.stringify(query);
+      const aggregationEndPoint = 'indexer_logs/_aggrs/slugToSessionId?avars=';
+      return this._httpClient.get<any[]>(this.resourceServiceUrl + aggregationEndPoint + stringQuery).pipe(
+        map((response) => {
+          return response;
+        }),
+      );
+
+    }
+    const query = { session_id: typeInfo };
+    const stringQuery = JSON.stringify(query);
+    const aggregationEndPoint = 'indexer_logs/_aggrs/sessionIdToSlug?avars=';
+    return this._httpClient.get<any[]>(this.resourceServiceUrl + aggregationEndPoint + stringQuery).pipe(
+      map((response) => {
+        return response;
+      }),
+    );
+  }
+
+    /* PAGINATION */
+  paginationChanged(limit: number, pageNumber: number, logType:string) {
+    this.limit = limit;
+    this.pageNumber = pageNumber;
+    if (logType === 'INFO') {
+      this._infoLogSubject.next();
+    }if (logType === 'ERROR') {
+      this._errorLogSubject.next();
+    }if (logType === 'SLUG') {
+      this._slugLogSubject.next();
+    }if (logType === 'SESSION') {
+      this._sessionLogSubject.next();
+    }
+  }
+
+  reverseSortOrder(): void {
+    if (this.sortOptions.order === 'asc') {
+      this.sortOptions.order = 'desc';
+    } else {
+      this.sortOptions.order = 'asc';
+    }
+  }
+  get infoLogChange$(): Observable<string> {
+    return this._infoLogSubject.asObservable();
+  }
+  get errorLogChange$(): Observable<string> {
+    return this._errorLogSubject.asObservable();
+  }
+  get sessionLogChange$(): Observable<string> {
+    return this._sessionLogSubject.asObservable();
+  }
+  get slugLogChange$(): Observable<string> {
+    return this._slugLogSubject.asObservable();
+  }
+
+}
diff --git a/src/app/services/format.service.ts b/src/app/services/format.service.ts
index bd2b12a3a6c1c5b27570b675282159bf9922bfa4..e7f321b760c11341a7bc3de360de70f27f15a3fe 100644
--- a/src/app/services/format.service.ts
+++ b/src/app/services/format.service.ts
@@ -61,14 +61,7 @@ export class FormatService {
     return this._httpClient.delete(this.formatServiceUrl + id, { withCredentials: true });
   }
 
-  replaceOrCreate(data): Observable<Format> {
-    if (data.id) {
-      return this._httpClient.put<IFormat>(this.formatServiceUrl + data.id, data, { withCredentials: true }).pipe(
-        map((response) => {
-          return new Format(response);
-        }),
-      );
-    }
+  create(data): Observable<Format> {
     return this._httpClient.post<IFormat>(this.formatServiceUrl, data, { withCredentials: true }).pipe(
       map((response) => {
         return new Format(response);
@@ -76,6 +69,14 @@ export class FormatService {
     );
   }
 
+  update(data): Observable<Format> {
+    return this._httpClient.put<IFormat>(this.formatServiceUrl + data.id, data, { withCredentials: true }).pipe(
+      map((response) => {
+        return new Format(response);
+      }),
+    );
+  }
+
   /* PAGINATION */
   paginationChanged(limit: number, pageNumber: number) {
     this.limit = limit;
diff --git a/src/app/services/index.ts b/src/app/services/index.ts
index 22c1f82163fa750ff151a10e41d7464774c781ee..590e44ae6c280bef6e8f522caf215880f163e960 100644
--- a/src/app/services/index.ts
+++ b/src/app/services/index.ts
@@ -3,7 +3,13 @@ import { OrganizationService } from './organization.service';
 import { ResourceService } from './resource.service';
 import { FormatService } from './format.service';
 import { NotificationService } from './notification.service';
+import { DataLogsService } from './data-logs.service';
 import { NavigationHistoryService } from './navigation-history.service';
+import { ChangelogService } from './changelog.service';
+import { CreditService } from './credit.service';
+import { MediaService } from './media.service';
+import { ReuseService } from './reuse.service';
+import { ProjectionService } from './projection.service';
 
 export {
   AppConfigService,
@@ -11,7 +17,13 @@ export {
   ResourceService,
   FormatService,
   NotificationService,
+  DataLogsService,
   NavigationHistoryService,
+  ChangelogService,
+  CreditService,
+  MediaService,
+  ReuseService,
+  ProjectionService,
 };
 
 // tslint:disable-next-line:variable-name
@@ -21,5 +33,11 @@ export const AppServices = [
   ResourceService,
   FormatService,
   NotificationService,
+  DataLogsService,
   NavigationHistoryService,
+  ChangelogService,
+  CreditService,
+  MediaService,
+  ReuseService,
+  ProjectionService,
 ];
diff --git a/src/app/services/media.service.ts b/src/app/services/media.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..25e49dcdb5aaca75258ac908a2b3246e6ade01fa
--- /dev/null
+++ b/src/app/services/media.service.ts
@@ -0,0 +1,104 @@
+import { Injectable } from '@angular/core';
+import { Observable, Subject } from 'rxjs';
+import { HttpClient } from '@angular/common/http';
+import { APP_CONFIG } from './app-config.service';
+import { map } from 'rxjs/operators';
+import { MediaRO, IMedia, Media, MediaDTO } from '../models/media.model';
+
+@Injectable()
+export class MediaService {
+
+  limit: number;
+  pageNumber: number;
+  sortOptions: {
+    value: string,
+    order: string,
+  };
+
+  private _searchChangeSubject: Subject<any>;
+
+  constructor(
+    private _httpClient: HttpClient) {
+    this._searchChangeSubject = new Subject<any>();
+    this.limit = 10;
+    this.pageNumber = 1;
+  }
+
+  getMedia(options?): Observable<MediaRO> {
+    let query = '?';
+    query += `limit=${(this.limit ? this.limit : 20)}`;
+    query += `&offset=${(this.pageNumber ? (this.pageNumber - 1) * this.limit : 0)}`;
+    query += `&sort_by=${this.sortOptions.value}.${this.sortOptions.order}`;
+
+    return this._httpClient.get<IMedia[]>(
+      `${APP_CONFIG.mediaLibrary.url}media${query}`,
+      { withCredentials: true, observe: 'response' }).pipe(
+        map((response) => {
+          const totalCount = response.headers.get('Content-Range');
+          const media = [];
+          response.body.forEach((item) => {
+            media.push(new Media(item));
+          });
+          return new MediaRO(media, parseInt(totalCount, 10));
+        }),
+      );
+  }
+
+  findById(id): Observable<Media> {
+    return this._httpClient.get<IMedia>(`${APP_CONFIG.mediaLibrary.url}media/${id}`).pipe(
+      map((response) => {
+        return new Media(response);
+      }),
+    );
+  }
+
+  uploadFile(file, data?: MediaDTO): Observable<Media> {
+    const formData = new FormData();
+    formData.append('file', file);
+
+    if (data) {
+      for (const prop in data) {
+        if (data.hasOwnProperty(prop)) {
+          formData.append(prop, data[prop]);
+        }
+      }
+    }
+
+    return this._httpClient.post<IMedia>(`${APP_CONFIG.mediaLibrary.url}media`, formData, { withCredentials: true }).pipe(
+      map((response) => {
+        return new Media(response);
+      }),
+    );
+  }
+
+  update(data: MediaDTO): Observable<Media> {
+    return this._httpClient.put<IMedia>(`${APP_CONFIG.mediaLibrary.url}media/${data._id}`, data, { withCredentials: true }).pipe(
+      map((response) => {
+        return new Media(response);
+      }),
+    );
+  }
+
+  delete(id) {
+    return this._httpClient.delete(`${APP_CONFIG.mediaLibrary.url}media/${id}`, { withCredentials: true });
+  }
+
+  /* PAGINATION */
+  paginationChanged(limit: number, pageNumber: number) {
+    this.limit = limit;
+    this.pageNumber = pageNumber;
+    this._searchChangeSubject.next();
+  }
+
+  reverseSortOrder(): void {
+    if (this.sortOptions.order === 'asc') {
+      this.sortOptions.order = 'desc';
+    } else {
+      this.sortOptions.order = 'asc';
+    }
+  }
+
+  get searchChange$(): Observable<string> {
+    return this._searchChangeSubject.asObservable();
+  }
+}
diff --git a/src/app/services/navigation-history.service.ts b/src/app/services/navigation-history.service.ts
index 608c4a52fc5ea94113eb2967ddc079e1b4524f5d..b749461f07589ff1bcdf8f1f4d5b8ea334d8dc22 100644
--- a/src/app/services/navigation-history.service.ts
+++ b/src/app/services/navigation-history.service.ts
@@ -20,8 +20,8 @@ export class NavigationHistoryService {
     const position = this.history.length - 1 - index;
     let res = null;
     if (position >= 0) {
-      res = this.history[position];
+      res = this.history.splice(position, this.history.length - position);
     }
-    return res;
+    return res ? res[0] : null;
   }
 }
diff --git a/src/app/services/organization.service.ts b/src/app/services/organization.service.ts
index 689c7407ef08430af970d1ed7eb7fabde66bab85..6e1f8b83a821dc7641e687e0a9c3de67d7bcb536 100644
--- a/src/app/services/organization.service.ts
+++ b/src/app/services/organization.service.ts
@@ -1,6 +1,6 @@
 import { Injectable } from '@angular/core';
 import { Observable, Subject } from 'rxjs';
-import { map, mergeMap } from 'rxjs/operators';
+import { map } from 'rxjs/operators';
 import { HttpClient } from '@angular/common/http';
 import { Organization, IOrganization, OrganizationRO } from '../models/organization.model';
 import { APP_CONFIG } from './app-config.service';
@@ -30,7 +30,7 @@ export class OrganizationService {
     query += `&offset=${(this.pageNumber ? (this.pageNumber - 1) * this.limit : 0)}`;
     query += `&sort_by=${this.sortOptions.value}.${this.sortOptions.order}`;
 
-    return this._httpClient.get<IOrganization[]>(APP_CONFIG.organizations.url + query, { observe: 'response' }).pipe(
+    return this._httpClient.get<IOrganization[]>(APP_CONFIG.organizations.url + query, { withCredentials: true, observe: 'response' }).pipe(
       map((response) => {
         const totalCount = response.headers.get('Content-Range');
         const organizations = [];
@@ -42,7 +42,7 @@ export class OrganizationService {
   }
 
   findById(id): Observable<Organization> {
-    return this._httpClient.get<IOrganization>(APP_CONFIG.organizations.url + id).pipe(
+    return this._httpClient.get<IOrganization>(APP_CONFIG.organizations.url + id, { withCredentials: true }).pipe(
       map((response) => {
         return new Organization(response);
       }),
@@ -53,36 +53,16 @@ export class OrganizationService {
     return this._httpClient.delete(APP_CONFIG.organizations.url + id, { withCredentials: true });
   }
 
-  uploadLogoAndSaveOrganization(logoFile, organization) {
-    return this.uploadLogo(logoFile).pipe(
-      mergeMap((response: any) => {
-        organization.logo = response.mediaUrl;
-        return this.replaceOrCreate(organization);
-      }),
-    )
-  }
-
-  uploadLogo(logoFile): Observable<string> {
-    const formData = new FormData();
-    formData.append('file', logoFile);
-
-    return this._httpClient.post<string>(`${APP_CONFIG.mediaLibrary.url}media`, formData, { withCredentials: true }).pipe(
+  create(data: Organization): Observable<Organization> {
+    return this._httpClient.post<IOrganization>(APP_CONFIG.organizations.url, data, { withCredentials: true }).pipe(
       map((response) => {
-        return response;
+        return new Organization(response);
       }),
     );
   }
 
-  replaceOrCreate(data: Organization): Observable<Organization> {
-    if (data.id) {
-      return this._httpClient.put<IOrganization>(APP_CONFIG.organizations.url + data.id, data, { withCredentials: true }).pipe(
-        map((response) => {
-          return new Organization(response);
-        }),
-      );
-    }
-
-    return this._httpClient.post<IOrganization>(APP_CONFIG.organizations.url, data, { withCredentials: true }).pipe(
+  update(data: Organization): Observable<Organization> {
+    return this._httpClient.put<IOrganization>(APP_CONFIG.organizations.url + data.id, data, { withCredentials: true }).pipe(
       map((response) => {
         return new Organization(response);
       }),
diff --git a/src/app/services/projection.service.ts b/src/app/services/projection.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2c6f8f11e1993b232bdc23b438a0833379582e51
--- /dev/null
+++ b/src/app/services/projection.service.ts
@@ -0,0 +1,98 @@
+import { Injectable } from '@angular/core';
+import { Observable, Subject } from 'rxjs';
+import { map } from 'rxjs/operators';
+import { HttpClient } from '@angular/common/http';
+import { APP_CONFIG } from './app-config.service';
+import { ProjectionRO, IProjection, Projection } from '../models/projection.model';
+
+@Injectable()
+export class ProjectionService {
+
+  projectionServiceUrl: string;
+  limit: number;
+  pageNumber: number;
+  sortOptions: {
+    value: string,
+    order: string,
+  };
+
+  private _searchChangeSubject: Subject<any>;
+
+  constructor(
+    private _httpClient: HttpClient) {
+    this.projectionServiceUrl = `${APP_CONFIG.resources.url}projections/`;
+    this._searchChangeSubject = new Subject<any>();
+    this.limit = 10;
+    this.pageNumber = 1;
+  }
+
+  getProjections(): Observable<ProjectionRO> {
+    let query = '?';
+    query += `limit=${(this.limit ? this.limit : 20)}`;
+    query += `&offset=${(this.pageNumber ? (this.pageNumber - 1) * this.limit : 0)}`;
+    query += `&sort_by=${this.sortOptions.value}.${this.sortOptions.order}`;
+
+    return this._httpClient.get<IProjection[]>(this.projectionServiceUrl + query, { observe: 'response' }).pipe(
+      map((response) => {
+        const totalCount = response.headers.get('Content-Range');
+        const projections = [];
+        response.body.forEach((projection) => {
+          projections.push(new Projection(projection));
+        });
+        return new ProjectionRO(projections, parseInt(totalCount, 10));
+      }));
+  }
+
+  getAllProjections(): Observable<Projection[]> {
+    return this._httpClient.get<IProjection[]>(this.projectionServiceUrl).pipe(
+      map(body => body.map(projection => new Projection(projection))),
+    );
+  }
+
+  findById(id): Observable<Projection> {
+    return this._httpClient.get<IProjection>(this.projectionServiceUrl + id).pipe(
+      map((response) => {
+        return new Projection(response);
+      }),
+    );
+  }
+
+  delete(id) {
+    return this._httpClient.delete(this.projectionServiceUrl + id, { withCredentials: true });
+  }
+
+  create(data): Observable<Projection> {
+    return this._httpClient.post<IProjection>(this.projectionServiceUrl, data, { withCredentials: true }).pipe(
+      map((response) => {
+        return new Projection(response);
+      }),
+    );
+  }
+
+  update(data): Observable<Projection> {
+    return this._httpClient.put<IProjection>(this.projectionServiceUrl + data.id, data, { withCredentials: true }).pipe(
+      map((response) => {
+        return new Projection(response);
+      }),
+    );
+  }
+
+  /* PAGINATION */
+  paginationChanged(limit: number, pageNumber: number) {
+    this.limit = limit;
+    this.pageNumber = pageNumber;
+    this._searchChangeSubject.next();
+  }
+
+  reverseSortOrder(): void {
+    if (this.sortOptions.order === 'asc') {
+      this.sortOptions.order = 'desc';
+    } else {
+      this.sortOptions.order = 'asc';
+    }
+  }
+
+  get searchChange$(): Observable<string> {
+    return this._searchChangeSubject.asObservable();
+  }
+}
diff --git a/src/app/services/resource.service.ts b/src/app/services/resource.service.ts
index 52b4b03363f4e926e9f97a8e4ea486cf82c515d0..d5e03ce38c9ffc3f7d175c1f79cf2218711bb096 100644
--- a/src/app/services/resource.service.ts
+++ b/src/app/services/resource.service.ts
@@ -36,7 +36,6 @@ export class ResourceService {
       map((response) => {
         const totalCount = response.headers.get('Content-Range');
         const resources = [];
-        console.log(response);
         response.body.forEach((resource) => {
           resources.push(new Resource(resource));
         });
@@ -56,14 +55,7 @@ export class ResourceService {
     return this._httpClient.delete(this.resourceServiceUrl + id, { withCredentials: true });
   }
 
-  replaceOrCreate(data): Observable<Resource> {
-    if (data.id) {
-      return this._httpClient.put<IResource>(this.resourceServiceUrl + data.id, data, { withCredentials: true }).pipe(
-        map((response) => {
-          return new Resource(response);
-        }),
-      );
-    }
+  create(data) {
     return this._httpClient.post<IResource>(this.resourceServiceUrl, data, { withCredentials: true }).pipe(
       map((response) => {
         return new Resource(response);
@@ -71,6 +63,14 @@ export class ResourceService {
     );
   }
 
+  update(data) {
+    return this._httpClient.put<IResource>(this.resourceServiceUrl + data.id, data, { withCredentials: true }).pipe(
+      map((response) => {
+        return new Resource(response);
+      }),
+    );
+  }
+
   createResourceFormats(resourceId: string, resourceFormats: IResourceFormat[]): Observable<IResourceFormat> {
     return from(resourceFormats).pipe(
       concatMap((rf) => {
diff --git a/src/app/services/reuse.service.ts b/src/app/services/reuse.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e2331685e2340b5f14b76278e0ea093c403c26de
--- /dev/null
+++ b/src/app/services/reuse.service.ts
@@ -0,0 +1,92 @@
+import { Injectable } from '@angular/core';
+import { Observable, Subject } from 'rxjs';
+import { map } from 'rxjs/operators';
+import { HttpClient } from '@angular/common/http';
+import { APP_CONFIG } from './app-config.service';
+import { ReuseRO, IReuse, Reuse } from '../models/reuse.model';
+
+@Injectable()
+export class ReuseService {
+
+  reuseServiceUrl: string;
+  limit: number;
+  pageNumber: number;
+  sortOptions: {
+    value: string,
+    order: string,
+  };
+
+  private _searchChangeSubject: Subject<any>;
+
+  constructor(
+    private _httpClient: HttpClient) {
+    this.reuseServiceUrl = `${APP_CONFIG.reuses.url}reuses/`;
+    this._searchChangeSubject = new Subject<any>();
+    this.limit = 10;
+    this.pageNumber = 1;
+  }
+
+  getReuses(): Observable<ReuseRO> {
+    let query = '?';
+    query += `limit=${(this.limit ? this.limit : 20)}`;
+    query += `&offset=${(this.pageNumber ? (this.pageNumber - 1) * this.limit : 0)}`;
+    query += `&sort_by=${this.sortOptions.value}.${this.sortOptions.order}`;
+
+    return this._httpClient.get<IReuse[]>(this.reuseServiceUrl + query, { withCredentials: true, observe: 'response' }).pipe(
+      map((response) => {
+        const totalCount = response.headers.get('Content-Range');
+        const reuses = [];
+        response.body.forEach((reuse) => {
+          reuses.push(new Reuse(reuse));
+        });
+        return new ReuseRO(reuses, parseInt(totalCount, 10));
+      }));
+  }
+
+  findById(id): Observable<Reuse> {
+    return this._httpClient.get<IReuse>(this.reuseServiceUrl + id, { withCredentials: true }).pipe(
+      map((response) => {
+        return new Reuse(response);
+      }),
+    );
+  }
+
+  delete(id) {
+    return this._httpClient.delete(this.reuseServiceUrl + id, { withCredentials: true });
+  }
+
+  create(data) {
+    return this._httpClient.post<IReuse>(this.reuseServiceUrl, data, { withCredentials: true }).pipe(
+      map((response) => {
+        return new Reuse(response);
+      }),
+    );
+  }
+
+  update(data) {
+    return this._httpClient.put<IReuse>(this.reuseServiceUrl + data._id, data, { withCredentials: true }).pipe(
+      map((response) => {
+        return new Reuse(response);
+      }),
+    );
+  }
+
+  /* PAGINATION */
+  paginationChanged(limit: number, pageNumber: number) {
+    this.limit = limit;
+    this.pageNumber = pageNumber;
+    this._searchChangeSubject.next();
+  }
+
+  reverseSortOrder(): void {
+    if (this.sortOptions.order === 'asc') {
+      this.sortOptions.order = 'desc';
+    } else {
+      this.sortOptions.order = 'asc';
+    }
+  }
+
+  get searchChange$(): Observable<string> {
+    return this._searchChangeSubject.asObservable();
+  }
+}
diff --git a/src/app/user/interceptors/auth-interceptor.ts b/src/app/user/interceptors/auth-interceptor.ts
index 2f8f113eaf85d5183915cb387743d1faed7300e5..15fef602552523f8d23b2971805db0a3b55188d7 100644
--- a/src/app/user/interceptors/auth-interceptor.ts
+++ b/src/app/user/interceptors/auth-interceptor.ts
@@ -13,7 +13,7 @@ export class AuthInterceptor implements HttpInterceptor {
     let request = req;
 
     // && req.url.includes('https://data-intothesky.alpha.grandlyon.com/authentication/api/logout'
-    if (xsrfToken) {
+    if (xsrfToken && !req.url.includes('indexerdb')) {
       request = req.clone({
         headers: req.headers.set('x-xsrf-token', xsrfToken),
       });
diff --git a/src/assets/config/config.json b/src/assets/config/config.json
index adf822d5cb7103ae1e517c14ba05e845c154d0c4..cb7be29dc905b917b56916f84d3f7c3f2c304d0b 100644
--- a/src/assets/config/config.json
+++ b/src/assets/config/config.json
@@ -13,5 +13,18 @@
   },
   "middlewareLegacyAuth": {
     "url": "https://kong-dev.alpha.grandlyon.com/middleware-legacy/"
+  },
+  "restHeartAggregations": {
+    "url": "https://kong-dev.alpha.grandlyon.com/indexer-logs/indexerdb/"
+  },
+  "changelog": {
+    "url": "https://kong-dev.alpha.grandlyon.com/changelog/"
+  },
+  "credits": {
+    "url": "https://kong-dev.alpha.grandlyon.com/credits/credits/"
+  },
+  "reuses": {
+    "url": "http://localhost:3008/"
   }
 }
+
diff --git a/src/assets/img/default-file-logo.png b/src/assets/img/default-file-logo.png
new file mode 100644
index 0000000000000000000000000000000000000000..e7794f8d4076f561c4d956aaa572f82d17fb8750
Binary files /dev/null and b/src/assets/img/default-file-logo.png differ
diff --git a/src/assets/img/ghost-logo.png b/src/assets/img/ghost-logo.png
new file mode 100644
index 0000000000000000000000000000000000000000..84c1ec5d2099baaff1f7b1ab4b784ec7dabcc125
Binary files /dev/null and b/src/assets/img/ghost-logo.png differ
diff --git a/src/scss/init_bulma.scss b/src/scss/init_bulma.scss
index 233d54e3a2b260c24b8dad3fe549d039a43a23d5..105a86142e81fadce62ae9ecb5f3074c5d96abb4 100644
--- a/src/scss/init_bulma.scss
+++ b/src/scss/init_bulma.scss
@@ -42,3 +42,4 @@ $input-color: $brand-color;
 @import "../../node_modules/bulma/sass/grid/_all";
 @import "../../node_modules/bulma/sass/form/_all";
 @import "../../node_modules/bulma/sass/layout/_all";
+@import 'bulma-switch';
diff --git a/src/styles.scss b/src/styles.scss
index 09822331d571d2ba51805064dfcb6a158b662a04..480998d60cb2b1158b7f9599ad5ccb755c523c28 100644
--- a/src/styles.scss
+++ b/src/styles.scss
@@ -1,4 +1,6 @@
 @import './scss/init_bulma.scss';
+// @import hljs from 'highlight.js/lib/highlight';
+@import 'highlight.js/styles/github.css';
 
 html,
 body {
@@ -83,6 +85,79 @@ textarea.ng-invalid:not(form).ng-touched {
   /* red */
 }
 
+.btn-blue-text {
+  background: transparent;
+  border-radius: 2px;
+  font-size: $size-6;
+  color: $link-color;
+  border: 1px solid $grey-super-light-color;
+  padding: 0.5em 0.75em 0.5em 0.75em;
+  white-space: normal;
+  line-height: unset;
+  min-width: 8rem;
+
+  /* stylelint-disable-next-line */
+  span {
+    font-size: 0.875rem;
+    color: $link-color;
+    white-space: normal;
+    line-height: 1;
+  }
+
+  &:disabled {
+    color: $link-color;
+  }
+
+  &:hover:not(:disabled),
+  &:focus:not(:disabled) {
+    color: $link-color;
+    border-color: $grey-dark-color;
+    font-weight: bold;
+  }
+
+  &.is-loading::after {
+    border-left-color: $link-color;
+    border-bottom-color: $link-color;
+  }
+}
+
+.btn-red-text {
+  background: transparent;
+  border-radius: 2px;
+  font-size: $size-6;
+  color: $tomato-color;
+  border: 1px solid $grey-super-light-color;
+  padding: 0.5em 0.75em 0.5em 0.75em;
+  white-space: normal;
+  line-height: unset;
+  min-width: 8rem;
+
+  /* stylelint-disable-next-line */
+  span {
+    font-size: 0.875rem;
+    color: $tomato-color;
+    white-space: normal;
+    line-height: 1;
+  }
+
+  &:disabled {
+    color: $tomato-color;
+  }
+
+  &:hover:not(:disabled),
+  &:focus:not(:disabled) {
+    color: $tomato-color;
+    border-color: $grey-dark-color;
+    font-weight: bold;
+  }
+
+  &.is-loading::after {
+    border-left-color: $tomato-color;
+    border-bottom-color: $tomato-color;
+  }
+}
+
+
 .button-gl {
   min-width: 8rem;
   background: $tomato-color;
@@ -203,4 +278,44 @@ textarea.ng-invalid:not(form).ng-touched {
   .paginator {
     border-top: 1px solid $grey-super-light-color;
   }
+
+  .entity-logo-in-list {
+    max-height: 40px;
+  }
+}
+
+.header-with-publication-status {
+  display: flex;
+  align-items: center;
+
+  app-page-header {
+    flex-grow: 1;
+  }
+
+  @media screen and (max-width: $tablet) {
+    flex-flow: column;
+    align-items: flex-start;
+  }
+
+  .status-field {
+    margin-left: 1rem;
+
+    @media screen and (max-width: $tablet) {
+      margin-left: 0;
+    }
+
+    .fake-label {
+      font-weight: bold;
+      margin-right: 0.5rem;
+    }
+
+    label {
+      padding-top: 0;
+    }
+  }
+}
+
+.empty-property {
+  font-style: italic;
+  color: #818080;
 }