diff --git a/config/config-dev.json b/config/config-dev.json
index 3320942c82b8a0ff6ae8e347c87e619f1ab91685..e154cc463bc6600168812652f7d706e0b587c0fb 100644
--- a/config/config-dev.json
+++ b/config/config-dev.json
@@ -3,6 +3,9 @@
     "url": "https://kong-dev.alpha.grandlyon.com/organizations/"
   },
   "resources": {
-    "url": "https://kong-dev.alpha.grandlyon.com/resources/resources/"
+    "url": "https://kong-dev.alpha.grandlyon.com/resources/"
+  },
+  "mediaLibrary": {
+    "url": "https://kong-dev.alpha.grandlyon.com/media-library/"
   }
-}
\ No newline at end of file
+}
diff --git a/config/config-rec.json b/config/config-rec.json
index 4955a9a3ca1fb9c3cc45c9fc867b4ff120df5e06..99666bacf0611bdb0dc575d1877ca8fd1161b41f 100644
--- a/config/config-rec.json
+++ b/config/config-rec.json
@@ -3,6 +3,9 @@
     "url": "https://kong-rec.alpha.grandlyon.com/organizations/"
   },
   "resources": {
-    "url": "https://kong-rec.alpha.grandlyon.com/resources/resources/"
+    "url": "https://kong-rec.alpha.grandlyon.com/resources/"
+  },
+  "mediaLibrary": {
+    "url": "https://kong-rec.alpha.grandlyon.com/media-library/"
   }
-}
\ No newline at end of file
+}
diff --git a/package-lock.json b/package-lock.json
index 407e9f82fe0cc07b92a531131648fed6ca8d87f1..4880633268b69e163a910ab76ab2c76cce10bede 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -930,6 +930,46 @@
         }
       }
     },
+    "@fimbul/bifrost": {
+      "version": "0.17.0",
+      "resolved": "https://registry.npmjs.org/@fimbul/bifrost/-/bifrost-0.17.0.tgz",
+      "integrity": "sha512-gVTkJAOef5HtN6LPmrtt5fAUmBywwlgmObsU3FBhPoNeXPLaIl2zywXkJEtvvVLQnaFmtff3x+wIj5lHRCDE3Q==",
+      "dev": true,
+      "requires": {
+        "@fimbul/ymir": "^0.17.0",
+        "get-caller-file": "^2.0.0",
+        "tslib": "^1.8.1",
+        "tsutils": "^3.5.0"
+      },
+      "dependencies": {
+        "get-caller-file": {
+          "version": "2.0.5",
+          "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+          "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+          "dev": true
+        },
+        "tsutils": {
+          "version": "3.10.0",
+          "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.10.0.tgz",
+          "integrity": "sha512-q20XSMq7jutbGB8luhKKsQldRKWvyBO2BGqni3p4yq8Ys9bEP/xQw3KepKmMRt9gJ4lvQSScrihJrcKdKoSU7Q==",
+          "dev": true,
+          "requires": {
+            "tslib": "^1.8.1"
+          }
+        }
+      }
+    },
+    "@fimbul/ymir": {
+      "version": "0.17.0",
+      "resolved": "https://registry.npmjs.org/@fimbul/ymir/-/ymir-0.17.0.tgz",
+      "integrity": "sha512-xMXM9KTXRLHLVS6dnX1JhHNEkmWHcAVCQ/4+DA1KKwC/AFnGHzu/7QfQttEPgw3xplT+ILf9e3i64jrFwB3JtA==",
+      "dev": true,
+      "requires": {
+        "inversify": "^5.0.0",
+        "reflect-metadata": "^0.1.12",
+        "tslib": "^1.8.1"
+      }
+    },
     "@ngtools/webpack": {
       "version": "7.3.1",
       "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-7.3.1.tgz",
@@ -1489,14 +1529,12 @@
     "ansi-regex": {
       "version": "2.1.1",
       "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
-      "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
-      "dev": true
+      "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
     },
     "ansi-styles": {
       "version": "3.2.1",
       "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
       "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
-      "dev": true,
       "requires": {
         "color-convert": "^1.9.0"
       }
@@ -1546,7 +1584,6 @@
       "version": "1.0.10",
       "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
       "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
-      "dev": true,
       "requires": {
         "sprintf-js": "~1.0.2"
       }
@@ -1756,7 +1793,6 @@
       "version": "6.26.0",
       "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz",
       "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=",
-      "dev": true,
       "requires": {
         "chalk": "^1.1.3",
         "esutils": "^2.0.2",
@@ -1766,14 +1802,12 @@
         "ansi-styles": {
           "version": "2.2.1",
           "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
-          "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
-          "dev": true
+          "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4="
         },
         "chalk": {
           "version": "1.1.3",
           "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
           "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
-          "dev": true,
           "requires": {
             "ansi-styles": "^2.2.1",
             "escape-string-regexp": "^1.0.2",
@@ -1785,8 +1819,7 @@
         "supports-color": {
           "version": "2.0.0",
           "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
-          "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
-          "dev": true
+          "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc="
         }
       }
     },
@@ -1882,8 +1915,7 @@
     "balanced-match": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
-      "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
-      "dev": true
+      "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
     },
     "base": {
       "version": "0.11.2",
@@ -2075,7 +2107,6 @@
       "version": "1.1.11",
       "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
       "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
-      "dev": true,
       "requires": {
         "balanced-match": "^1.0.0",
         "concat-map": "0.0.1"
@@ -2118,7 +2149,7 @@
     },
     "browserify-aes": {
       "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
+      "resolved": "http://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
       "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==",
       "dev": true,
       "requires": {
@@ -2155,7 +2186,7 @@
     },
     "browserify-rsa": {
       "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz",
+      "resolved": "http://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz",
       "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=",
       "dev": true,
       "requires": {
@@ -2209,7 +2240,7 @@
     },
     "buffer": {
       "version": "4.9.1",
-      "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz",
+      "resolved": "http://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz",
       "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=",
       "dev": true,
       "requires": {
@@ -2261,8 +2292,7 @@
     "builtin-modules": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
-      "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=",
-      "dev": true
+      "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8="
     },
     "builtin-status-codes": {
       "version": "3.0.0",
@@ -2340,7 +2370,7 @@
     },
     "camelcase-keys": {
       "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz",
+      "resolved": "http://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz",
       "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=",
       "dev": true,
       "optional": true,
@@ -2591,7 +2621,6 @@
       "version": "1.9.3",
       "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
       "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
-      "dev": true,
       "requires": {
         "color-name": "1.1.3"
       }
@@ -2599,8 +2628,7 @@
     "color-name": {
       "version": "1.1.3",
       "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
-      "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
-      "dev": true
+      "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
     },
     "colors": {
       "version": "1.1.2",
@@ -2629,8 +2657,7 @@
     "commander": {
       "version": "2.17.1",
       "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz",
-      "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==",
-      "dev": true
+      "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg=="
     },
     "commondir": {
       "version": "1.0.1",
@@ -2689,8 +2716,7 @@
     "concat-map": {
       "version": "0.0.1",
       "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
-      "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
-      "dev": true
+      "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
     },
     "concat-stream": {
       "version": "1.6.2",
@@ -2882,7 +2908,7 @@
     },
     "create-hash": {
       "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
+      "resolved": "http://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
       "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==",
       "dev": true,
       "requires": {
@@ -2895,7 +2921,7 @@
     },
     "create-hmac": {
       "version": "1.1.7",
-      "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz",
+      "resolved": "http://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz",
       "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==",
       "dev": true,
       "requires": {
@@ -3208,12 +3234,11 @@
     "diff": {
       "version": "3.5.0",
       "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz",
-      "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==",
-      "dev": true
+      "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA=="
     },
     "diffie-hellman": {
       "version": "5.0.3",
-      "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz",
+      "resolved": "http://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz",
       "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==",
       "dev": true,
       "requires": {
@@ -3256,6 +3281,30 @@
         "buffer-indexof": "^1.0.0"
       }
     },
+    "doctrine": {
+      "version": "0.7.2",
+      "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-0.7.2.tgz",
+      "integrity": "sha1-fLhgNZujvpDgQLJrcpzkv6ZUxSM=",
+      "dev": true,
+      "requires": {
+        "esutils": "^1.1.6",
+        "isarray": "0.0.1"
+      },
+      "dependencies": {
+        "esutils": {
+          "version": "1.1.6",
+          "resolved": "https://registry.npmjs.org/esutils/-/esutils-1.1.6.tgz",
+          "integrity": "sha1-wBzKqa5LiXxtDD4hCuUvPHqEQ3U=",
+          "dev": true
+        },
+        "isarray": {
+          "version": "0.0.1",
+          "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+          "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
+          "dev": true
+        }
+      }
+    },
     "dom-serialize": {
       "version": "2.2.1",
       "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz",
@@ -3520,8 +3569,7 @@
     "escape-string-regexp": {
       "version": "1.0.5",
       "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
-      "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
-      "dev": true
+      "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
     },
     "eslint-scope": {
       "version": "4.0.0",
@@ -3551,8 +3599,7 @@
     "esutils": {
       "version": "2.0.2",
       "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz",
-      "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=",
-      "dev": true
+      "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs="
     },
     "etag": {
       "version": "1.8.1",
@@ -4022,7 +4069,7 @@
     },
     "finalhandler": {
       "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz",
+      "resolved": "http://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz",
       "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==",
       "dev": true,
       "requires": {
@@ -4198,8 +4245,7 @@
     "fs.realpath": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
-      "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
-      "dev": true
+      "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
     },
     "fsevents": {
       "version": "1.2.4",
@@ -4242,14 +4288,12 @@
         "balanced-match": {
           "version": "1.0.0",
           "bundled": true,
-          "dev": true,
-          "optional": true
+          "dev": true
         },
         "brace-expansion": {
           "version": "1.1.11",
           "bundled": true,
           "dev": true,
-          "optional": true,
           "requires": {
             "balanced-match": "^1.0.0",
             "concat-map": "0.0.1"
@@ -4269,8 +4313,7 @@
         "concat-map": {
           "version": "0.0.1",
           "bundled": true,
-          "dev": true,
-          "optional": true
+          "dev": true
         },
         "console-control-strings": {
           "version": "1.1.0",
@@ -4418,7 +4461,6 @@
           "version": "3.0.4",
           "bundled": true,
           "dev": true,
-          "optional": true,
           "requires": {
             "brace-expansion": "^1.1.7"
           }
@@ -4815,7 +4857,6 @@
       "version": "7.1.3",
       "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
       "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
-      "dev": true,
       "requires": {
         "fs.realpath": "^1.0.0",
         "inflight": "^1.0.4",
@@ -4989,7 +5030,6 @@
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
       "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
-      "dev": true,
       "requires": {
         "ansi-regex": "^2.0.0"
       }
@@ -5020,8 +5060,7 @@
     "has-flag": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
-      "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
-      "dev": true
+      "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
     },
     "has-unicode": {
       "version": "2.0.1",
@@ -5180,7 +5219,7 @@
     },
     "http-proxy-middleware": {
       "version": "0.18.0",
-      "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.18.0.tgz",
+      "resolved": "http://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.18.0.tgz",
       "integrity": "sha512-Fs25KVMPAIIcgjMZkVHJoKg9VcXcC1C8yb9JUgeDvVXY0S/zgVIhMb+qVswDIgtJe2DfckMSY2d6TuTEutlk6Q==",
       "dev": true,
       "requires": {
@@ -5407,7 +5446,6 @@
       "version": "1.0.6",
       "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
       "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
-      "dev": true,
       "requires": {
         "once": "^1.3.0",
         "wrappy": "1"
@@ -5416,8 +5454,7 @@
     "inherits": {
       "version": "2.0.3",
       "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
-      "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
-      "dev": true
+      "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
     },
     "ini": {
       "version": "1.3.5",
@@ -5523,6 +5560,12 @@
         "loose-envify": "^1.0.0"
       }
     },
+    "inversify": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/inversify/-/inversify-5.0.1.tgz",
+      "integrity": "sha512-Ieh06s48WnEYGcqHepdsJUIJUXpwH5o5vodAX+DK2JA/gjy4EbEcQZxw+uFfzysmKjiLXGYwNG3qDZsKVMcINQ==",
+      "dev": true
+    },
     "invert-kv": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz",
@@ -6067,14 +6110,12 @@
     "js-tokens": {
       "version": "3.0.2",
       "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz",
-      "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=",
-      "dev": true
+      "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls="
     },
     "js-yaml": {
       "version": "3.12.0",
       "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz",
       "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==",
-      "dev": true,
       "requires": {
         "argparse": "^1.0.7",
         "esprima": "^4.0.0"
@@ -6083,8 +6124,7 @@
         "esprima": {
           "version": "4.0.1",
           "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
-          "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
-          "dev": true
+          "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="
         }
       }
     },
@@ -6391,7 +6431,7 @@
     },
     "load-json-file": {
       "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
+      "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
       "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=",
       "dev": true,
       "requires": {
@@ -6769,7 +6809,7 @@
       "dependencies": {
         "minimist": {
           "version": "1.2.0",
-          "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+          "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
           "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
           "dev": true,
           "optional": true
@@ -6874,7 +6914,6 @@
       "version": "3.0.4",
       "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
       "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
-      "dev": true,
       "requires": {
         "brace-expansion": "^1.1.7"
       }
@@ -6882,8 +6921,7 @@
     "minimist": {
       "version": "0.0.8",
       "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
-      "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
-      "dev": true
+      "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
     },
     "minipass": {
       "version": "2.3.5",
@@ -7104,7 +7142,7 @@
       "dependencies": {
         "semver": {
           "version": "5.3.0",
-          "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz",
+          "resolved": "http://registry.npmjs.org/semver/-/semver-5.3.0.tgz",
           "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=",
           "dev": true,
           "optional": true
@@ -7196,7 +7234,7 @@
         },
         "chalk": {
           "version": "1.1.3",
-          "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+          "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
           "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
           "dev": true,
           "optional": true,
@@ -7459,7 +7497,6 @@
       "version": "1.4.0",
       "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
       "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
-      "dev": true,
       "requires": {
         "wrappy": "1"
       }
@@ -7486,7 +7523,6 @@
       "version": "0.6.1",
       "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz",
       "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=",
-      "dev": true,
       "requires": {
         "minimist": "~0.0.1",
         "wordwrap": "~0.0.2"
@@ -7495,8 +7531,7 @@
         "wordwrap": {
           "version": "0.0.3",
           "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz",
-          "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=",
-          "dev": true
+          "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc="
         }
       }
     },
@@ -7523,7 +7558,7 @@
     },
     "os-locale": {
       "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz",
+      "resolved": "http://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz",
       "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=",
       "dev": true,
       "optional": true,
@@ -7868,8 +7903,7 @@
     "path-is-absolute": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
-      "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
-      "dev": true
+      "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
     },
     "path-is-inside": {
       "version": "1.0.2",
@@ -7886,8 +7920,7 @@
     "path-parse": {
       "version": "1.0.6",
       "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
-      "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
-      "dev": true
+      "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw=="
     },
     "path-to-regexp": {
       "version": "0.1.7",
@@ -8689,7 +8722,6 @@
       "version": "1.8.1",
       "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.8.1.tgz",
       "integrity": "sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA==",
-      "dev": true,
       "requires": {
         "path-parse": "^1.0.5"
       }
@@ -8788,6 +8820,30 @@
         "tslib": "^1.9.0"
       }
     },
+    "rxjs-tslint": {
+      "version": "0.1.7",
+      "resolved": "https://registry.npmjs.org/rxjs-tslint/-/rxjs-tslint-0.1.7.tgz",
+      "integrity": "sha512-NnOfqutNfdT7VQnQm32JLYh2gDZjc0gdWZFtrxf/czNGkLKJ1nOO6jbKAFI09W0f9lCtv6P2ozxjbQH8TSPPFQ==",
+      "requires": {
+        "chalk": "^2.4.0",
+        "optimist": "^0.6.1",
+        "tslint": "^5.9.1",
+        "tsutils": "^2.25.0",
+        "typescript": ">=2.8.3"
+      },
+      "dependencies": {
+        "chalk": {
+          "version": "2.4.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+          "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+          "requires": {
+            "ansi-styles": "^3.2.1",
+            "escape-string-regexp": "^1.0.5",
+            "supports-color": "^5.3.0"
+          }
+        }
+      }
+    },
     "safe-buffer": {
       "version": "5.1.2",
       "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
@@ -8852,7 +8908,7 @@
     },
     "sax": {
       "version": "0.5.8",
-      "resolved": "https://registry.npmjs.org/sax/-/sax-0.5.8.tgz",
+      "resolved": "http://registry.npmjs.org/sax/-/sax-0.5.8.tgz",
       "integrity": "sha1-1HLbIo6zMcJQaw6MFVJK25OdEsE=",
       "dev": true
     },
@@ -8931,8 +8987,7 @@
     "semver": {
       "version": "5.6.0",
       "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz",
-      "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==",
-      "dev": true
+      "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg=="
     },
     "semver-dsl": {
       "version": "1.0.1",
@@ -9057,7 +9112,7 @@
     },
     "sha.js": {
       "version": "2.4.11",
-      "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
+      "resolved": "http://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
       "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==",
       "dev": true,
       "requires": {
@@ -9587,8 +9642,7 @@
     "sprintf-js": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
-      "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
-      "dev": true
+      "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
     },
     "sshpk": {
       "version": "1.15.1",
@@ -9754,7 +9808,6 @@
       "version": "3.0.1",
       "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
       "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
-      "dev": true,
       "requires": {
         "ansi-regex": "^2.0.0"
       }
@@ -9848,7 +9901,6 @@
       "version": "5.5.0",
       "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
       "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
-      "dev": true,
       "requires": {
         "has-flag": "^3.0.0"
       }
@@ -10260,7 +10312,6 @@
       "version": "5.9.1",
       "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.9.1.tgz",
       "integrity": "sha1-ElX4ej/1frCw4fDmEKi0dIBGya4=",
-      "dev": true,
       "requires": {
         "babel-code-frame": "^6.22.0",
         "builtin-modules": "^1.1.1",
@@ -10280,7 +10331,6 @@
           "version": "2.4.1",
           "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz",
           "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==",
-          "dev": true,
           "requires": {
             "ansi-styles": "^3.2.1",
             "escape-string-regexp": "^1.0.5",
@@ -10289,11 +10339,80 @@
         }
       }
     },
+    "tslint-config-airbnb": {
+      "version": "5.11.1",
+      "resolved": "https://registry.npmjs.org/tslint-config-airbnb/-/tslint-config-airbnb-5.11.1.tgz",
+      "integrity": "sha512-hkaittm2607vVMe8eotANGN1CimD5tor7uoY3ypg2VTtEcDB/KGWYbJOz58t8LI4cWSyWtgqYQ5F0HwKxxhlkQ==",
+      "dev": true,
+      "requires": {
+        "tslint-consistent-codestyle": "^1.14.1",
+        "tslint-eslint-rules": "^5.4.0",
+        "tslint-microsoft-contrib": "~5.2.1"
+      }
+    },
+    "tslint-consistent-codestyle": {
+      "version": "1.15.1",
+      "resolved": "https://registry.npmjs.org/tslint-consistent-codestyle/-/tslint-consistent-codestyle-1.15.1.tgz",
+      "integrity": "sha512-38Y3Dz4zcABe/PlPAQSGNEWPGVq0OzcIQR7SEU6dNujp/SgvhxhJOhIhI9gY4r0I3/TNtvVQwARWor9O9LPZWg==",
+      "dev": true,
+      "requires": {
+        "@fimbul/bifrost": "^0.17.0",
+        "tslib": "^1.7.1",
+        "tsutils": "^2.29.0"
+      }
+    },
+    "tslint-eslint-rules": {
+      "version": "5.4.0",
+      "resolved": "https://registry.npmjs.org/tslint-eslint-rules/-/tslint-eslint-rules-5.4.0.tgz",
+      "integrity": "sha512-WlSXE+J2vY/VPgIcqQuijMQiel+UtmXS+4nvK4ZzlDiqBfXse8FAvkNnTcYhnQyOTW5KFM+uRRGXxYhFpuBc6w==",
+      "dev": true,
+      "requires": {
+        "doctrine": "0.7.2",
+        "tslib": "1.9.0",
+        "tsutils": "^3.0.0"
+      },
+      "dependencies": {
+        "tslib": {
+          "version": "1.9.0",
+          "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.0.tgz",
+          "integrity": "sha512-f/qGG2tUkrISBlQZEjEqoZ3B2+npJjIf04H1wuAv9iA8i04Icp+61KRXxFdha22670NJopsZCIjhC3SnjPRKrQ==",
+          "dev": true
+        },
+        "tsutils": {
+          "version": "3.10.0",
+          "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.10.0.tgz",
+          "integrity": "sha512-q20XSMq7jutbGB8luhKKsQldRKWvyBO2BGqni3p4yq8Ys9bEP/xQw3KepKmMRt9gJ4lvQSScrihJrcKdKoSU7Q==",
+          "dev": true,
+          "requires": {
+            "tslib": "^1.8.1"
+          }
+        }
+      }
+    },
+    "tslint-microsoft-contrib": {
+      "version": "5.2.1",
+      "resolved": "https://registry.npmjs.org/tslint-microsoft-contrib/-/tslint-microsoft-contrib-5.2.1.tgz",
+      "integrity": "sha512-PDYjvpo0gN9IfMULwKk0KpVOPMhU6cNoT9VwCOLeDl/QS8v8W2yspRpFFuUS7/c5EIH/n8ApMi8TxJAz1tfFUA==",
+      "dev": true,
+      "requires": {
+        "tsutils": "^2.27.2 <2.29.0"
+      },
+      "dependencies": {
+        "tsutils": {
+          "version": "2.28.0",
+          "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.28.0.tgz",
+          "integrity": "sha512-bh5nAtW0tuhvOJnx1GLRn5ScraRLICGyJV5wJhtRWOLsxW70Kk5tZtpK3O/hW6LDnqKS9mlUMPZj9fEMJ0gxqA==",
+          "dev": true,
+          "requires": {
+            "tslib": "^1.8.1"
+          }
+        }
+      }
+    },
     "tsutils": {
       "version": "2.29.0",
       "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz",
       "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==",
-      "dev": true,
       "requires": {
         "tslib": "^1.8.1"
       }
@@ -10338,8 +10457,7 @@
     "typescript": {
       "version": "3.2.4",
       "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.2.4.tgz",
-      "integrity": "sha512-0RNDbSdEokBeEAkgNbxJ+BLwSManFy9TeXz8uW+48j/xhEXv1ePME60olyzw2XzUqUBNAYFeJadIqAgNqIACwg==",
-      "dev": true
+      "integrity": "sha512-0RNDbSdEokBeEAkgNbxJ+BLwSManFy9TeXz8uW+48j/xhEXv1ePME60olyzw2XzUqUBNAYFeJadIqAgNqIACwg=="
     },
     "uglify-js": {
       "version": "3.4.9",
@@ -11133,8 +11251,7 @@
     "wrappy": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
-      "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
-      "dev": true
+      "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
     },
     "xml2js": {
       "version": "0.4.19",
diff --git a/package.json b/package.json
index 44e295b09d709f0807cae53bd0e4fbed2c2ed20d..d9459cdc73c1364adc05fc7b717eda481ce7812f 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "admin-gui",
-  "version": "0.0.0",
+  "version": "1.0.0",
   "scripts": {
     "ng": "ng",
     "start": "ng serve",
@@ -26,6 +26,7 @@
     "bulma": "^0.7.4",
     "core-js": "^2.6.4",
     "rxjs": "^6.4.0",
+    "rxjs-tslint": "^0.1.7",
     "sass-recursive-map-merge": "^1.0.1",
     "zone.js": "^0.8.29"
   },
@@ -48,6 +49,7 @@
     "protractor": "^5.4.2",
     "ts-node": "~5.0.1",
     "tslint": "~5.9.1",
+    "tslint-config-airbnb": "^5.11.1",
     "typescript": "~3.2.4"
   }
 }
diff --git a/src/app/app.component.html b/src/app/app.component.html
index f752e2e749bef88d778a4a25316c122aa1536965..3043831d86a8dae809137666c003e22eeb3ad9cc 100644
--- a/src/app/app.component.html
+++ b/src/app/app.component.html
@@ -1,23 +1,23 @@
 <div class="grid">
 
-  <header class="main-header">
-      <div class="hamburger" 
-      (click)="sidebarOpened = !sidebarOpened"
-      [ngClass]="{'expanded': sidebarOpened}">
+    <header class="main-header">
+        <div class="hamburger" (click)="sidebarOpened = !sidebarOpened" [ngClass]="{'expanded': sidebarOpened}">
 
-      </div>
-      <h4>Plateforme Data Admin</h4>
-      <div class="logo">
-          <img src="assets/img/logo.svg" alt="Le Grand Lyon, la métropole">
+        </div>
+        <h4>Plateforme Data Admin</h4>
+        <div class="logo">
+            <img src="assets/img/logo.svg" alt="Le Grand Lyon, la métropole">
 
-      </div>
-  </header>
-  <nav class="main-nav" [ngClass]="{'is-active': sidebarOpened}">
-      <app-menu [expanded]="sidebarOpened"></app-menu>
-  </nav>
-  <div class="main-content" [ngClass]="{'wide': !sidebarOpened}">
-      <router-outlet></router-outlet>
+        </div>
+    </header>
+    <nav class="main-nav" [ngClass]="{'is-active': sidebarOpened}">
+        <app-menu [expanded]="sidebarOpened"></app-menu>
+    </nav>
+    <div class="main-content" [ngClass]="{'wide': !sidebarOpened}">
+        <router-outlet></router-outlet>
 
-  </div>
+    </div>
 
 </div>
+
+<app-notifications></app-notifications>
\ No newline at end of file
diff --git a/src/app/app.component.ts b/src/app/app.component.ts
index 75e19beeeef3748f190ce47d71bba6935be957f6..d00c62e939d0fa8b9677eab2569861298897d7ec 100644
--- a/src/app/app.component.ts
+++ b/src/app/app.component.ts
@@ -1,8 +1,5 @@
 import { Component, OnInit } from '@angular/core';
 
-
-
-
 @Component({
   selector: 'app-root',
   templateUrl: './app.component.html',
@@ -14,13 +11,9 @@ export class AppComponent implements OnInit {
   sidebarOpened: boolean;
 
   constructor(
-  ) {
-  }
-
-
+  ) {}
 
   ngOnInit(): void {
     this.sidebarOpened = false;
   }
-
 }
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index c5e0ccd4a906c771333afc79d90f75d50cdf6476..9b843b6f527b5aebbae62eec91003795da9b875f 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -41,6 +41,6 @@ export function initAppConfig(appConfigService: AppConfigService) {
       multi: true,
     },
   ],
-  bootstrap: [AppComponent]
+  bootstrap: [AppComponent],
 })
 export class AppModule { }
diff --git a/src/app/app.routing.module.ts b/src/app/app.routing.module.ts
index 23e3d791e5f1e9615cc870ed1b5260219d76c007..2e229688a10fc14059435ecf98a68a990b6f7dc4 100644
--- a/src/app/app.routing.module.ts
+++ b/src/app/app.routing.module.ts
@@ -7,7 +7,7 @@ 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';
 
 const appRoutes: Routes = [
   {
@@ -73,6 +73,34 @@ const appRoutes: Routes = [
       title: 'Ressource',
     },
   },
+  {
+    path: 'formats',
+    component: FormatsComponent,
+    data: {
+      title: 'Formats',
+    },
+  },
+  {
+    path: 'formats/new',
+    component: FormatFormComponent,
+    data: {
+      title: 'Nouveau format',
+    },
+  },
+  {
+    path: 'formats/:id/edit',
+    component: FormatFormComponent,
+    data: {
+      title: 'Modifier le format',
+    },
+  },
+  {
+    path: 'formats/:id',
+    component: FormatDetailComponent,
+    data: {
+      title: 'Format',
+    },
+  },
 ];
 
 @NgModule({
diff --git a/src/app/components/back-button/back-button.component.html b/src/app/components/back-button/back-button.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..64c049e03ec9b0b68670c52df1dfe65aa8dbe544
--- /dev/null
+++ b/src/app/components/back-button/back-button.component.html
@@ -0,0 +1,8 @@
+<div class="back-button">
+    <a [routerLink]="route" [title]="title">
+      <span class="icon is-medium">
+        <i class="fas fa-arrow-left"></i>
+      </span>
+      Retour
+    </a>
+</div>
diff --git a/src/app/components/back-button/back-button.component.scss b/src/app/components/back-button/back-button.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..e477cc426f1ad8b385c0b434647395db2399529a
--- /dev/null
+++ b/src/app/components/back-button/back-button.component.scss
@@ -0,0 +1,14 @@
+.back-button {
+  a {
+    color: unset; // Remove default link color
+    display: flex;
+    align-items: center;
+  }
+  padding: 1rem 1.5rem;
+  a:hover {
+    color: #d5232a;
+    span, a {
+      color: #d5232a;
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/app/components/back-button/back-button.component.ts b/src/app/components/back-button/back-button.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..cc592862d5aa1e1a21ace689ea34303c7ba33bef
--- /dev/null
+++ b/src/app/components/back-button/back-button.component.ts
@@ -0,0 +1,18 @@
+import { Component, OnInit, Input } from '@angular/core';
+
+@Component({
+  selector: 'app-back-button',
+  templateUrl: './back-button.component.html',
+  styleUrls: ['./back-button.component.scss'],
+})
+export class BackButtonComponent implements OnInit {
+
+  @Input() route: string;
+  @Input() title: string;
+
+  constructor() { }
+
+  ngOnInit() {
+  }
+
+}
diff --git a/src/app/components/crud-buttons/crud-buttons.component.html b/src/app/components/crud-buttons/crud-buttons.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..d81c63b54d8e4ffbbc9f902e0111589b4ee96257
--- /dev/null
+++ b/src/app/components/crud-buttons/crud-buttons.component.html
@@ -0,0 +1,11 @@
+<div class="crud-buttons-container">
+  <span class="icon">
+    <a [routerLink]="[id]"><i class="fas fa-eye"></i></a>
+  </span>
+  <span class="icon">
+    <a [routerLink]="[id, 'edit']"> <i class="fas fa-edit"></i></a>
+  </span>
+  <span class="icon has-text-danger" (click)="onDelete()">
+    <a> <i class="fas fa-trash"></i></a>
+  </span>
+</div>
diff --git a/src/app/components/crud-buttons/crud-buttons.component.scss b/src/app/components/crud-buttons/crud-buttons.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..327d4a70a9de5587a9bca70f677c69b904e890f1
--- /dev/null
+++ b/src/app/components/crud-buttons/crud-buttons.component.scss
@@ -0,0 +1,23 @@
+.crud-buttons-container {
+  display: flex;
+  align-items: center;
+  justify-content: space-around;
+}
+
+.icon {
+  cursor: pointer;
+
+  i {
+    color: #4a4a4a;
+  }
+
+  &:hover {
+    .fa-eye, .fa-edit {
+      color: blue;
+    }
+
+    .fa-trash {
+      color: #d5232a;
+    }
+  }
+}
diff --git a/src/app/components/crud-buttons/crud-buttons.component.ts b/src/app/components/crud-buttons/crud-buttons.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e2c660fee6700844e460b114711754ddc473b7ae
--- /dev/null
+++ b/src/app/components/crud-buttons/crud-buttons.component.ts
@@ -0,0 +1,23 @@
+import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
+import { identifierModuleUrl } from '@angular/compiler';
+
+@Component({
+  selector: 'app-crud-buttons',
+  templateUrl: './crud-buttons.component.html',
+  styleUrls: ['./crud-buttons.component.scss'],
+})
+export class CrudButtonsComponent implements OnInit {
+
+  @Output() delete = new EventEmitter<string>();
+  @Input() id;
+
+  constructor() { }
+
+  ngOnInit() {
+  }
+
+  onDelete() {
+    this.delete.emit(this.id);
+  }
+
+}
diff --git a/src/app/components/formats/detail/format-detail.component.html b/src/app/components/formats/detail/format-detail.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..064569e10d7d0053978eb236b6998d0667cefdc2
--- /dev/null
+++ b/src/app/components/formats/detail/format-detail.component.html
@@ -0,0 +1,30 @@
+<ng-container *ngIf="format">
+
+  <app-back-button [route]="'/formats'" [title]="'Retourner à la liste des formats'"></app-back-button>
+
+  <section class="section">
+    <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">
+              {{format.name}}
+            </p>
+          </header>
+          <div class="card-content">
+            <div class="content">
+              <p>
+                <span class="has-text-weight-bold">Id: </span>
+                <span>{{format.id}}</span>
+              </p>
+              <p>
+                <span class="has-text-weight-bold">Type MapServer: </span>
+                <span>{{format.mapServerType}}</span>
+              </p>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </section>
+</ng-container>
\ No newline at end of file
diff --git a/src/app/components/formats/detail/format-detail.component.scss b/src/app/components/formats/detail/format-detail.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..163f99f474dc5696a1670be5273eef97a2efbd39
--- /dev/null
+++ b/src/app/components/formats/detail/format-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/formats/detail/format-detail.component.ts b/src/app/components/formats/detail/format-detail.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..52b8bf8ce53e15d473d61a1639262df97609b87b
--- /dev/null
+++ b/src/app/components/formats/detail/format-detail.component.ts
@@ -0,0 +1,28 @@
+
+import { switchMap } from 'rxjs/operators';
+import { Component, OnInit } from '@angular/core';
+import { ActivatedRoute, ParamMap } from '@angular/router';
+import { FormatService } from 'src/app/services';
+import { Format } from 'src/app/models/format.model';
+
+@Component({
+  selector: 'app-format-detail',
+  templateUrl: './format-detail.component.html',
+  styleUrls: ['./format-detail.component.scss'],
+})
+export class FormatDetailComponent implements OnInit {
+
+  format: Format;
+
+  constructor(
+    private _route: ActivatedRoute,
+    private _formatService: FormatService,
+  ) {
+  }
+
+  ngOnInit(): void {
+    this._route.paramMap.pipe(
+      switchMap((params: ParamMap) => this._formatService.findById(params.get('id'))))
+      .subscribe((format: Format) => this.format = format);
+  }
+}
diff --git a/src/app/components/formats/edit/format-form.component.html b/src/app/components/formats/edit/format-form.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..dc9588a857641d4e0dcaff8b6343ca0fc2d66df4
--- /dev/null
+++ b/src/app/components/formats/edit/format-form.component.html
@@ -0,0 +1,41 @@
+<ng-container *ngIf="format">
+
+  <app-back-button [route]="'/formats'" [title]="'Retourner à la liste des formats'"></app-back-button>
+
+  <h1>{{ title }}</h1>
+
+  <form [formGroup]="form" (ngSubmit)="onSubmit()" class="columns is-centered is-marginless">
+    <div class="column is-7">
+      <input type="hidden" formControlName="id" value="{{format.id}}">
+
+      <div class="field">
+        <label class="label required" for="name">Nom</label>
+        <div class="control">
+          <input class="input" type="text" [value]="format.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 format est obligatoire.
+          </p>
+        </div>
+      </div>
+
+      <div class="field">
+        <label class="label required" for="mapServerType">Type MapServer</label>
+        <div class="control">
+          <input class="input" type="text" [value]="format.mapServerType" formControlName="mapServerType"
+            id="mapServerType">
+        </div>
+        <div *ngIf="mapServerType.invalid && (mapServerType.dirty || mapServerType.touched)" class="alert alert-danger">
+          <p *ngIf="mapServerType.errors['required']" class="help is-danger">
+            Le nom du type MapServer est obligatoire.
+          </p>
+        </div>
+      </div>
+
+      <div class="has-text-right">
+        <button class="button button-gl" type="submit" [disabled]="formInvalid == true">Valider</button>
+      </div>
+    </div>
+  </form>
+</ng-container>
\ No newline at end of file
diff --git a/src/app/components/formats/edit/format-form.component.scss b/src/app/components/formats/edit/format-form.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..5bea26fe55b4ab7bb07da39d6140df0c988864d4
--- /dev/null
+++ b/src/app/components/formats/edit/format-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/formats/edit/format-form.component.ts b/src/app/components/formats/edit/format-form.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..73665239d0f197242529f11b22649a127bd5d4e8
--- /dev/null
+++ b/src/app/components/formats/edit/format-form.component.ts
@@ -0,0 +1,78 @@
+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 { Format } from 'src/app/models/format.model';
+import { FormatService } from 'src/app/services';
+
+@Component({
+  selector: 'app-format-form',
+  templateUrl: './format-form.component.html',
+  styleUrls: ['./format-form.component.scss'],
+})
+export class FormatFormComponent implements OnInit {
+
+  format: Format = new Format();
+  form: FormGroup;
+  title: string;
+
+  constructor(
+    private _formatService: FormatService,
+    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._formatService.findById(paramMap.get('id'))))
+      .subscribe((format: Format) => {
+
+        this.format = format;
+
+        this.initForm();
+
+      });
+  }
+
+  initForm() {
+    this.form = this._fb.group({
+      id: [this.format.id],
+      name: [this.format.name, Validators.required],
+      mapServerType: [this.format.mapServerType, Validators.required],
+    });
+  }
+
+  onSubmit() {
+    if (!this.formInvalid) {
+      this.format = new Format(this.form.value);
+      this._formatService.replaceOrCreate(this.format)
+        .subscribe(
+          (formatCreated) => {
+            this._router.navigate(['/formats', formatCreated.id]);
+          },
+          (err) => {
+            alert(err.message);
+          },
+        );
+    }
+  }
+
+  // Getters for each property
+  get name() {
+    return this.form.controls['name'];
+  }
+
+  get mapServerType() {
+    return this.form.controls['mapServerType'];
+  }
+
+  get formInvalid() {
+    return this.form.invalid;
+  }
+}
diff --git a/src/app/components/formats/list/formats.component.html b/src/app/components/formats/list/formats.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..9bb68d7be561e6f54b47659456ac6e85f63b8a16
--- /dev/null
+++ b/src/app/components/formats/list/formats.component.html
@@ -0,0 +1,59 @@
+<ng-container *ngIf="formats">
+  <div>
+    <div class="section">
+      <div class="columns is-centered is-marginless">
+        <div class="column has-text-centered">
+          <h2>{{ totalElement }} formats trouvés</h2>
+        </div>
+      </div>
+      <div class="add-item has-text-right">
+        <a class="button button-gl" [routerLink]="['new']">
+          Ajouter
+        </a>
+      </div>
+      <div class="table">
+        <div class="header columns is-marginless">
+          <div class="column is-2 has-text-centered">
+            <span (click)="sortBy('name')" class="is-sortable">
+              <span class="column-title" [ngClass]="{'active': sortOptions.value === name}">Name</span>
+              <span *ngIf="sortOptions.value  === 'name'" class="has-text-danger">
+                <i class="fas sort-order-icon"
+                  [ngClass]="{'fa-arrow-up': sortOptions.order === 'asc', 'fa-arrow-down': sortOptions.order === 'desc'}"></i>
+              </span>
+            </span>
+          </div>
+          <div class="column is-2 has-text-centered">
+            <span class="column-title">Type MapServer</span>
+          </div>
+          <div class="column is-offset-7 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 format of formats; let i=index; let odd=odd; let even=even;"
+            [ngClass]="{ odd: odd, even: even }">
+            <div class="column is-2 has-text-centered">
+              {{ format.name}}
+            </div>
+            <div class="column is-2 has-text-centered">
+              {{ format.mapServerType}}
+            </div>
+            <div class="column is-offset-7 is-1 has-text-centered actions"> 
+                <app-crud-buttons [id]="format.id" (delete)="displayDeletePopup($event)"></app-crud-buttons>
+            </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>
+</ng-container>
diff --git a/src/app/components/formats/list/formats.component.scss b/src/app/components/formats/list/formats.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..087d2f4242ed39d8f3505d06012bf823f015572e
--- /dev/null
+++ b/src/app/components/formats/list/formats.component.scss
@@ -0,0 +1,53 @@
+section {
+  padding: 20px;
+}
+.section {
+  padding-top: 0;
+}
+
+.table {
+  background-color: white;
+  border: 1px solid lightgray;
+}
+
+.center {
+  text-align: center;
+}
+
+img {
+  max-width: 100px;
+}
+
+.header {
+  border-bottom: 1px solid lightgray;
+  background-color: #fafafa;
+  span {
+    color: #999;
+
+  }
+}
+
+.is-sortable .column-title {
+  cursor: pointer;
+}
+
+.table .columns {
+  border-bottom: 1px solid lightgray
+}
+
+i {
+
+  &.title-icon {
+    margin-right: 0.75rem;
+  }
+
+  &.sort-order-icon {
+    padding-left: 0.75rem;
+    cursor: pointer;
+  }
+}
+
+
+.add-item {
+  margin-bottom: 20px;
+}
diff --git a/src/app/components/formats/list/formats.component.ts b/src/app/components/formats/list/formats.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0efba29653deeaa91189cc967d4cfe9a8edea5bb
--- /dev/null
+++ b/src/app/components/formats/list/formats.component.ts
@@ -0,0 +1,99 @@
+import { Component, OnInit } 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';
+
+@Component({
+  selector: 'app-formats',
+  templateUrl: './formats.component.html',
+  styleUrls: ['./formats.component.scss'],
+})
+export class FormatsComponent implements OnInit {
+
+  formats: Format[];
+  searchChangeSub: Subscription;
+
+  // Paginator options
+  paginator: PaginatorOptions;
+
+  sortValue: string;
+
+  totalElement: number;
+  pageSize = 10;
+  pageSizeOptions = [5, 10, 25, 100];
+  filters = {
+    name: '',
+  };
+  where = {};
+
+  constructor(
+    private formatsService: FormatService,
+  ) {
+    this.paginator = {
+      pageIndex: this.formatsService.pageNumber,
+      length: 0,
+      limit: this.formatsService.limit,
+      pageSizeOptions: [5, 10, 20],
+    };
+  }
+
+  ngOnInit(): void {
+    this.formatsService.sortOptions = {
+      value: 'name',
+      order: 'asc',
+    };
+    this.search();
+
+    this.searchChangeSub = this.formatsService.searchChange$.subscribe(
+      () => {
+        this.search();
+      },
+    );
+  }
+
+  private search() {
+    this.formatsService.getFormats()
+      .subscribe((items: FormatRO) => {
+        this.formats = items.formats;
+        this.totalElement = items.totalCount;
+
+        this.paginator.limit = this.formatsService.limit;
+        this.paginator.pageIndex = this.formatsService.pageNumber;
+        this.paginator.length = items.totalCount;
+      });
+  }
+
+  // When pagination is changed by user, we update datasetList with new pagination options
+  changePagination(pageIndex) {
+    this.formatsService.paginationChanged(this.paginator.limit, pageIndex);
+  }
+
+  changePageSize(pageSize) {
+    this.formatsService.paginationChanged(pageSize, 1);
+  }
+
+  sortBy(key: string) {
+    if (this.formatsService.sortOptions.value === key) {
+      this.formatsService.reverseSortOrder();
+    } else {
+      this.formatsService.sortOptions.value = key;
+      this.formatsService.sortOptions.order = 'asc';
+    }
+    this.search();
+  }
+
+  get sortOptions() {
+    return this.formatsService.sortOptions;
+  }
+
+  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;
+        this.search();
+      });
+    }
+  }
+}
diff --git a/src/app/components/image-upload/image-upload.component.html b/src/app/components/image-upload/image-upload.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..72e3400a5b9ffda47784a569bdcaca09f18d99ff
--- /dev/null
+++ b/src/app/components/image-upload/image-upload.component.html
@@ -0,0 +1,11 @@
+<div class="field">
+  <label class="label" [for]="fieldParams.inputName">{{ fieldParams.label }}</label>
+  <div class="image-preview-container" *ngIf="fieldParams.existingImageUrl && !removeImageBtnClicked">
+    <button class="button" (click)="removeImage()"><i class="far fa-times-circle"></i></button>
+    <img [src]="fieldParams.existingImageUrl" alt="" class="image-preview">
+  </div>
+  <div class="control">
+    <input *ngIf="!fieldParams.existingImageUrl || removeImageBtnClicked" class="input" type="file"
+      [name]="fieldParams.inputName" [id]="fieldParams.inputName" (change)="setFile($event)">
+  </div>
+</div>
\ No newline at end of file
diff --git a/src/app/components/image-upload/image-upload.component.scss b/src/app/components/image-upload/image-upload.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..d4a9d5613206de3aa75a786ce5adcc2bb26cfa48
--- /dev/null
+++ b/src/app/components/image-upload/image-upload.component.scss
@@ -0,0 +1,17 @@
+.image-preview-container {
+  display: inline-block;
+  position: relative;
+  max-height: 5rem;
+}
+
+button {
+  position: absolute;
+  top: 0;
+  right: 0;
+  border: none;
+  background: transparent;
+}
+
+.field {
+  margin-bottom: 0.75rem;
+}
diff --git a/src/app/components/image-upload/image-upload.component.ts b/src/app/components/image-upload/image-upload.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..476ad0fffdc9e7b2634d8def54e4c4d9e4826780
--- /dev/null
+++ b/src/app/components/image-upload/image-upload.component.ts
@@ -0,0 +1,35 @@
+import { Component, OnInit, Input, EventEmitter, Output } from '@angular/core';
+import { IImageUploadFieldParams } from 'src/app/models/image-upload.model';
+
+@Component({
+  selector: 'app-image-upload',
+  templateUrl: './image-upload.component.html',
+  styleUrls: ['./image-upload.component.scss']
+})
+export class ImageUploadComponent implements OnInit {
+
+  removeImageBtnClicked: boolean = false;
+
+  @Input() fieldParams: IImageUploadFieldParams;
+  @Output() fileChanged = new EventEmitter<FileList>();
+  @Output() imageRemoved = new EventEmitter<boolean>();
+
+  constructor() { }
+
+  ngOnInit() {
+  }
+
+  setFile(event) {
+    const files = event.srcElement.files;
+    if (!files) {
+      return;
+    }
+
+    this.fileChanged.emit(files);
+  }
+
+  removeImage() {
+    this.removeImageBtnClicked = true;
+    this.imageRemoved.emit(true);
+  }
+}
diff --git a/src/app/components/index.ts b/src/app/components/index.ts
index c1207017b83f5bfc5cfb9855c460632a50d73a78..5b89ff87ce224523dc22b61a682d4166c34024d6 100644
--- a/src/app/components/index.ts
+++ b/src/app/components/index.ts
@@ -7,6 +7,13 @@ import { ResourceDetailComponent } from './resources/detail/resource-detail.comp
 import { ResourceFormComponent } from './resources/edit/resource-form.component';
 import { ResourcesComponent } from './resources/list/resources.component';
 import { WelcomeComponent } from './welcome/welcome.component';
+import { BackButtonComponent } from './back-button/back-button.component';
+import { CrudButtonsComponent } from './crud-buttons/crud-buttons.component';
+import { FormatsComponent } from './formats/list/formats.component';
+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';
 
 export {
   MenuComponent,
@@ -18,6 +25,13 @@ export {
   ResourceFormComponent,
   ResourcesComponent,
   WelcomeComponent,
+  BackButtonComponent,
+  CrudButtonsComponent,
+  FormatsComponent,
+  FormatDetailComponent,
+  FormatFormComponent,
+  ImageUploadComponent,
+  NotificationsComponent,
 };
 
 // tslint:disable-next-line:variable-name
@@ -31,4 +45,11 @@ export const AppComponents = [
   ResourceFormComponent,
   ResourcesComponent,
   WelcomeComponent,
+  BackButtonComponent,
+  CrudButtonsComponent,
+  FormatsComponent,
+  FormatDetailComponent,
+  FormatFormComponent,
+  ImageUploadComponent,
+  NotificationsComponent,
 ];
diff --git a/src/app/components/menu/menu.component.html b/src/app/components/menu/menu.component.html
index 63c9727b0315c031bea447c498e166b48b913e8a..5652d6a7c9467e4a64f59bbcf700726943dbb027 100644
--- a/src/app/components/menu/menu.component.html
+++ b/src/app/components/menu/menu.component.html
@@ -22,5 +22,12 @@
       <span class="label-menu">Ressources</span> 
       </a>
     </li>
+    <li><a [routerLink]="['/', 'formats']" routerLinkActive="active-link">
+      <span class="icon">
+          <i class="far fa-file"></i>
+        </span>
+      <span class="label-menu">Formats</span> 
+      </a>
+    </li>
   </ul>
 </aside>
diff --git a/src/app/components/menu/menu.component.scss b/src/app/components/menu/menu.component.scss
index c00f4bee1b2f45e079bb923d6cf96ea308b37dcd..a3172b6b6b674406c5a483a2355a059682eae417 100644
--- a/src/app/components/menu/menu.component.scss
+++ b/src/app/components/menu/menu.component.scss
@@ -40,6 +40,8 @@
 
 li a {
   border-left: 3px solid #333745;
+  padding-left: calc(0.75em - 3px) ;
+  
   &.active-link {
     border-color:#d5232a;
   }
diff --git a/src/app/components/notifications/notifications.component.html b/src/app/components/notifications/notifications.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..8c0b6dbd5b8e3ca86bb68010e18fbe0c8b448a92
--- /dev/null
+++ b/src/app/components/notifications/notifications.component.html
@@ -0,0 +1,21 @@
+<div *ngIf="notifications.length > 0" class="app-notifications">
+  <div *ngFor="let notification of notifications; index as i" class="app-notification" [class.success]="notification.type === 'success'"
+    [class.warning]="notification.type === 'warning'" [class.error]="notification.type === 'error'">
+    <ng-container [ngSwitch]="notification.type">
+      <div class="app-notification-type" *ngSwitchCase="'success'">
+        <img src="./assets/img/notif_ok.svg" alt="Ok icon">
+        <span>Ok!</span>
+      </div>
+      <div class="app-notification-type" *ngSwitchCase="'warning'">
+        <img src="./assets/img/notif_warning.svg" alt="Warning icon">
+        <span i18n="@@notification.oops">Oops!</span>
+      </div>
+      <div class="app-notification-type" *ngSwitchCase="'error'">
+        <img src="./assets/img/notif_error.svg" alt="Error icon">
+        <span i18n="@@notification.ouch">Ouch!</span>
+      </div>
+    </ng-container>
+    <p class="app-notification-content">{{ notification.message }}</p>
+    <button class="close-button" (click)="hide(i)"><i class="fas fa-times"></i></button>
+  </div>
+</div>
diff --git a/src/app/components/notifications/notifications.component.scss b/src/app/components/notifications/notifications.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..2c212c343bd7e3ad437add33026feaba335856b9
--- /dev/null
+++ b/src/app/components/notifications/notifications.component.scss
@@ -0,0 +1,118 @@
+@import '../../../../node_modules/bulma/sass/utilities/_all.sass';
+
+$app-notification-width: 29rem;
+
+.app-notifications {
+  position: fixed;
+  z-index: 10;
+  bottom: 0.5rem;
+  right: 0.5rem;
+
+  @media screen and (max-width: $tablet) {
+    bottom: 0;
+    right: 0;
+    width: 100%;
+  }
+}
+
+.app-notification {
+  position: relative;
+  min-width: $app-notification-width;
+  max-width: $app-notification-width;
+  padding: 1rem;
+  margin-top: 0.5rem;
+  border: 2px solid white;
+  border-radius: 4px;
+
+  @media screen and (max-width: $tablet) {
+    min-width: 100%;
+    max-width: 100%;
+  }
+
+  // Adding slideFromRight animation on notification classe
+  // The div come from the right and go back after a while
+  // The animation lasts 5s and is runned only one time
+  animation: slideFromRight 8s 1;
+
+  @media screen and (max-width: $tablet) {
+    animation: slideFromRightMobile 8s 1;
+  }
+
+  span,
+  p,
+  button {
+    color: white;
+  }
+
+  div.app-notification-type {
+    display: flex;
+    margin-bottom: 0.75rem;
+
+    img {
+      width: 1.125rem;
+      height: 1.125rem;
+      margin-right: 0.5rem;
+    }
+
+    span {
+      font-size: 1rem;
+      font-weight: bold;
+    }
+  }
+
+  p.app-notification-content {
+    font-size: 0.875rem;
+  }
+}
+
+.success {
+  background-color: rgba(84, 180, 51, 0.9);
+}
+
+.warning {
+  background-color: rgba(239, 114, 23, 0.9);
+}
+
+.error {
+  background-color: rgba(217, 3, 3, 0.9);
+}
+
+.close-button {
+  position: absolute;
+  top: 0;
+  right: 0;
+  border: none;
+  background-color: transparent;
+  cursor: pointer;
+}
+
+// Animation
+@keyframes slideFromRight {
+  0% {
+    transform: translateX($app-notification-width);
+  }
+
+  15%,
+  85% {
+    transform: translateX(0);
+  }
+
+  100% {
+    transform: translateX($app-notification-width);
+  }
+}
+
+@keyframes slideFromRightMobile {
+  0% {
+    transform: translateX(100%);
+  }
+
+  15%,
+  85% {
+    transform: translateX(0);
+  }
+
+  100% {
+    transform: translateX(100%);
+  }
+}
diff --git a/src/app/components/notifications/notifications.component.ts b/src/app/components/notifications/notifications.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..cc9bf93d5aa81ec68fc9e804b58fb67ce506e34c
--- /dev/null
+++ b/src/app/components/notifications/notifications.component.ts
@@ -0,0 +1,34 @@
+import { Component, OnInit } from '@angular/core';
+import { NotificationService } from '../../services';
+import { Notification } from '../../models/notification.model';
+
+@Component({
+  selector: 'app-notifications',
+  templateUrl: './notifications.component.html',
+  styleUrls: ['./notifications.component.scss'],
+})
+export class NotificationsComponent implements OnInit {
+  notifications: Notification[] = [];
+
+  constructor(
+    private _notificationService: NotificationService,
+  ) { }
+
+  ngOnInit() {
+    this._notificationService.notification$.subscribe(
+      (notification) => {
+        if (notification) {
+          this.notifications.push(notification);
+          // Remove notification from array after 5 seconds
+          // IMPORTANT (if you change this value also change the value of the css animation)
+          setTimeout(() => this.notifications.shift(), 8000);
+        }
+      },
+    );
+  }
+
+  hide(index) {
+    this.notifications.splice(index, 1);
+  }
+
+}
diff --git a/src/app/components/organizations/detail/organization-detail.component.html b/src/app/components/organizations/detail/organization-detail.component.html
index cd7687506be21813e2a2d2a9c411fde647abc0d6..3d17686ee4cdaecaee0b39828686063c00b39507 100644
--- a/src/app/components/organizations/detail/organization-detail.component.html
+++ b/src/app/components/organizations/detail/organization-detail.component.html
@@ -1,14 +1,6 @@
 <ng-container *ngIf="organization">
 
-  <div class="back-button">
-    <a routerLink="/organizations" title="Retourner à la liste des organisations">
-      <span class="icon is-medium">
-        <i class="fas fa-arrow-left fa-lg"></i>
-      </span>
-      Retour
-    </a>
-  </div>
-
+  <app-back-button [route]="'/organizations'" [title]="'Retourner à la liste des organisations'"></app-back-button>
 
   <section class="section">
     <div class="columns is-centered">
@@ -21,14 +13,18 @@
           </header>
           <div class="card-image">
             <figure class="image">
-              <img src="{{organization.logo}}" alt="Logo de l'organization">
+              <img [src]="organization.logo" alt="Logo de l'organization">
             </figure>
           </div>
           <div class="card-content">
 
             <div class="content">
               <div>
-                  {{organization.description}}
+                <p>{{organization.description}}</p>
+              </div>
+              <br>
+              <div>
+                <p><span class="has-text-weight-bold">Id:</span> {{ organization.id}}</p>
               </div>
               <br>
               <div>
@@ -39,10 +35,10 @@
               <div>
                 <p class="has-text-weight-bold">Liens</p>
                 <p *ngFor="let link of organization.links">
-                    <a href="{{ link.url }}" target="_blank">{{link.url}}</a>
-                  </p>
+                  <a href="{{ link.url }}" target="_blank">{{link.url}}</a>
+                </p>
               </div>
-              
+
             </div>
           </div>
         </div>
@@ -53,4 +49,4 @@
 
   </section>
 
-</ng-container>
+</ng-container>
\ No newline at end of file
diff --git a/src/app/components/organizations/detail/organization-detail.component.scss b/src/app/components/organizations/detail/organization-detail.component.scss
index f7cc6fc699db9733d186dc7455c50eb990a195b9..dff286c3a01ee11891ef45a0378e9fa7e35f4172 100644
--- a/src/app/components/organizations/detail/organization-detail.component.scss
+++ b/src/app/components/organizations/detail/organization-detail.component.scss
@@ -1,17 +1,3 @@
-
-.back-button {
-  a {
-    color: unset; // Remove default link color
-  }
-  padding: 1rem 1.5rem;
-  a:hover {
-    color: #d5232a;
-    span, a {
-      color: #d5232a;
-    }
-  }
-}
-
 figure {
   text-align: center;
 }
diff --git a/src/app/components/organizations/edit/organization-form.component.html b/src/app/components/organizations/edit/organization-form.component.html
index a3639683bfa080bf2df4a0109d57c7fb0fd917b5..d00a3415c96010844093813d85249076880e9878 100644
--- a/src/app/components/organizations/edit/organization-form.component.html
+++ b/src/app/components/organizations/edit/organization-form.component.html
@@ -1,13 +1,6 @@
 <ng-container *ngIf="organization">
 
-  <div class="back-button">
-    <a routerLink="/organizations" title="Retourner à la liste des organisations"> 
-      <span class="icon is-medium">
-        <i class="fas fa-arrow-left fa-lg"></i>
-      </span>
-      Retour
-    </a>
-  </div>
+  <app-back-button [route]="'/organizations'" [title]="'Retourner à la liste des organisations'"></app-back-button>
 
   <h1>{{ title }}</h1>
 
@@ -27,12 +20,9 @@
         </div>
       </div>
 
-      <div class="field">
-        <label class="label" for="logo">Logo</label>
-        <div class="control">
-          <input class="input" type="text" [value]="organization.logo" formControlName="logo" id="logo">
-        </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</label>
@@ -51,7 +41,8 @@
       <div class="field">
         <label class="label" for="elasticSearchName">Nom ElasticSearch de l'organisation</label>
         <div class="control">
-          <input class="input" type="text" [value]="organization.elasticSearchName" formControlName="elasticSearchName" id="elasticSearchName">
+          <input class="input" type="text" [value]="organization.elasticSearchName" formControlName="elasticSearchName"
+            id="elasticSearchName">
         </div>
       </div>
 
@@ -69,13 +60,13 @@
 
         <div formArrayName="links">
           <div *ngFor="let link of formLinks.controls; let i = index;" [formGroupName]="i"
-           class="columns is-multiline field">
+            class="columns is-multiline field">
             <div class="column is-11">
               <div class="control">
-                  <input class="input" type="text" formControlName="url" required>
+                <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">
+              <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>
@@ -90,15 +81,10 @@
           </div>
         </div>
       </div>
-
-      <div class="field">
-        <div class="control">
-          <button class="button validate" [disabled]="formInvalid == true">Valider</button>
-        </div>
+      <br>
+      <div class="has-text-right">
+        <button class="button button-gl" type="submit" [disabled]="formInvalid == true">Valider</button>
       </div>
-
     </div>
   </form>
-
-
-</ng-container>
+</ng-container>
\ No newline at end of file
diff --git a/src/app/components/organizations/edit/organization-form.component.scss b/src/app/components/organizations/edit/organization-form.component.scss
index 8522a9ab88fb48e0ae39d622940fa851978291f8..5bea26fe55b4ab7bb07da39d6140df0c988864d4 100644
--- a/src/app/components/organizations/edit/organization-form.component.scss
+++ b/src/app/components/organizations/edit/organization-form.component.scss
@@ -16,22 +16,4 @@ h1 {
       color: #d5232a;
     }
   }
-}
-
-.validate {
-  background-color: #168f48;
-  color: white;
-}
-
-.back-button {
-  a {
-    color: unset; // Remove default link color
-  }
-  padding: 1rem 1.5rem;
-  a:hover {
-    color: #d5232a;
-    span, a {
-      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 3c2ce77e6618f345bdf34710f8783202e94f06bc..6952b5acf2765a670c81d8a822bc13353d8917a8 100644
--- a/src/app/components/organizations/edit/organization-form.component.ts
+++ b/src/app/components/organizations/edit/organization-form.component.ts
@@ -3,7 +3,9 @@ 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 { tap, filter, switchMap } from 'rxjs/operators';
+import { filter, switchMap, mergeMap } from 'rxjs/operators';
+import { IImageUploadFieldParams } from 'src/app/models/image-upload.model';
+import { NotificationService } from 'src/app/services';
 
 @Component({
   selector: 'app-organization-form',
@@ -14,45 +16,54 @@ export class OrganizationFormComponent implements OnInit {
 
   organization: Organization;
   form: FormGroup;
+  logoFile: File;
+  logoFieldParams: IImageUploadFieldParams = {
+    inputName: 'logo',
+    label: 'Logo',
+    existingImageUrl: null,
+  };
+  logo: File;
   title: string;
 
   constructor(
-    private organizationService: OrganizationService,
-    private route: ActivatedRoute,
-    private router: Router,
+    private _organizationService: OrganizationService,
+    private _notificationService: NotificationService,
+    private _route: ActivatedRoute,
+    private _router: Router,
     private _fb: FormBuilder,
   ) {
   }
 
   ngOnInit() {
-    this.title = this.route.snapshot.data.title;
+    this.title = this._route.snapshot.data.title;
     this.initForm();
 
-    this.route.paramMap.pipe(
+    this._route.paramMap.pipe(
       filter((paramMap: ParamMap) => (paramMap.get('id') !== null)),
-      switchMap((paramMap: ParamMap) => this.organizationService.findById(paramMap.get('id'))))
+      switchMap((paramMap: ParamMap) => this._organizationService.findById(paramMap.get('id'))))
       .subscribe((organization: Organization) => {
 
         this.organization = organization;
 
         const arr = new FormArray([]);
-        this.organization.links.forEach(y => {
+        this.organization.links.forEach((y) => {
           arr.push(this._fb.group({
             url: y.url,
             organizationId: y.organizationId,
           }));
         });
 
+        this.logoFieldParams.existingImageUrl = organization.logo;
+
         this.form = this._fb.group(
           {
             id: [this.organization.id],
             name: [organization.name, Validators.required],
             description: [organization.description, Validators.required],
-            logo: [organization.logo],
             elasticSearchName: [organization.elasticSearchName],
+            logo: [organization.logo],
             links: arr,
           });
-
       });
 
   }
@@ -60,13 +71,14 @@ export class OrganizationFormComponent implements OnInit {
   initForm() {
     this.organization = new Organization();
     const arr = new FormArray([]);
+    this.logoFieldParams.existingImageUrl = null;
     this.form = this._fb.group(
       {
         id: [this.organization.id],
         name: [this.organization.name, Validators.required],
         description: [this.organization.description, Validators.required],
-        logo: [this.organization.logo],
         elasticSearchName: [this.organization.elasticSearchName],
+        logo: [this.organization.logo],
         links: arr,
       });
   }
@@ -83,9 +95,12 @@ export class OrganizationFormComponent implements OnInit {
       this.organization.links = [];
     }
     (this.form.controls.links as FormArray).push(this._fb.group(
-      { id: null, name: '',
-      url: ['', Validators.required],
-      organizationId: this.organization.id }));
+      {
+        id: null, name: '',
+        url: ['', Validators.required],
+        organizationId: this.organization.id,
+      }),
+    );
   }
 
   removeLink(index) {
@@ -97,19 +112,31 @@ export class OrganizationFormComponent implements OnInit {
   }
 
   onSubmit() {
-    if (! this.formInvalid) {
+    if (!this.formInvalid) {
       this.organization = new Organization(this.form.value);
-      this.organizationService.replaceOrCreate(this.organization)
-        .subscribe(
-          (organizationCreated) => {
-            this.router.navigate(['/organizations', organizationCreated.id])
-              .then(() => {
-              });
-          },
-          (err) => {
-            alert(err.message);
-          },
-        );
+      let action;
+
+      if (this.logoFile) {
+        action = this._organizationService.uploadLogoAndSaveOrganization(this.logoFile, this.organization);
+      } else {
+        action = this._organizationService.replaceOrCreate(this.organization);
+      }
+
+      action.subscribe(
+        (organizationCreated) => {
+          this._notificationService.notify({
+            message: 'L\'organisation a été créée avec succès.',
+            type: 'success',
+          });
+          this._router.navigate(['/organizations', organizationCreated.id]);
+        },
+        () => {
+          this._notificationService.notify({
+            message: 'Une erreur est survenue lors de la création de l\'organisation.',
+            type: 'error',
+          });
+        },
+      );
     }
   }
 
@@ -123,6 +150,17 @@ export class OrganizationFormComponent implements OnInit {
   }
 
   get formInvalid() {
-    return this.form.invalid;
+    return this.form.invalid ||
+      (!this.organization.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/organizations/list/organizations.component.html b/src/app/components/organizations/list/organizations.component.html
index e0c22f35203ad5145dfa7dd76d09f0cad0fbde41..cd06cbfebf61d7545861c6e923a256096f5ff6e3 100644
--- a/src/app/components/organizations/list/organizations.component.html
+++ b/src/app/components/organizations/list/organizations.component.html
@@ -6,21 +6,13 @@
           <h2>{{ totalElement }} organisations trouvées</h2>
         </div>
       </div>
-      <div class="add-item">
-        <a class="button is-link" [routerLink]="['new']">
+      <div class="add-item has-text-right">
+        <a class="button button-gl" [routerLink]="['new']">
           Ajouter
         </a>
       </div>
       <div class="table">
           <div class="header columns is-marginless">
-              <div class="column is-1 has-text-centered">
-                <span (click)="sortBy('id')" class="is-sortable">
-                  <span class="column-title" [ngClass]="{'active': sortOptions.value === 'id'}">Id</span>
-                  <span *ngIf="sortOptions.value === 'id'" class="has-text-danger">
-                    <i class="fas sort-order-icon" [ngClass]="{'fa-arrow-up': sortOptions.order === 'asc', 'fa-arrow-down': sortOptions.order === 'desc'}"></i>
-                  </span>
-                </span>
-              </div>
               <div class="column is-2 has-text-centered">
                 <span (click)="sortBy('name')" class="is-sortable">
                   <span class="column-title" [ngClass]="{'active': sortOptions.value === name}">Name</span>
@@ -35,7 +27,7 @@
               <div class="column is-4 has-text-centered">
                 <span class="column-title">Description</span>
               </div>
-              <div class="column is-2 has-text-centered">
+              <div class="column is-3 has-text-centered">
                 <span class="column-title">Liens</span>
               </div>
               <div class="column is-1 has-text-centered">
@@ -46,9 +38,6 @@
               <div class="data columns is-multiline is-vcentered is-marginless" 
               *ngFor="let organization of organizations; let i=index; let odd=odd; let even=even;"
                 [ngClass]="{ odd: odd, even: even }">
-                <div class="column is-1 has-text-centered">
-                  {{ organization.id }}
-                </div>
                 <div class="column is-2 has-text-centered">
                   {{ organization.name}}
                 </div>
@@ -58,24 +47,13 @@
                 <div class="column is-4 has-text-justified">
                   {{ organization.description | slice:0:200}}...
                 </div>
-                <div class="column is-2 has-text-centered">
+                <div class="column is-3 has-text-centered">
                   <p *ngFor="let link of organization.links">
                     <a href="{{ link.url }}" target="_blank">{{link.url}}</a>
                   </p>
                 </div>
                 <div class="column is-1 has-text-centered actions"> 
-                  <div class="columns is-marginless is-centered">
-                    <span class="icon column is-narrow">
-                      <a [routerLink]="[organization.id]"><i class="fas fa-eye"></i></a>
-                    </span>
-                    <span class="icon column is-narrow">
-                      <a [routerLink]="[organization.id, 'edit']"> <i class="fas fa-edit"></i></a>
-                    </span>
-                    <span class="icon has-text-danger column is-narrow" (click)="displayDeletePopup(organization.id)">
-                      <a> <i class="fas fa-trash"></i></a>
-                    </span>
-                  </div>
-      
+                    <app-crud-buttons [id]="organization.id" (delete)="displayDeletePopup($event)"></app-crud-buttons>
                 </div>
       
               </div>
diff --git a/src/app/components/organizations/list/organizations.component.scss b/src/app/components/organizations/list/organizations.component.scss
index 5b91182b06c8b66f96e30646c55767d96bc0a805..087d2f4242ed39d8f3505d06012bf823f015572e 100644
--- a/src/app/components/organizations/list/organizations.component.scss
+++ b/src/app/components/organizations/list/organizations.component.scss
@@ -35,40 +35,6 @@ img {
   border-bottom: 1px solid lightgray
 }
 
-.actions .columns {
-  border: none;
-}
-
-.actions {
-  vertical-align: middle;
-
-  .icon.column {
-    padding: 0;
-  }
-
-  span {
-    display: inline-block;
-  }
-}
-
-.icon {
-  height: unset;
-  cursor: pointer;
-  i {
-    color: #4a4a4a;
-  }
-  &:hover {
-    .fa-eye, .fa-edit {
-      color: blue;
-    }
-
-    .fa-trash {
-      color: #d5232a;
-    }
-
-  }
-}
-
 i {
 
   &.title-icon {
diff --git a/src/app/components/organizations/list/organizations.component.ts b/src/app/components/organizations/list/organizations.component.ts
index 17d063f7da2902ff8f6a68cef367260d148850ca..60020afb5dd4c14df58fccfe84cb081347bc9620 100644
--- a/src/app/components/organizations/list/organizations.component.ts
+++ b/src/app/components/organizations/list/organizations.component.ts
@@ -1,7 +1,7 @@
-import { Component, OnInit, ViewChild } from '@angular/core';
+import { Component, OnInit } from '@angular/core';
 import { Organization, OrganizationRO } from 'src/app/models/organization.model';
 import { OrganizationService } from 'src/app/services/organization.service';
-import { BehaviorSubject, Subscription } from 'rxjs';
+import { Subscription } from 'rxjs';
 import { PaginatorOptions } from 'src/app/models/paginator-options.model';
 
 @Component({
@@ -13,7 +13,6 @@ export class OrganizationsComponent implements OnInit {
 
   organizations: Organization[];
   searchChangeSub: Subscription;
-  displayedColumns = ['name', 'logo', 'description', 'liens', 'actions'];
 
   // Paginator options
   paginator: PaginatorOptions;
@@ -42,7 +41,7 @@ export class OrganizationsComponent implements OnInit {
   ngOnInit(): void {
     this.organizationsService.sortOptions = {
       value: 'name',
-      order: 'asc'
+      order: 'asc',
     };
     this.search();
 
@@ -56,7 +55,6 @@ export class OrganizationsComponent implements OnInit {
   private search() {
     this.organizationsService.getOrganizations()
       .subscribe((items: OrganizationRO) => {
-        console.log(items);
         this.organizations = items.organizations;
         this.totalElement = items.totalCount;
 
@@ -66,7 +64,6 @@ export class OrganizationsComponent implements OnInit {
       });
   }
 
-
   // When pagination is changed by user, we update datasetList with new pagination options
   changePagination(pageIndex) {
     this.organizationsService.paginationChanged(this.paginator.limit, pageIndex);
@@ -92,7 +89,7 @@ export class OrganizationsComponent implements OnInit {
 
   displayDeletePopup(organizationId) {
     const pop = confirm('Etes vous sûr de vouloir supprimer cette organisation ?');
-    if (pop === true) {
+    if (pop) {
       this.organizationsService.delete(organizationId).subscribe(() => {
         this.organizationsService.pageNumber = 1;
         this.search();
diff --git a/src/app/components/resources/detail/resource-detail.component.html b/src/app/components/resources/detail/resource-detail.component.html
index fac0e690cee7db63941a75eb4d472c7aa9a9cbf2..60a5b0114ad29ff66798be9b4543a00585edf42a 100644
--- a/src/app/components/resources/detail/resource-detail.component.html
+++ b/src/app/components/resources/detail/resource-detail.component.html
@@ -1,14 +1,6 @@
 <ng-container *ngIf="resource">
 
-  <div class="back-button">
-    <a routerLink="/resources" title="Retourner à la liste des organisations">
-      <span class="icon is-medium">
-        <i class="fas fa-arrow-left fa-lg"></i>
-      </span>
-      Retour
-    </a>
-  </div>
-
+  <app-back-button [route]="'/resources'" [title]="'Retourner à la liste des ressources'"></app-back-button>
 
   <section class="section">
     <div class="columns is-centered">
@@ -20,45 +12,94 @@
             </p>
           </header>
           <div class="card-content">
-
             <div class="content">
               <p>
-                  <span class="has-text-weight-bold">Type: </span>
-                  <span>{{resource.type}}</span>
+                <span class="has-text-weight-bold">Id: </span>
+                <span>{{resource.id}}</span>
+              </p>
+              <p>
+                <span class="has-text-weight-bold">Acronyme: </span>
+                <span *ngIf="resource.acronym; else emptyAcronymWarning">{{resource.acronym}}</span>
+                <ng-template #emptyAcronymWarning>
+                  <span class="empty-property">Non renseigné</span>
+                </ng-template>
+              </p>
+
+              <p>
+                <span class="has-text-weight-bold">Type: </span>
+                <span *ngIf="resource.type; else emptyTypeWarning">{{resource.type}}</span>
+                <ng-template #emptyTypeWarning>
+                  <span class="empty-property">Non renseigné</span>
+                </ng-template>
               </p>
 
-              <p *ngIf="resource.description">
-                  <span class="has-text-weight-bold">Description</span> <br>
-                   <span>{{resource.description}}</span>
+              <div *ngIf="resource.description">
+                <span class="has-text-weight-bold">Description: </span>
+                <span *ngIf="resource.description; else emptyDescriptionWarning">{{resource.description}}</span>
+                <ng-template #emptyDescriptionWarning>
+                  <span class="empty-property">Non renseigné</span>
+                </ng-template>
+              </div>
+
+              <p>
+                <span class="has-text-weight-bold">Requêtable: </span>
+                <span class="icon has-text-success"
+                  [ngClass]="{'has-text-success': resource.isQueryable, 'has-text-danger': !resource.isQueryable}">
+                  <i class="far fa-check-circle"
+                    [ngClass]="{'fa-check-circle': resource.isQueryable, 'fa-times-circle': !resource.isQueryable}"></i>
+                </span>
               </p>
 
               <p>
-                  <span class="has-text-weight-bold">Requêtable</span> 
-                  <span class="icon has-text-success" *ngIf="resource.queryable">
-                      <i class="far fa-check-circle"></i>
-                    </span>
-                    <span class="icon has-text-danger" *ngIf="!resource.queryable">
-                        <i class="far fa-times-circle"></i>
-                      </span>
+                <span class="has-text-weight-bold">Téléchargeable: </span>
+                <span class="icon has-text-success"
+                  [ngClass]="{'has-text-success': resource.isDownloadable, 'has-text-danger': !resource.isDownloadable}">
+                  <i class="far fa-check-circle"
+                    [ngClass]="{'fa-check-circle': resource.isDownloadable, 'fa-times-circle': !resource.isDownloadable}"></i>
+                </span>
               </p>
 
               <p>
-                  <span class="has-text-weight-bold">Téléchargeable</span> 
-                  <span class="icon has-text-success" *ngIf="resource.downloadable">
-                      <i class="far fa-check-circle"></i>
-                    </span>
-                    <span class="icon has-text-danger" *ngIf="!resource.downloadable">
-                        <i class="far fa-times-circle"></i>
-                      </span>
+                <span class="has-text-weight-bold">Standardisé: </span>
+                <span class="icon has-text-success"
+                  [ngClass]="{'has-text-success': resource.isStandard, 'has-text-danger': !resource.isStandard}">
+                  <i class="far fa-check-circle"
+                    [ngClass]="{'fa-check-circle': resource.isStandard, 'fa-times-circle': !resource.isStandard}"></i>
+                </span>
               </p>
 
+              <div *ngIf="resource.parametersUrl">
+                <span class="has-text-weight-bold">Parametres URL: </span>
+                <span *ngIf="resource.parametersUrl; else emptyParameterUrlWarning">{{resource.parametersUrl}}</span>
+                <ng-template #emptyParameterUrlWarning>
+                  <span class="empty-property">Non renseigné</span>
+                </ng-template>
+              </div>
+
+              <div *ngIf="resource.messageWarning">
+                <span class="has-text-weight-bold">Message d'alerte: </span>
+                <span *ngIf="resource.messageWarning; else emptyMessageWarning">{{resource.messageWarning}}</span>
+                <ng-template #emptyMessageWarning>
+                  <span class="empty-property">Non renseigné</span>
+                </ng-template>
+              </div>
+              <br>
+
               <div>
                 <span class="has-text-weight-bold">Formats de sortie: </span>
-                <span *ngFor="let format of resource.outputFormats; let isLast=last">
-                    {{ format }}{{ isLast ? '' : ',' }}
-                </span>
+                <ul
+                  *ngIf="resource.resourceFormats && resource.resourceFormats.length > 0; else noResourceFormatTemplate">
+                  <li *ngFor="let resourceFormat of resource.resourceFormats">
+                    {{ resourceFormat.format.name }}<span *ngIf="resourceFormat.isCuttable">, découpable</span><span
+                      *ngIf="resourceFormat.isProjectable">, projectable</span>
+                  </li>
+                </ul>
+                <ng-template #noResourceFormatTemplate>
+                  <span class="empty-property">Non renseigné</span>
+                </ng-template>
               </div>
-              
+
+
             </div>
           </div>
         </div>
@@ -69,4 +110,4 @@
 
   </section>
 
-</ng-container>
+</ng-container>
\ No newline at end of file
diff --git a/src/app/components/resources/detail/resource-detail.component.scss b/src/app/components/resources/detail/resource-detail.component.scss
index 9198904fe9916e79864faf708db4350f7d80bae6..05012fa666610636fa011ff7250fcdc78764b468 100644
--- a/src/app/components/resources/detail/resource-detail.component.scss
+++ b/src/app/components/resources/detail/resource-detail.component.scss
@@ -1,17 +1,8 @@
-
-.back-button {
-  a {
-    color: unset; // Remove default link color
-  }
-  padding: 1rem 1.5rem;
-  a:hover {
-    color: #d5232a;
-    span, a {
-      color: #d5232a;
-    }
-  }
-}
-
 .card-header-title {
   justify-content: center;
-}
\ No newline at end of file
+}
+
+.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 984aa5d6b31947a1e6594b3e3edf0b18cbbbe54a..9e24f8e03eb9db223d6f6fd09aaab963021582af 100644
--- a/src/app/components/resources/edit/resource-form.component.html
+++ b/src/app/components/resources/edit/resource-form.component.html
@@ -1,13 +1,6 @@
 <ng-container *ngIf="resource">
 
-  <div class="back-button">
-    <a routerLink="/resources" title="Retourner à la liste des organisations">
-      <span class="icon is-medium">
-        <i class="fas fa-arrow-left fa-lg"></i>
-      </span>
-      Retour
-    </a>
-  </div>
+  <app-back-button [route]="'/resources'" [title]="'Retourner à la liste des ressources'"></app-back-button>
 
   <h1>{{ title }}</h1>
 
@@ -18,7 +11,7 @@
       <div class="field">
         <label class="label required" for="name">Nom</label>
         <div class="control">
-          <input class="input" type="text" formControlName="name" id="name" required>
+          <input class="input" type="text" formControlName="name" id="name">
         </div>
         <div *ngIf="name.invalid && (name.dirty || name.touched)" class="alert alert-danger">
           <p *ngIf="name.errors['required']" class="help is-danger">
@@ -27,10 +20,17 @@
         </div>
       </div>
 
+      <div class="field">
+        <label class="label" for="acronym">Acronyme</label>
+        <div class="control">
+          <input class="input" type="text" formControlName="acronym" id="acronym">
+        </div>
+      </div>
+
       <div class="field">
         <label class="label required" for="type">Type</label>
         <div class="control">
-          <input class="input" type="text" formControlName="type" id="type" required>
+          <input class="input" type="text" formControlName="type" id="type">
         </div>
         <div *ngIf="type.invalid && (type.dirty || type.touched)" class="alert alert-danger">
           <p *ngIf="type.errors['required']" class="help is-danger">
@@ -40,28 +40,42 @@
       </div>
 
       <div class="field">
-        <label class="label required" for="queryable">Requêtable</label>
+        <label class="label required" for="isQueryable">Requêtable</label>
+        <div class="control">
+          <label class="radio">
+            <input type="radio" formControlName="isQueryable" [value]="1">
+            Oui
+          </label>
+          <label class="radio">
+            <input type="radio" formControlName="isQueryable" [value]="0">
+            Non
+          </label>
+        </div>
+      </div>
+
+      <div class="field">
+        <label class="label required" for="isDownloadable">Téléchargeable</label>
         <div class="control">
           <label class="radio">
-            <input type="radio" formControlName="queryable" [value]="1">
+            <input type="radio" formControlName="isDownloadable" [value]="1">
             Oui
           </label>
           <label class="radio">
-            <input type="radio" formControlName="queryable" [value]="0">
+            <input type="radio" formControlName="isDownloadable" [value]="0">
             Non
           </label>
         </div>
       </div>
 
       <div class="field">
-        <label class="label required" for="downloadable">Téléchargeable</label>
+        <label class="label required" for="isStandard">Standardisé</label>
         <div class="control">
           <label class="radio">
-            <input type="radio" formControlName="downloadable" [value]="1">
+            <input type="radio" formControlName="isStandard" [value]="1">
             Oui
           </label>
           <label class="radio">
-            <input type="radio" formControlName="downloadable" [value]="0">
+            <input type="radio" formControlName="isStandard" [value]="0">
             Non
           </label>
         </div>
@@ -71,26 +85,97 @@
         <label class="label" for="description">Description</label>
         <div class="control">
           <textarea class="textarea" formControlName="description" id="description">
-                                  {{ resource.description }}
-          </textarea>
+            </textarea>
         </div>
       </div>
 
       <div class="field">
-        <label class="label" for="outputFormats">Formats de sortie (Séparer chaque format par une virgule)</label>
+        <label class="label required" for="parametersUrl">Paramètres URL</label>
         <div class="control">
-          <input class="input" type="text" formControlName="outputFormats" id="outputFormats">
+          <input class="input" type="text" formControlName="parametersUrl" id="parametersUrl">
         </div>
-
       </div>
+
       <div class="field">
+        <label class="label required" for="messageWarning">Message d'alerte</label>
         <div class="control">
-          <button class="button validate" [disabled]="formInvalid == true">Valider</button>
+          <input class="input" type="text" formControlName="messageWarning" id="messageWarning">
         </div>
       </div>
 
-    </div>
-  </form>
+      <div class="field links">
+        <div class="columns">
+          <div class="column is-11">
+            <label class="label">Formats</label>
+          </div>
+          <div class="column is-1">
+            <span class="icon" (click)="addResourceFormat()" title="Ajouter un format">
+              <i class="fas fa-plus"></i>
+            </span>
+          </div>
+        </div>
+        <div formArrayName="resourceFormats">
+          <div *ngFor="let resourceFormat of formResourceFormats.controls; let i = index;" [formGroupName]="i"
+            class="columns is-multiline">
+            <div class="field column is-5">
+              <label class="label required" for="format">Format</label>
+              <div class="control">
+                <div class="select">
+                  <select formControlName="formatId">
+                    <option [ngValue]="format.id" *ngFor="let format of formatsList"> {{format.name}}</option>
+                  </select>
+                </div>
+              </div>
+              <div
+                *ngIf="resourceFormat['controls'].formatId.invalid && (resourceFormat['controls'].formatId.dirty || resourceFormat['controls'].formatId.touched)"
+                class="alert alert-danger">
+                <p *ngIf="resourceFormat.hasError('required', 'format')" class="help is-danger">
+                  Le format est requis.
+                </p>
+              </div>
+            </div>
+            <div class="field column is-3">
+              <label class="label required" for="isProjectable">Projectable</label>
+              <div class="control">
+                <label class="radio">
+                  <input type="radio" formControlName="isProjectable" [value]="1">
+                  Oui
+                </label>
+                <label class="radio">
+                  <input type="radio" formControlName="isProjectable" [value]="0">
+                  Non
+                </label>
+              </div>
+            </div>
+            <div class="field column is-3">
+              <label class="label required" for="isCuttable">Découpable</label>
+              <div class="control">
+                <label class="radio">
+                  <input type="radio" formControlName="isCuttable" [value]="1">
+                  Oui
+                </label>
+                <label class="radio">
+                  <input type="radio" formControlName="isCuttable" [value]="0">
+                  Non
+                </label>
+              </div>
+            </div>
 
+            <div class="column is-1">
+              <span class="icon" (click)="removeResourceFormat(i)" title="Supprimer le format">
+                <i class="fas fa-trash"></i>
+              </span>
+            </div>
+          </div>
+        </div>
+      </div>
 
-</ng-container>
+
+      <br>
+      <div class="has-text-right">
+        <button class="button button-gl" type="submit" [disabled]="formInvalid == true">Valider</button>
+      </div>
+
+    </div>
+  </form>
+</ng-container>
\ No newline at end of file
diff --git a/src/app/components/resources/edit/resource-form.component.scss b/src/app/components/resources/edit/resource-form.component.scss
index 8522a9ab88fb48e0ae39d622940fa851978291f8..c540d1b2697b6ce03cfbbdb19454eb365d6e2a4a 100644
--- a/src/app/components/resources/edit/resource-form.component.scss
+++ b/src/app/components/resources/edit/resource-form.component.scss
@@ -22,16 +22,3 @@ h1 {
   background-color: #168f48;
   color: white;
 }
-
-.back-button {
-  a {
-    color: unset; // Remove default link color
-  }
-  padding: 1rem 1.5rem;
-  a:hover {
-    color: #d5232a;
-    span, a {
-      color: #d5232a;
-    }
-  }
-}
diff --git a/src/app/components/resources/edit/resource-form.component.ts b/src/app/components/resources/edit/resource-form.component.ts
index 4b77a4d0c857e8d33a260d0b5cdaa199e8839346..b40283eb2dbed3b4d2148c60ebd4bb3e6e048052 100644
--- a/src/app/components/resources/edit/resource-form.component.ts
+++ b/src/app/components/resources/edit/resource-form.component.ts
@@ -1,9 +1,12 @@
 import { Component, OnInit } from '@angular/core';
 import { ActivatedRoute, ParamMap, Router } from '@angular/router';
-import { Resource } from 'src/app/models/resource.model';
+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 } from 'rxjs/operators';
+import { filter, switchMap, mergeMap, merge, concatMap, map } from 'rxjs/operators';
+import { Format } from 'src/app/models/format.model';
+import { FormatService } from 'src/app/services';
+import { from, forkJoin, Observable, of } from 'rxjs';
 
 @Component({
   selector: 'app-resource-form',
@@ -12,75 +15,134 @@ import { filter, switchMap } from 'rxjs/operators';
 })
 export class ResourceFormComponent implements OnInit {
 
-  resource: Resource;
+  resource: Resource = new Resource();
+  formatsList: Format[] = [];
   form: FormGroup;
   title: string;
 
   constructor(
-    private resourceService: ResourceService,
-    private route: ActivatedRoute,
-    private router: Router,
+    private _resourceService: ResourceService,
+    private _formatService: FormatService,
+    private _route: ActivatedRoute,
+    private _router: Router,
     private _fb: FormBuilder,
   ) {
   }
 
   ngOnInit() {
-    this.title = this.route.snapshot.data.title;
+    this.title = this._route.snapshot.data.title;
     this.initForm();
 
-    this.route.paramMap.pipe(
+    this._formatService.getAllFormats().subscribe((res) => {
+      this.formatsList = res;
+    });
+
+    this._route.paramMap.pipe(
       filter((paramMap: ParamMap) => (paramMap.get('id') !== null)),
-      switchMap((paramMap: ParamMap) => this.resourceService.findById(paramMap.get('id'))))
+      switchMap((paramMap: ParamMap) => this._resourceService.findById(paramMap.get('id'))))
       .subscribe((resource: Resource) => {
-
         this.resource = resource;
-
-        this.form = this._fb.group(
-          {
-            id: [this.resource.id],
-            name: [resource.name, Validators.required],
-            type: [resource.type, Validators.required],
-            queryable: [resource.queryable, Validators.required],
-            downloadable: [this.resource.downloadable, Validators.required],
-            description: [resource.description],
-            outputFormats: [this.resource.outputFormats.join(',')],
-          });
-
+        this.initForm();
       });
 
   }
 
   initForm() {
-    this.resource = new Resource();
-    const arr = new FormArray([]);
+    const resourceFormats = new FormArray([]);
+    this.resource.resourceFormats.forEach((resourceFormat) => {
+      resourceFormats.push(this._fb.group({
+        formatId: [resourceFormat.format.id, Validators.required],
+        isProjectable: resourceFormat.isProjectable,
+        isCuttable: resourceFormat.isCuttable,
+        id: resourceFormat.id,
+      }));
+    });
+
     this.form = this._fb.group(
       {
+        resourceFormats,
         id: [this.resource.id],
         name: [this.resource.name, Validators.required],
+        acronym: [this.resource.acronym],
         type: [this.resource.type, Validators.required],
-        queryable: [this.resource.queryable, Validators.required],
-        downloadable: [this.resource.downloadable, Validators.required],
+        isQueryable: [this.resource.isQueryable, Validators.required],
+        isDownloadable: [this.resource.isDownloadable, Validators.required],
+        isStandard: [this.resource.isStandard, Validators.required],
         description: [this.resource.description],
-        outputFormats: [this.resource.outputFormats.join(',')],
+        parametersUrl: [this.resource.parametersUrl],
+        messageWarning: [this.resource.messageWarning],
       });
   }
 
   onSubmit() {
     if (!this.formInvalid) {
-      this.resource = new Resource(this.form.value);
-      this.resource.outputFormats = this.form.value.outputFormats.split(',');
-      this.resourceService.replaceOrCreate(this.resource)
-        .subscribe(
-          (resourceCreated) => {
-            this.router.navigate(['/resources', resourceCreated.id])
-              .then(() => {
-              });
-          },
-          (err) => {
-            alert(err.message);
-          },
-        );
+      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(
+        mergeMap((resource) => {
+          savedResource = resource;
+          let actions: Observable<any>[] = [];
+
+          const toAdd = resourceFormats.filter(e => !e.id);
+          const toUpdate = resourceFormats.filter(e => e.id)
+          const toDelete = this.resource.resourceFormats.map(e => e.id).filter((id) => {
+            return !this.form.controls.resourceFormats['controls'].map(e => e.value.id).find(rfId => rfId === id);
+          });
+
+          if (toAdd.length > 0) {
+            actions.push(this._resourceService.createResourceFormats(resource.id, toAdd));
+          }
+
+          if (toUpdate.length > 0) {
+            actions.push(this._resourceService.updateResourceFormats(resource.id, toUpdate));
+          }
+
+          if (toDelete.length > 0) {
+            actions.push(this._resourceService.deleteResourceFormats(resource.id, toDelete));
+          }
+
+          let res;
+          (actions.length > 0) ? res = forkJoin(actions) : res = of(null);
+
+          return res;
+        }),
+        map(() => savedResource.id),
+      ).subscribe(
+        (resourceCreatedId) => {
+          this._router.navigate(['/resources', resourceCreatedId]);
+        },
+        (err) => {
+        },
+      );
+    }
+  }
+
+  arrayToString(array: string[]) {
+    let val = '';
+    if (array && array.length) {
+      val = array.join(',');
     }
+    return val;
+  }
+
+  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,
+      isCuttable: 0,
+    }));
+  }
+
+  removeResourceFormat(index) {
+    (this.form.controls.resourceFormats as FormArray).removeAt(index);
+  }
+
+  compareFormats(format1: Format, format2: Format) {
+    return format1 && format2 ? format1.id === format2.id : false;
   }
 
   // Getters for each property
@@ -92,18 +154,22 @@ export class ResourceFormComponent implements OnInit {
     return this.form.controls['type'];
   }
 
-  get queryable() {
-    return this.form.controls['queryable'];
+  get isQueryable() {
+    return this.form.controls['isQueryable'];
   }
 
   get description() {
     return this.form.controls['description'];
   }
 
-  get outputFormats() {
-    return this.form.controls['outputFormats'];
+  get formResourceFormats() {
+    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 9606966c5198e8fe8198338de2a52f5ebf0d2bd7..67f84ebc6389a162554ef6300e57f97b2ccc1b1c 100644
--- a/src/app/components/resources/list/resources.component.html
+++ b/src/app/components/resources/list/resources.component.html
@@ -6,110 +6,100 @@
           <h2>{{ totalElement }} ressources trouvées</h2>
         </div>
       </div>
-      <div class="add-item">
-        <a class="button is-link" [routerLink]="['new']">
+      <div class="add-item has-text-right">
+        <a class="button button-gl" [routerLink]="['new']">
           Ajouter
         </a>
       </div>
       <div class="table">
-          <div class="header columns is-marginless">
-              <div class="column is-1 has-text-centered">
-                <span (click)="sortBy('id')" class="is-sortable">
-                  <span class="column-title" [ngClass]="{'active': sortOptions.value === 'id'}">Id</span>
-                  <span *ngIf="sortOptions.value === 'id'" class="has-text-danger">
-                    <i class="fas sort-order-icon" [ngClass]="{'fa-arrow-up': sortOptions.order === 'asc', 'fa-arrow-down': sortOptions.order === 'desc'}"></i>
-                  </span>
-                </span>
-              </div>
-              <div class="column is-2 has-text-centered">
-                <span (click)="sortBy('name')" class="is-sortable">
-                  <span class="column-title" [ngClass]="{'active': sortOptions.value === name}">Name</span>
-                  <span *ngIf="sortOptions.value  === 'name'"  class="has-text-danger">
-                    <i class="fas sort-order-icon" [ngClass]="{'fa-arrow-up': sortOptions.order === 'asc', 'fa-arrow-down': sortOptions.order === 'desc'}"></i>
-                  </span>
-                </span>
-              </div>
-              <div class="column is-1 has-text-centered">
-                <span class="column-title">Type</span>
-              </div>
-              <div class="column is-3 has-text-centered">
-                <span class="column-title">Description</span>
-              </div>
-              <div class="column is-1 has-text-centered">
-                <span class="column-title">Requêtable</span>
-              </div>
-              <div class="column is-1 has-text-centered">
-                  <span class="column-title">Téléchargeable</span>
-              </div>
-              <div class="column is-2 has-text-centered">
-                  <span class="column-title">Formats</span>
-              </div>
-              <div class="column is-1 has-text-centered">
-                <span class="column-title">Actions</span>
-              </div>
+        <div class="header columns is-marginless">
+          <div class="column is-2 has-text-centered">
+            <span (click)="sortBy('name')" class="is-sortable">
+              <span class="column-title" [ngClass]="{'active': sortOptions.value === name}">Nom</span>
+              <span *ngIf="sortOptions.value  === 'name'" class="has-text-danger">
+                <i class="fas sort-order-icon"
+                  [ngClass]="{'fa-arrow-up': sortOptions.order === 'asc', 'fa-arrow-down': sortOptions.order === 'desc'}"></i>
+              </span>
+            </span>
+          </div>
+          <div class="column is-1 has-text-centered">
+            <span class="column-title">Acronyme</span>
+          </div>
+          <div class="column is-1 has-text-centered">
+            <span class="column-title">Type</span>
+          </div>
+          <div class="column is-4 has-text-centered">
+            <span class="column-title">Description</span>
+          </div>
+          <div class="column is-1 has-text-centered">
+            <span class="column-title">Requêtable</span>
+          </div>
+          <div class="column is-1 has-text-centered">
+            <span class="column-title">Téléchargeable</span>
+          </div>
+          <div class="column is-1 has-text-centered">
+            <span class="column-title">Standard</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 resource of resources; let i=index; let odd=odd; let even=even;"
+            [ngClass]="{ odd: odd, even: even }">
+            <div class="column is-2 has-text-centered">
+              {{ resource.name}}
+            </div>
+            <div class="column is-1 has-text-centered">
+              {{ resource.acronym}}
+            </div>
+            <div class="column is-1 has-text-centered">
+              {{ resource.type}}
+            </div>
+            <div class="column is-4 has-text-centered">
+              {{ resource.description | slice:0:300}}
             </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-1 has-text-centered">
-                  {{ resource.id }}
-                </div>
-                <div class="column is-2 has-text-centered">
-                  {{ resource.name}}
-                </div>
-                <div class="column is-1 has-text-centered">
-                    {{ resource.type}}
-                </div>
-                <div class="column is-3 has-text-centered">
-                  {{ resource.description | slice:0:300}}
-                </div>
-                <div class="column is-1 has-text-centered">
-                  <span class="icon has-text-success" *ngIf="resource.queryable">
-                    <i class="far fa-check-circle"></i>
-                  </span>
-                  <span class="icon has-text-danger" *ngIf="!resource.queryable">
-                      <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.downloadable">
-                        <i class="far fa-check-circle"></i>
-                      </span>
-                      <span class="icon has-text-danger" *ngIf="!resource.downloadable">
-                          <i class="far fa-times-circle"></i>
-                        </span>
-                </div>
-                <div class="column is-2 has-text-centered">
-                    {{ resource.outputFormats}}
-                </div>
-                <div class="column is-1 has-text-centered actions"> 
-                  <div class="columns is-marginless is-centered">
-                    <span class="icon column is-narrow">
-                      <a [routerLink]="[resource.id]"><i class="fas fa-eye"></i></a>
-                    </span>
-                    <span class="icon column is-narrow">
-                      <a [routerLink]="[resource.id, 'edit']"> <i class="fas fa-edit"></i></a>
-                    </span>
-                    <span class="icon has-text-danger column is-narrow" (click)="displayDeletePopup(resource.id)">
-                      <a> <i class="fas fa-trash"></i></a>
-                    </span>
-                  </div>
-      
-                </div>
-      
-              </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="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 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">
+          <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>
-</ng-container>
+</ng-container>
\ No newline at end of file
diff --git a/src/app/components/resources/list/resources.component.scss b/src/app/components/resources/list/resources.component.scss
index aa337b2be447c0ed3974f37cb692ae0ef133d19b..c746b911909d67f2432690682808294ac825224e 100644
--- a/src/app/components/resources/list/resources.component.scss
+++ b/src/app/components/resources/list/resources.component.scss
@@ -35,37 +35,6 @@ img {
   border-bottom: 1px solid lightgray
 }
 
-.actions .columns {
-  border: none;
-}
-
-.actions {
-  vertical-align: middle;
-
-  .icon.column {
-    padding: 0;
-  }
-
-  span {
-    display: inline-block;
-  }
-}
-
-.actions .icon {
-  cursor: pointer;
-
-  i {
-    color: #4a4a4a;
-  }
-  &:hover {
-    .fa-eye, .fa-edit {
-      color: blue;
-    }
-    .fa-trash {
-      color: #d5232a;
-    }
-  }
-}
 .icon {
   height: unset;
 }
diff --git a/src/app/components/resources/list/resources.component.ts b/src/app/components/resources/list/resources.component.ts
index a8d9357aa537cee3e71e6f1b72186adf159138f8..a56ed6ddeeaf05512edcd33cddbfa12247c45bb7 100644
--- a/src/app/components/resources/list/resources.component.ts
+++ b/src/app/components/resources/list/resources.component.ts
@@ -41,7 +41,7 @@ export class ResourcesComponent implements OnInit {
   ngOnInit(): void {
     this.resourcesService.sortOptions = {
       value: 'name',
-      order: 'asc'
+      order: 'asc',
     };
     this.search();
 
@@ -64,7 +64,6 @@ export class ResourcesComponent implements OnInit {
       });
   }
 
-
   // When pagination is changed by user, we update datasetList with new pagination options
   changePagination(pageIndex) {
     this.resourcesService.paginationChanged(this.paginator.limit, pageIndex);
@@ -89,8 +88,8 @@ export class ResourcesComponent implements OnInit {
   }
 
   displayDeletePopup(resourceId) {
-    const pop = confirm('Etes vous sûr de vouloir supprimer cette organisation ?');
-    if (pop === true) {
+    const pop = confirm('Etes vous sûr de vouloir supprimer cette ressource ?');
+    if (pop) {
       this.resourcesService.delete(resourceId).subscribe(() => {
         this.resourcesService.pageNumber = 1;
         this.search();
diff --git a/src/app/models/format.model.ts b/src/app/models/format.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ebef0b2408621fcc803d6c67e724cf9c3ec13748
--- /dev/null
+++ b/src/app/models/format.model.ts
@@ -0,0 +1,34 @@
+export class Format {
+  id?: number;
+  name: string;
+  mapServerType: string;
+
+  constructor(format?: IFormat) {
+    if (format) {
+      if (format.id) {
+        this.id = format.id;
+      }
+      this.name = format.name;
+      this.mapServerType = format.mapServerType;
+    } else {
+      this.name = '';
+      this.mapServerType = '';
+    }
+  }
+}
+
+export class FormatRO {
+  formats: Format[];
+  totalCount: number;
+
+  constructor(formats, totalCount) {
+    this.formats = formats;
+    this.totalCount = totalCount;
+  }
+}
+
+export interface IFormat {
+  id: number;
+  name: string;
+  mapServerType: string;
+}
diff --git a/src/app/models/image-upload.model.ts b/src/app/models/image-upload.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0c4a768dc92888a6ef1f8ee5f8f0ceba92f32b4e
--- /dev/null
+++ b/src/app/models/image-upload.model.ts
@@ -0,0 +1,5 @@
+export interface IImageUploadFieldParams {
+  label: string;
+  inputName: string;
+  existingImageUrl?: string;
+}
diff --git a/src/app/models/notification.model.ts b/src/app/models/notification.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..8fdf5fcff77594e5963c191719c2bc0990403e76
--- /dev/null
+++ b/src/app/models/notification.model.ts
@@ -0,0 +1,16 @@
+type errorType = 'error' | 'warning' | 'success';
+
+export interface INotification {
+  message: string;
+  type: errorType;
+}
+
+export class Notification {
+  message: string;
+  type: errorType;
+
+  constructor(notification: INotification) {
+    this.message = notification.message;
+    this.type = notification.type;
+  }
+}
diff --git a/src/app/models/organization.model.ts b/src/app/models/organization.model.ts
index 53adbd907ffd027f9924c0d4b9376bcae8364a02..54074cb77548abbd77d3452fe686f213512f5f73 100644
--- a/src/app/models/organization.model.ts
+++ b/src/app/models/organization.model.ts
@@ -8,17 +8,25 @@ export class Organization {
 
   constructor(organization?: IOrganization) {
     if (organization) {
-      this.id = organization.id;
+      if (organization.id) {
+        this.id = organization.id;
+      }
       this.name = organization.name;
       this.description = organization.description;
       this.elasticSearchName = organization.elasticSearchName;
       this.links = organization.links;
+
+      this.links.forEach((link) => {
+        if (!link.id) {
+          delete link.id;
+        }
+      });
       this.logo = organization.logo;
     } else {
-      this.name = '';
-      this.description = '';
-      this.logo = '';
-      this.elasticSearchName = '';
+      this.name = null;
+      this.description = null;
+      this.logo = null;
+      this.elasticSearchName = null;
       this.links = [];
     }
 
diff --git a/src/app/models/resource.model.ts b/src/app/models/resource.model.ts
index 7fb24fa24833f9c6553c65955df1c017b10db4c2..2d28cdda3fc135178cc0515efd361079a1f286ed 100644
--- a/src/app/models/resource.model.ts
+++ b/src/app/models/resource.model.ts
@@ -1,30 +1,46 @@
+import { Format } from './format.model';
+
 export class Resource {
-  id: number;
+  id?: string;
   name: string;
+  acronym: string;
   type: string;
-  description?: string;
-  queryable: number;
-  downloadable: number;
-  outputFormats: string[];
+  description: string;
+  isQueryable: number;
+  isDownloadable: number;
+  isStandard: number;
+  parametersUrl: string;
+  messageWarning: string;
+  resourceFormats: IResourceFormat[];
 
   constructor(resource?: IResource) {
+
     if (resource) {
-      this.id = resource.id;
+      if (resource.id) {
+        this.id = resource.id;
+      }
       this.name = resource.name;
+      this.acronym = resource.acronym;
       this.type = resource.type;
       this.description = resource.description;
-      this.queryable = resource.queryable;
-      this.downloadable = resource.downloadable;
-      this.outputFormats = resource.outputFormats;
+      this.isQueryable = resource.isQueryable;
+      this.isDownloadable = resource.isDownloadable;
+      this.isStandard = resource.isStandard;
+      this.parametersUrl = resource.parametersUrl;
+      this.messageWarning = resource.messageWarning;
+      this.resourceFormats = resource.resourceFormats ? resource.resourceFormats : [];
     } else {
       this.name = '';
-      this.description = '';
+      this.acronym = '';
       this.type = '';
-      this.queryable = 0;
-      this.downloadable = 0;
-      this.outputFormats = [];
+      this.description = '';
+      this.isQueryable = 0;
+      this.isDownloadable = 0;
+      this.isStandard = 0;
+      this.parametersUrl = '';
+      this.messageWarning = '';
+      this.resourceFormats = [];
     }
-
   }
 }
 
@@ -39,18 +55,23 @@ export class ResourceRO {
 }
 
 export interface IResource {
-  id: number;
+  id: string;
   name: string;
+  acronym: string;
   type: string;
   description: string;
-  queryable: number;
-  downloadable: number;
-  outputFormats: string[];
+  isQueryable: number;
+  isDownloadable: number;
+  isStandard: number;
+  parametersUrl: string;
+  messageWarning: string;
+  resourceFormats: IResourceFormat[];
 }
 
-interface ILink {
-  id?: number;
-  name: string;
-  url: string;
-  organizationId: number;
+export interface IResourceFormat {
+  format: Format;
+  formatId: string;
+  isProjectable: number;
+  isCuttable: number;
+  id?: string;
 }
diff --git a/src/app/services/app-config.service.ts b/src/app/services/app-config.service.ts
index edabf90b5de6509faaa179008aaa0cac92a70606..66e9fd31fad98efbde56154a7c9f346e8cf1dcb4 100644
--- a/src/app/services/app-config.service.ts
+++ b/src/app/services/app-config.service.ts
@@ -7,6 +7,9 @@ export class AppConfig {
   resources: {
     url: string;
   };
+  mediaLibrary: {
+    url: string;
+  };
 }
 
 export let APP_CONFIG: AppConfig;
diff --git a/src/app/services/format.service.ts b/src/app/services/format.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..722f25a8a0188f9c85742e343b50e66f59a167c7
--- /dev/null
+++ b/src/app/services/format.service.ts
@@ -0,0 +1,97 @@
+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 { FormatRO, IFormat, Format } from '../models/format.model';
+
+@Injectable()
+export class FormatService {
+
+  formatServiceUrl: string;
+  limit: number;
+  pageNumber: number;
+  sortOptions: {
+    value: string,
+    order: string,
+  };
+
+  private _searchChangeSubject: Subject<any>;
+
+  constructor(
+    private _httpClient: HttpClient) {
+    this.formatServiceUrl = `${APP_CONFIG.resources.url}formats/`;
+    this._searchChangeSubject = new Subject<any>();
+    this.limit = 10;
+    this.pageNumber = 1;
+  }
+
+  getFormats(): Observable<FormatRO> {
+    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<IFormat[]>(this.formatServiceUrl + query, { observe: 'response' }).pipe(
+      map((response) => {
+        const totalCount = response.headers.get('Content-Range');
+        const formats = [];
+        response.body.forEach((format) => {
+          formats.push(new Format(format));
+        });
+        return new FormatRO(formats, parseInt(totalCount, 10));
+      }));
+  }
+
+  getAllFormats(): Observable<Format[]> {
+    return this._httpClient.get<IFormat[]>(this.formatServiceUrl).pipe(
+      map(body => body.map(format => new Format(format))),
+    );
+  }
+
+  findById(id): Observable<Format> {
+    return this._httpClient.get<IFormat>(this.formatServiceUrl + id).pipe(
+      map((response) => {
+        return new Format(response);
+      }),
+    );
+  }
+
+  delete(id) {
+    return this._httpClient.delete(this.formatServiceUrl + id);
+  }
+
+  replaceOrCreate(data): Observable<Format> {
+    if (data.id) {
+      return this._httpClient.put<IFormat>(this.formatServiceUrl + data.id, data).pipe(
+        map((response) => {
+          return new Format(response);
+        }),
+      );
+    }
+    return this._httpClient.post<IFormat>(this.formatServiceUrl, data).pipe(
+      map((response) => {
+        return new Format(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/index.ts b/src/app/services/index.ts
index 24455cc0269d221c37faf7bb2c66792a0ec257b8..34d4ee52b65425a4c8e071ecb732d74c314446ec 100644
--- a/src/app/services/index.ts
+++ b/src/app/services/index.ts
@@ -1,11 +1,15 @@
 import { AppConfigService } from './app-config.service';
 import { OrganizationService } from './organization.service';
 import { ResourceService } from './resource.service';
+import { FormatService } from './format.service';
+import { NotificationService } from './notification.service';
 
 export {
   AppConfigService,
   OrganizationService,
   ResourceService,
+  FormatService,
+  NotificationService,
 };
 
 // tslint:disable-next-line:variable-name
@@ -13,4 +17,6 @@ export const AppServices = [
   AppConfigService,
   OrganizationService,
   ResourceService,
+  FormatService,
+  NotificationService,
 ];
diff --git a/src/app/services/notification.service.ts b/src/app/services/notification.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7a1a532e14cd3da0e832564d46e40aa03f3f447b
--- /dev/null
+++ b/src/app/services/notification.service.ts
@@ -0,0 +1,21 @@
+
+import { Injectable } from '@angular/core';
+import { BehaviorSubject } from 'rxjs';
+import { Notification } from '../models/notification.model';
+
+@Injectable()
+export class NotificationService {
+
+  private _notification: BehaviorSubject<Notification> = new BehaviorSubject(null);
+
+  constructor() { }
+
+  notify(notification: Notification) {
+    this._notification.next(notification);
+  }
+
+  get notification$() {
+    return this._notification;
+  }
+
+}
diff --git a/src/app/services/organization.service.ts b/src/app/services/organization.service.ts
index c5360560f0a3ef99269166b974109ef92beef31d..d5a82d7ce9d00a5b3a3aeebd765a1a93fca73923 100644
--- a/src/app/services/organization.service.ts
+++ b/src/app/services/organization.service.ts
@@ -1,11 +1,10 @@
 import { Injectable } from '@angular/core';
-import { Observable ,  Subject } from 'rxjs';
-import { map } from 'rxjs/operators';
+import { Observable, Subject } from 'rxjs';
+import { map, mergeMap } from 'rxjs/operators';
 import { HttpClient } from '@angular/common/http';
 import { Organization, IOrganization, OrganizationRO } from '../models/organization.model';
 import { APP_CONFIG } from './app-config.service';
 
-
 @Injectable()
 export class OrganizationService {
 
@@ -20,16 +19,16 @@ export class OrganizationService {
 
   constructor(
     private _httpClient: HttpClient) {
-      this._searchChangeSubject = new Subject<any>();
-      this.limit = 10;
-      this.pageNumber = 1;
+    this._searchChangeSubject = new Subject<any>();
+    this.limit = 10;
+    this.pageNumber = 1;
   }
 
   getOrganizations(options?): Observable<OrganizationRO> {
     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;
+    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<IOrganization[]>(APP_CONFIG.organizations.url + query, { observe: 'response' }).pipe(
       map((response) => {
@@ -46,49 +45,66 @@ export class OrganizationService {
     return this._httpClient.get<IOrganization>(APP_CONFIG.organizations.url + id).pipe(
       map((response) => {
         return new Organization(response);
-      }
-      ));
+      }),
+    );
   }
 
   delete(id) {
     return this._httpClient.delete(APP_CONFIG.organizations.url + id);
   }
 
+  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).pipe(
+      map((response) => {
+        return response;
+      }),
+    );
+  }
+
   replaceOrCreate(data: Organization): Observable<Organization> {
     if (data.id) {
       return this._httpClient.put<IOrganization>(APP_CONFIG.organizations.url + data.id, data).pipe(
         map((response) => {
           return new Organization(response);
-        }
-        ));
+        }),
+      );
     }
-    delete data.id;
-    data.links.forEach((link) => {
-      delete link.id;
-    });
+
     return this._httpClient.post<IOrganization>(APP_CONFIG.organizations.url, data).pipe(
       map((response) => {
         return new Organization(response);
-      }
-      ));
+      }),
+    );
   }
 
-    /* PAGINATION */
-    paginationChanged(limit: number, pageNumber: number) {
-      this.limit = limit;
-      this.pageNumber = pageNumber;
-      this._searchChangeSubject.next();
-    }
+  /* 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';
-      }
+  reverseSortOrder(): void {
+    if (this.sortOptions.order === 'asc') {
+      this.sortOptions.order = 'desc';
+    } else {
+      this.sortOptions.order = 'asc';
     }
+  }
 
-    get searchChange$(): Observable<string> {
-      return this._searchChangeSubject.asObservable();
-    }
+  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 d1b0d38d309beb7f3763e593c08a352f987e4737..b598eb31653bfe0f7fd8005c2d93a78bb996abf5 100644
--- a/src/app/services/resource.service.ts
+++ b/src/app/services/resource.service.ts
@@ -1,13 +1,14 @@
 import { Injectable } from '@angular/core';
-import { Observable ,  Subject } from 'rxjs';
-import { map } from 'rxjs/operators';
+import { Observable, Subject, from } from 'rxjs';
+import { map, concatMap } from 'rxjs/operators';
 import { HttpClient } from '@angular/common/http';
-import { Resource, IResource, ResourceRO } from '../models/resource.model';
+import { Resource, IResource, ResourceRO, IResourceFormat } from '../models/resource.model';
 import { APP_CONFIG } from './app-config.service';
 
 @Injectable()
 export class ResourceService {
 
+  resourceServiceUrl: string;
   limit: number;
   pageNumber: number;
   sortOptions: {
@@ -19,18 +20,19 @@ export class ResourceService {
 
   constructor(
     private _httpClient: HttpClient) {
-      this._searchChangeSubject = new Subject<any>();
-      this.limit = 10;
-      this.pageNumber = 1;
+    this.resourceServiceUrl = `${APP_CONFIG.resources.url}resources/`;
+    this._searchChangeSubject = new Subject<any>();
+    this.limit = 10;
+    this.pageNumber = 1;
   }
 
-  getResources(options?): Observable<ResourceRO> {
+  getResources(): Observable<ResourceRO> {
     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;
+    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<IResource[]>(APP_CONFIG.resources.url + query, { observe: 'response' }).pipe(
+    return this._httpClient.get<IResource[]>(this.resourceServiceUrl + query, { observe: 'response' }).pipe(
       map((response) => {
         const totalCount = response.headers.get('Content-Range');
         const resources = [];
@@ -42,48 +44,72 @@ export class ResourceService {
   }
 
   findById(id): Observable<Resource> {
-    return this._httpClient.get<IResource>(APP_CONFIG.resources.url + id).pipe(
+    return this._httpClient.get<IResource>(this.resourceServiceUrl + id).pipe(
       map((response) => {
         return new Resource(response);
-      }
-      ));
+      }),
+    );
   }
 
   delete(id) {
-    return this._httpClient.delete(APP_CONFIG.resources.url + id);
+    return this._httpClient.delete(this.resourceServiceUrl + id);
   }
 
   replaceOrCreate(data): Observable<Resource> {
     if (data.id) {
-      return this._httpClient.put<IResource>(APP_CONFIG.resources.url + data.id, data).pipe(
+      return this._httpClient.put<IResource>(this.resourceServiceUrl + data.id, data).pipe(
         map((response) => {
           return new Resource(response);
-        }
-        ));
+        }),
+      );
     }
-    return this._httpClient.post<IResource>(APP_CONFIG.resources.url, data).pipe(
+    return this._httpClient.post<IResource>(this.resourceServiceUrl, data).pipe(
       map((response) => {
         return new Resource(response);
-      }
-      ));
+      }),
+    );
   }
 
-    /* PAGINATION */
-    paginationChanged(limit: number, pageNumber: number) {
-      this.limit = limit;
-      this.pageNumber = pageNumber;
-      this._searchChangeSubject.next();
-    }
+  createResourceFormats(resourceId: string, resourceFormats: IResourceFormat[]): Observable<IResourceFormat> {
+    return from(resourceFormats).pipe(
+      concatMap((rf) => {
+        return this._httpClient.post<IResourceFormat>(`${this.resourceServiceUrl}${resourceId}/formats`, rf);
+      }),
+    );
+  }
 
-    reverseSortOrder(): void {
-      if (this.sortOptions.order === 'asc') {
-        this.sortOptions.order = 'desc';
-      } else {
-        this.sortOptions.order = 'asc';
-      }
-    }
+  updateResourceFormats(resourceId: string, resourceFormats: IResourceFormat[]) {
+    return from(resourceFormats).pipe(
+      concatMap((rf) => {
+        return this._httpClient.put<IResourceFormat>(`${this.resourceServiceUrl}${resourceId}/formats/${rf.id}`, rf);
+      }),
+    );
+  }
+
+  deleteResourceFormats(resourceId: string, resourceFormatsId: string[]) {
+    return from(resourceFormatsId).pipe(
+      concatMap((rfId) => {
+        return this._httpClient.delete<IResourceFormat>(`${this.resourceServiceUrl}${resourceId}/formats/${rfId}`);
+      }),
+    );
+  }
 
-    get searchChange$(): Observable<string> {
-      return this._searchChangeSubject.asObservable();
+  /* 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/assets/config/config.json b/src/assets/config/config.json
index 3320942c82b8a0ff6ae8e347c87e619f1ab91685..43cecd099741db2447135e4a96bc1d43339ac48c 100644
--- a/src/assets/config/config.json
+++ b/src/assets/config/config.json
@@ -1,8 +1,11 @@
 {
   "organizations": {
-    "url": "https://kong-dev.alpha.grandlyon.com/organizations/"
+    "url": "http://localhost:3000/organizations/"
   },
   "resources": {
-    "url": "https://kong-dev.alpha.grandlyon.com/resources/resources/"
+    "url": "http://localhost:3003/"
+  },
+  "mediaLibrary": {
+    "url": "http://localhost:3006/"
   }
-}
\ No newline at end of file
+}
diff --git a/src/assets/img/notif_error.svg b/src/assets/img/notif_error.svg
new file mode 100644
index 0000000000000000000000000000000000000000..7a66bc0ae16910cb1cb4a33a133b6837e2d6651d
--- /dev/null
+++ b/src/assets/img/notif_error.svg
@@ -0,0 +1,11 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
+  <style>
+    .white {
+      fill: white;
+    }
+  </style>
+  <g id="picto_x5F_warning">
+    <path d="M18.8 15.7c.2.3.2.7.2 1-.1.4-.2.6-.5.9-.3.2-.6.4-1 .4H2.6c-.4 0-.7-.1-1-.4-.3-.2-.4-.5-.5-.9-.1-.3 0-.7.2-1L8.8 2.8c.2-.3.4-.5.7-.7.4-.1.7-.1 1 0 .4.1.6.3.8.7l7.5 12.9zm-8.7-3c-.4 0-.7.1-1 .4-.3.3-.4.6-.4 1s.1.7.4 1c.3.3.6.4 1 .4s.7-.1 1-.4c.3-.3.4-.6.4-1s-.1-.7-.4-1c-.3-.2-.6-.4-1-.4zM8.8 8.1L9 12c0 .1 0 .2.1.2.1.1.2.1.3.1h1.4c.1 0 .2 0 .2-.1.1-.1.1-.2.1-.2l.2-3.9c0-.1 0-.2-.1-.3-.1-.1-.1-.1-.2-.1H9.2c-.1 0-.2 0-.3.1-.1.1-.1.2-.1.3z" class="white"/>
+    <path d="M18.8 15.7c.2.3.2.7.2 1-.1.4-.2.6-.5.9-.3.2-.6.4-1 .4H2.6c-.4 0-.7-.1-1-.4-.3-.2-.4-.5-.5-.9-.1-.3 0-.7.2-1L8.8 2.8c.2-.3.4-.5.7-.7.4-.1.7-.1 1 0 .4.1.6.3.8.7l7.5 12.9zm-8.7-3c-.4 0-.7.1-1 .4-.3.3-.4.6-.4 1s.1.7.4 1c.3.3.6.4 1 .4s.7-.1 1-.4c.3-.3.4-.6.4-1s-.1-.7-.4-1c-.3-.2-.6-.4-1-.4zM8.8 8.1L9 12c0 .1 0 .2.1.2.1.1.2.1.3.1h1.4c.1 0 .2 0 .2-.1.1-.1.1-.2.1-.2l.2-3.9c0-.1 0-.2-.1-.3-.1-.1-.1-.1-.2-.1H9.2c-.1 0-.2 0-.3.1-.1.1-.1.2-.1.3z" class="white"/>
+  </g>
+</svg>
diff --git a/src/assets/img/notif_ok.svg b/src/assets/img/notif_ok.svg
new file mode 100644
index 0000000000000000000000000000000000000000..d7d07dc1eb579c1765df18d638422344bb73e8b9
--- /dev/null
+++ b/src/assets/img/notif_ok.svg
@@ -0,0 +1,11 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
+  <style>
+    .white {
+      fill: white;
+    }
+  </style>
+  <g id="picto_x5F_ok">
+    <path d="M18.8 10.1c0 1.6-.4 3.1-1.2 4.4-.8 1.4-1.9 2.4-3.2 3.2-1.4.9-2.9 1.3-4.5 1.3s-3.1-.4-4.4-1.2c-1.4-.8-2.4-1.9-3.3-3.2-.8-1.4-1.2-2.8-1.2-4.4 0-1.6.4-3.1 1.2-4.4.8-1.4 1.9-2.4 3.3-3.2 1.4-.8 2.8-1.2 4.4-1.2 1.6 0 3.1.4 4.4 1.2 1.4.8 2.4 1.9 3.2 3.2.9 1.2 1.3 2.7 1.3 4.3zm-9.9 4.7l6.6-6.6c.1-.1.2-.2.2-.4s-.1-.3-.2-.4l-.8-.8c-.1-.1-.3-.2-.4-.2-.1 0-.3.1-.4.2L8.5 12 6 9.5c-.1-.1-.3-.2-.4-.2-.1 0-.3.1-.4.2l-.8.8c-.1.1-.2.2-.2.4s.1.3.2.4l3.7 3.7c.1.1.2.2.4.2s.3-.1.4-.2z" class="white"/>
+    <path d="M18.8 10.1c0 1.6-.4 3.1-1.2 4.4-.8 1.4-1.9 2.4-3.2 3.2-1.4.9-2.9 1.3-4.5 1.3s-3.1-.4-4.4-1.2c-1.4-.8-2.4-1.9-3.3-3.2-.8-1.4-1.2-2.8-1.2-4.4 0-1.6.4-3.1 1.2-4.4.8-1.4 1.9-2.4 3.3-3.2 1.4-.8 2.8-1.2 4.4-1.2 1.6 0 3.1.4 4.4 1.2 1.4.8 2.4 1.9 3.2 3.2.9 1.2 1.3 2.7 1.3 4.3zm-9.9 4.7l6.6-6.6c.1-.1.2-.2.2-.4s-.1-.3-.2-.4l-.8-.8c-.1-.1-.3-.2-.4-.2-.1 0-.3.1-.4.2L8.5 12 6 9.5c-.1-.1-.3-.2-.4-.2-.1 0-.3.1-.4.2l-.8.8c-.1.1-.2.2-.2.4s.1.3.2.4l3.7 3.7c.1.1.2.2.4.2s.3-.1.4-.2z" class="white"/>
+  </g>
+</svg>
diff --git a/src/assets/img/notif_warning.svg b/src/assets/img/notif_warning.svg
new file mode 100644
index 0000000000000000000000000000000000000000..f544dba648d1f2479de8a24a70cfa85f3ba2f283
--- /dev/null
+++ b/src/assets/img/notif_warning.svg
@@ -0,0 +1,11 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
+  <style>
+    .white {
+      fill: white;
+    }
+  </style>
+  <g id="picto_x5F_oops">
+    <path d="M17 11.3l1.6.9c.1.1.2.1.2.2s.1.2 0 .3c-.4 1.3-1.1 2.5-2.1 3.5-.1.1-.2.1-.3.2h-.3l-1.6-.9c-.7.6-1.4 1-2.3 1.3v1.8c0 .1 0 .2-.1.3 0 .1-.1.2-.2.2-1.4.3-2.7.3-4.1 0-.1 0-.2-.1-.3-.2 0-.1-.1-.2-.1-.3v-1.8c-.8-.3-1.6-.7-2.3-1.3l-1.6.9c-.1.1-.2.1-.3 0-.1 0-.2-.1-.3-.2-.9-1-1.6-2.2-2.1-3.5v-.3c0-.1.1-.2.2-.2l1.6-.9c-.1-.9-.1-1.8 0-2.6l-1.4-1-.2-.2v-.3c.4-1.3 1.1-2.5 2.1-3.5.1-.1.2-.1.3-.2h.3l1.6.9c.7-.6 1.4-1 2.3-1.3V1.3c0-.1 0-.2.1-.3.1-.1.2-.2.3-.2 1.4-.3 2.7-.3 4.1 0 .1 0 .2.1.2.2.1.1.1.2.1.3v1.8c.8.3 1.6.7 2.3 1.3l1.6-.9c.1-.1.2-.1.3 0 .1 0 .2.1.3.2.9 1 1.6 2.2 2.1 3.5v.3c0 .1-.1.2-.2.2l-1.8.9c.1.9.1 1.8 0 2.7zM13.1 10c0-.9-.3-1.6-1-2.3-.6-.6-1.4-1-2.3-1-.8 0-1.6.3-2.3 1-.6.6-.9 1.4-.9 2.3 0 .9.3 1.7.9 2.3.6.6 1.4.9 2.3.9.9 0 1.6-.3 2.3-.9.7-.7 1-1.5 1-2.3z" class="white"/>
+    <path d="M17 11.3l1.6.9c.1.1.2.1.2.2s.1.2 0 .3c-.4 1.3-1.1 2.5-2.1 3.5-.1.1-.2.1-.3.2h-.3l-1.6-.9c-.7.6-1.4 1-2.3 1.3v1.8c0 .1 0 .2-.1.3 0 .1-.1.2-.2.2-1.4.3-2.7.3-4.1 0-.1 0-.2-.1-.3-.2 0-.1-.1-.2-.1-.3v-1.8c-.8-.3-1.6-.7-2.3-1.3l-1.6.9c-.1.1-.2.1-.3 0-.1 0-.2-.1-.3-.2-.9-1-1.6-2.2-2.1-3.5v-.3c0-.1.1-.2.2-.2l1.6-.9c-.1-.9-.1-1.8 0-2.6l-1.4-1-.2-.2v-.3c.4-1.3 1.1-2.5 2.1-3.5.1-.1.2-.1.3-.2h.3l1.6.9c.7-.6 1.4-1 2.3-1.3V1.3c0-.1 0-.2.1-.3.1-.1.2-.2.3-.2 1.4-.3 2.7-.3 4.1 0 .1 0 .2.1.2.2.1.1.1.2.1.3v1.8c.8.3 1.6.7 2.3 1.3l1.6-.9c.1-.1.2-.1.3 0 .1 0 .2.1.3.2.9 1 1.6 2.2 2.1 3.5v.3c0 .1-.1.2-.2.2l-1.8.9c.1.9.1 1.8 0 2.7zM13.1 10c0-.9-.3-1.6-1-2.3-.6-.6-1.4-1-2.3-1-.8 0-1.6.3-2.3 1-.6.6-.9 1.4-.9 2.3 0 .9.3 1.7.9 2.3.6.6 1.4.9 2.3.9.9 0 1.6-.3 2.3-.9.7-.7 1-1.5 1-2.3z" class="white"/>
+  </g>
+</svg>
diff --git a/src/environments/environment.ts b/src/environments/environment.ts
index 0fe39f17326a0e65121c41612229dc0e808aed3d..da5ad8529bfb0812320aa37a2fc27e382d0dc913 100644
--- a/src/environments/environment.ts
+++ b/src/environments/environment.ts
@@ -5,4 +5,3 @@
 export const environment = {
   production: false,
 };
-
diff --git a/src/index.html b/src/index.html
index fffe33505be8307060536ab482fc2758dc8a3794..b0e833c746feb7b256d84120886d6ff18b45cefc 100644
--- a/src/index.html
+++ b/src/index.html
@@ -3,7 +3,7 @@
 
 <head>
   <meta charset="utf-8">
-  <title>GrandLyon</title>
+  <title>Plateforme data back office</title>
   <base href="/">
   <meta name="viewport" content="width=device-width, initial-scale=1">
   <link rel="icon" type="image/x-icon" href="assets/img/favicon.ico">
diff --git a/src/polyfills.ts b/src/polyfills.ts
index d310405a6817a4b84bb6117fb709e2c6ea11ef55..749bf310dfa5e49e016fa56a864a22924bbde19d 100644
--- a/src/polyfills.ts
+++ b/src/polyfills.ts
@@ -40,12 +40,10 @@
 /** IE10 and IE11 requires the following for the Reflect API. */
 // import 'core-js/es6/reflect';
 
-
 /** Evergreen browsers require these. **/
 // Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove.
 import 'core-js/es7/reflect';
 
-
 /**
  * Web Animations `@angular/platform-browser/animations`
  * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
@@ -73,8 +71,6 @@ import 'core-js/es7/reflect';
  */
 import 'zone.js/dist/zone';  // Included with Angular CLI.
 
-
-
 /***************************************************************************************************
  * APPLICATION IMPORTS
  */
diff --git a/src/styles.scss b/src/styles.scss
index b70b8572e5fdc6dd15e4da8edda5c78e7cdb7010..40d762110c62519cfb2acd2506d6c3d54a94ca23 100644
--- a/src/styles.scss
+++ b/src/styles.scss
@@ -2,11 +2,17 @@ $menu-item-color: white;
 $menu-item-hover-color: white;
 $menu-item-hover-background-color: #222222;
 $menu-item-active-background-color: #222222;
-// $menu-list-border-left: 1px solid $red;
+// $menu-list-border-left: 1px solid $tomato-color;
 
-$red: #d5232a;
+$tomato-color: #f72f2f;
 $dark-blue: #333745;
 
+$pagination-current-background-color: $tomato-color;
+$pagination-current-border-color: $tomato-color;
+$pagination-focus-border-color: $tomato-color;
+
+$button-focus-border-color: $tomato-color;
+
 @import "../node_modules/bulma/bulma.sass";
 
 html,
@@ -63,10 +69,32 @@ section {
 
 .required::after {
   content: " *";
-  color: $red;
+  color: $tomato-color;
 }
 
 input.ng-invalid:not(form).ng-dirty, input.ng-invalid:not(form).ng-touched,
-textarea.ng-invalid:not(form).ng-dirty, textarea.ng-invalid:not(form).ng-touched,{
-  border-left: 3px solid $red; /* red */
+textarea.ng-invalid:not(form).ng-dirty, textarea.ng-invalid:not(form).ng-touched {
+  border-left: 3px solid $tomato-color; /* red */
+}
+
+.button-gl {
+  min-width: 7rem;
+  background: $tomato-color;
+  border-radius: 2px;
+  border-width: 0;
+  font-size: $size-6;
+  color: white;
+  line-height: unset;
+
+  &:hover,
+  &:focus {
+    color: white;
+    background: $tomato-color;
+    opacity: 0.92;
+  }
+
+  &:disabled {
+    background: $tomato-color;
+    opacity: 0.38;
+  }
 }
\ No newline at end of file
diff --git a/src/test.ts b/src/test.ts
index 16317897b1c50a3a71bc775a8d6429f2b4c6cf98..b6d614daa243e0869709092a8276dc9d81e7c085 100644
--- a/src/test.ts
+++ b/src/test.ts
@@ -4,7 +4,7 @@ import 'zone.js/dist/zone-testing';
 import { getTestBed } from '@angular/core/testing';
 import {
   BrowserDynamicTestingModule,
-  platformBrowserDynamicTesting
+  platformBrowserDynamicTesting,
 } from '@angular/platform-browser-dynamic/testing';
 
 declare const require: any;
@@ -12,7 +12,7 @@ declare const require: any;
 // First, initialize the Angular testing environment.
 getTestBed().initTestEnvironment(
   BrowserDynamicTestingModule,
-  platformBrowserDynamicTesting()
+  platformBrowserDynamicTesting(),
 );
 // Then we find all the tests.
 const context = require.context('./', true, /\.spec\.ts$/);
diff --git a/src/tslint.json b/src/tslint.json
deleted file mode 100644
index 52e2c1a5a74ce268bec92d34cd3bca751624adb3..0000000000000000000000000000000000000000
--- a/src/tslint.json
+++ /dev/null
@@ -1,17 +0,0 @@
-{
-    "extends": "../tslint.json",
-    "rules": {
-        "directive-selector": [
-            true,
-            "attribute",
-            "app",
-            "camelCase"
-        ],
-        "component-selector": [
-            true,
-            "element",
-            "app",
-            "kebab-case"
-        ]
-    }
-}
diff --git a/tslint.json b/tslint.json
index 3ea984c776ef691c92b37ddf933df452cdc3d8bc..e43c8b8df38cc4c2463dd75196bd1715abead658 100644
--- a/tslint.json
+++ b/tslint.json
@@ -1,130 +1,17 @@
 {
+  "extends": [
+    "tslint-config-airbnb"
+  ],
   "rulesDirectory": [
-    "node_modules/codelyzer"
+    "node_modules/rxjs-tslint"
   ],
   "rules": {
-    "arrow-return-shorthand": true,
-    "callable-types": true,
-    "class-name": true,
-    "comment-format": [
-      true,
-      "check-space"
-    ],
-    "curly": true,
-    "deprecation": {
-      "severity": "warn"
-    },
-    "eofline": true,
-    "forin": true,
-    "import-blacklist": [
-      true,
-      "rxjs/Rx"
-    ],
-    "import-spacing": true,
-    "indent": [
-      true,
-      "spaces"
-    ],
-    "interface-over-type-literal": true,
-    "label-position": true,
-    "max-line-length": [
-      true,
-      140
-    ],
-    "member-access": false,
-    "member-ordering": [
-      true,
-      {
-        "order": [
-          "static-field",
-          "instance-field",
-          "static-method",
-          "instance-method"
-        ]
-      }
-    ],
-    "no-arg": true,
-    "no-bitwise": true,
-    "no-console": [
-      true,
-      "debug",
-      "info",
-      "time",
-      "timeEnd",
-      "trace"
-    ],
-    "no-construct": true,
-    "no-debugger": true,
-    "no-duplicate-super": true,
-    "no-empty": false,
-    "no-empty-interface": true,
-    "no-eval": true,
-    "no-inferrable-types": [
-      true,
-      "ignore-params"
-    ],
-    "no-misused-new": true,
-    "no-non-null-assertion": true,
-    "no-shadowed-variable": true,
-    "no-string-literal": false,
-    "no-string-throw": true,
-    "no-switch-case-fall-through": true,
-    "no-trailing-whitespace": true,
-    "no-unnecessary-initializer": true,
-    "no-unused-expression": true,
-    "no-use-before-declare": true,
-    "no-var-keyword": true,
-    "object-literal-sort-keys": false,
-    "one-line": [
-      true,
-      "check-open-brace",
-      "check-catch",
-      "check-else",
-      "check-whitespace"
-    ],
-    "prefer-const": true,
-    "quotemark": [
-      true,
-      "single"
-    ],
-    "radix": true,
-    "semicolon": [
-      true,
-      "always"
-    ],
-    "triple-equals": [
-      true,
-      "allow-null-check"
-    ],
-    "typedef-whitespace": [
-      true,
-      {
-        "call-signature": "nospace",
-        "index-signature": "nospace",
-        "parameter": "nospace",
-        "property-declaration": "nospace",
-        "variable-declaration": "nospace"
-      }
-    ],
-    "unified-signatures": true,
-    "variable-name": false,
-    "whitespace": [
-      true,
-      "check-branch",
-      "check-decl",
-      "check-operator",
-      "check-separator",
-      "check-type"
-    ],
-    "no-output-on-prefix": true,
-    "use-input-property-decorator": true,
-    "use-output-property-decorator": true,
-    "use-host-property-decorator": true,
-    "no-input-rename": true,
-    "no-output-rename": true,
-    "use-life-cycle-interface": true,
-    "use-pipe-transform-interface": true,
-    "component-class-suffix": true,
-    "directive-class-suffix": true
+    "variable-name": [true, "ban-keywords", "check-format", "allow-leading-underscore"],
+    "strict-boolean-expressions": [false],
+    "max-line-length": [true, 140],
+    "rxjs-collapse-imports": true,
+    "rxjs-pipeable-operators-only": true,
+    "rxjs-no-static-observable-methods": true,
+    "rxjs-proper-imports": true
   }
-}
+}
\ No newline at end of file