From b1aa589bc70e0ed72a96e6fed4a9600778971cea Mon Sep 17 00:00:00 2001
From: Chocobozzz <me@florianbigard.com>
Date: Fri, 21 Dec 2018 17:10:39 +0100
Subject: [PATCH] All components now use typescript

---
 js/package-lock.json                          | 952 +-----------------
 js/package.json                               |   4 +-
 js/src/App.vue                                | 100 +-
 js/src/components/Account/Account.vue         |  77 +-
 js/src/components/Account/Identities.vue      | 140 +--
 js/src/components/Account/Login.vue           | 116 +--
 js/src/components/Account/PasswordReset.vue   | 126 ++-
 js/src/components/Account/Register.vue        | 140 ++-
 js/src/components/Account/RegisterAvatar.vue  |  13 +-
 .../components/Account/ResendConfirmation.vue |  83 +-
 .../components/Account/SendPasswordReset.vue  |  88 +-
 js/src/components/Account/Validate.vue        |  58 +-
 js/src/components/Category/Create.vue         |  62 +-
 js/src/components/Category/List.vue           |  57 +-
 js/src/components/Event/Create.vue            | 161 ++-
 js/src/components/Event/Edit.vue              |  47 +-
 js/src/components/Event/Event.vue             | 419 ++++----
 js/src/components/Event/EventList.vue         | 218 ++--
 js/src/components/Group/Create.vue            | 133 +--
 js/src/components/Group/Group.vue             |  99 +-
 js/src/components/Group/GroupList.vue         |  85 +-
 js/src/components/Home.vue                    | 244 ++---
 js/src/components/Location.vue                |  33 +-
 js/src/components/NavBar.vue                  | 115 ++-
 js/src/graphql/event.ts                       |  10 +-
 js/src/graphql/search.ts                      |   2 +-
 js/src/registerServiceWorker.ts               |   3 +-
 js/src/shims-tsx.d.ts                         |   4 +-
 js/src/shims-vue.d.ts                         |   4 +-
 js/tslint.json                                |   3 +-
 30 files changed, 1348 insertions(+), 2248 deletions(-)

diff --git a/js/package-lock.json b/js/package-lock.json
index 7135bd603..651fa79d0 100644
--- a/js/package-lock.json
+++ b/js/package-lock.json
@@ -1198,37 +1198,6 @@
         "selenium-server": "^3.14.0"
       }
     },
-    "@vue/cli-plugin-eslint": {
-      "version": "3.1.5",
-      "resolved": "https://registry.npmjs.org/@vue/cli-plugin-eslint/-/cli-plugin-eslint-3.1.5.tgz",
-      "integrity": "sha512-ZcRJqqDeMxf5c6YkCT7nr29SGePBfyeuYfG8p3QVSND90nhS/952/G6ZAb7Ph1dNNsEONU4plQYnQAte6Caf4w==",
-      "dev": true,
-      "requires": {
-        "@vue/cli-shared-utils": "^3.1.1",
-        "babel-eslint": "^10.0.1",
-        "eslint": "^4.19.1",
-        "eslint-loader": "^2.1.1",
-        "eslint-plugin-vue": "^4.7.1",
-        "globby": "^8.0.1"
-      },
-      "dependencies": {
-        "globby": {
-          "version": "8.0.1",
-          "resolved": "http://registry.npmjs.org/globby/-/globby-8.0.1.tgz",
-          "integrity": "sha512-oMrYrJERnKBLXNLVTqhm3vPEdJ/b2ZE28xN4YARiix1NOIOBPEpOUnm844K1iu/BkphCaf2WNFwMszv8Soi1pw==",
-          "dev": true,
-          "requires": {
-            "array-union": "^1.0.1",
-            "dir-glob": "^2.0.0",
-            "fast-glob": "^2.0.2",
-            "glob": "^7.1.2",
-            "ignore": "^3.3.5",
-            "pify": "^3.0.0",
-            "slash": "^1.0.0"
-          }
-        }
-      }
-    },
     "@vue/cli-plugin-pwa": {
       "version": "3.1.2",
       "resolved": "https://registry.npmjs.org/@vue/cli-plugin-pwa/-/cli-plugin-pwa-3.1.2.tgz",
@@ -1504,17 +1473,6 @@
         }
       }
     },
-    "@vue/eslint-config-airbnb": {
-      "version": "3.0.5",
-      "resolved": "https://registry.npmjs.org/@vue/eslint-config-airbnb/-/eslint-config-airbnb-3.0.5.tgz",
-      "integrity": "sha512-kbsSFewrwffK2Bcw4yB5TzIfMQFJ0Grc1VOvCo3bup1Saub2RZy5RO3PzEBlDArIF/hkNrJHFxo0wMMarDBnBw==",
-      "dev": true,
-      "requires": {
-        "eslint-config-airbnb-base": "^12.1.0",
-        "eslint-import-resolver-webpack": "^0.9.0",
-        "eslint-plugin-import": "^2.11.0"
-      }
-    },
     "@vue/eslint-config-typescript": {
       "version": "3.2.0",
       "resolved": "https://registry.npmjs.org/@vue/eslint-config-typescript/-/eslint-config-typescript-3.2.0.tgz",
@@ -1816,23 +1774,6 @@
         "acorn": "^5.4.1"
       }
     },
-    "acorn-jsx": {
-      "version": "3.0.1",
-      "resolved": "http://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz",
-      "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=",
-      "dev": true,
-      "requires": {
-        "acorn": "^3.0.4"
-      },
-      "dependencies": {
-        "acorn": {
-          "version": "3.3.0",
-          "resolved": "http://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz",
-          "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=",
-          "dev": true
-        }
-      }
-    },
     "acorn-numeric-separator": {
       "version": "0.1.1",
       "resolved": "https://registry.npmjs.org/acorn-numeric-separator/-/acorn-numeric-separator-0.1.1.tgz",
@@ -1921,12 +1862,6 @@
       "integrity": "sha1-7PAh+hCP0X37Xms4Py3SM+Mf/Fk=",
       "dev": true
     },
-    "ajv-keywords": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-2.1.1.tgz",
-      "integrity": "sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I=",
-      "dev": true
-    },
     "align-text": {
       "version": "0.1.4",
       "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz",
@@ -1964,12 +1899,6 @@
       "integrity": "sha512-Xt+zb6nqgvV9SWAVp0EG3lRsHcbq5DDgqjPPz6pwgtj6RKz65zGXMNa82oJfOSBA/to6GmRP7Dr+6o+kbApTzQ==",
       "dev": true
     },
-    "ansi-escapes": {
-      "version": "3.1.0",
-      "resolved": "http://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz",
-      "integrity": "sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw==",
-      "dev": true
-    },
     "ansi-html": {
       "version": "0.0.7",
       "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz",
@@ -2402,12 +2331,6 @@
       "integrity": "sha1-fajPLiZijtcygDWB/SH2fKzS7uw=",
       "dev": true
     },
-    "array-find": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/array-find/-/array-find-1.0.0.tgz",
-      "integrity": "sha1-bI4obRHtdoMn+OYuzuhzU8o+eLg=",
-      "dev": true
-    },
     "array-find-index": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz",
@@ -2693,20 +2616,6 @@
         }
       }
     },
-    "babel-eslint": {
-      "version": "10.0.1",
-      "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.0.1.tgz",
-      "integrity": "sha512-z7OT1iNV+TjOwHNLLyJk+HN+YVWX+CLE6fPD2SymJZOZQBs+QIexFjhm4keGTm8MW9xr4EC9Q0PbaLB24V5GoQ==",
-      "dev": true,
-      "requires": {
-        "@babel/code-frame": "^7.0.0",
-        "@babel/parser": "^7.0.0",
-        "@babel/traverse": "^7.0.0",
-        "@babel/types": "^7.0.0",
-        "eslint-scope": "3.7.1",
-        "eslint-visitor-keys": "^1.0.0"
-      }
-    },
     "babel-extract-comments": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/babel-extract-comments/-/babel-extract-comments-1.0.0.tgz",
@@ -3328,21 +3237,6 @@
         }
       }
     },
-    "caller-path": {
-      "version": "0.1.0",
-      "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz",
-      "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=",
-      "dev": true,
-      "requires": {
-        "callsites": "^0.2.0"
-      }
-    },
-    "callsites": {
-      "version": "0.2.0",
-      "resolved": "http://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz",
-      "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=",
-      "dev": true
-    },
     "camel-case": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz",
@@ -3487,12 +3381,6 @@
         "is-regex": "^1.0.3"
       }
     },
-    "chardet": {
-      "version": "0.4.2",
-      "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz",
-      "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=",
-      "dev": true
-    },
     "charenc": {
       "version": "0.0.2",
       "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz",
@@ -3649,12 +3537,6 @@
         "safe-buffer": "^5.0.1"
       }
     },
-    "circular-json": {
-      "version": "0.3.3",
-      "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz",
-      "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==",
-      "dev": true
-    },
     "class-utils": {
       "version": "0.3.6",
       "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
@@ -3714,12 +3596,6 @@
       "integrity": "sha512-1QL4544moEsDVH9T/l6Cemov/37iv1RtoKf7NJ04A60+4MREXNfx/QvavbH6QoGdsD4N4Mwy49cmaINR/o2mdg==",
       "dev": true
     },
-    "cli-width": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz",
-      "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=",
-      "dev": true
-    },
     "clipboardy": {
       "version": "1.2.3",
       "resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-1.2.3.tgz",
@@ -4042,12 +3918,6 @@
       "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=",
       "dev": true
     },
-    "contains-path": {
-      "version": "0.1.0",
-      "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz",
-      "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=",
-      "dev": true
-    },
     "content-disposition": {
       "version": "0.5.2",
       "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz",
@@ -4930,15 +4800,6 @@
         "buffer-indexof": "^1.0.0"
       }
     },
-    "doctrine": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
-      "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
-      "dev": true,
-      "requires": {
-        "esutils": "^2.0.2"
-      }
-    },
     "doctypes": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/doctypes/-/doctypes-1.1.0.tgz",
@@ -5275,366 +5136,24 @@
       "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
     },
     "escodegen": {
-      "version": "1.11.0",
-      "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.11.0.tgz",
-      "integrity": "sha512-IeMV45ReixHS53K/OmfKAIztN/igDHzTJUhZM3k1jMhIZWjk45SMwAtBsEXiJp3vSPmTcu6CXn7mDvFHRN66fw==",
-      "dev": true,
-      "requires": {
-        "esprima": "^3.1.3",
-        "estraverse": "^4.2.0",
-        "esutils": "^2.0.2",
-        "optionator": "^0.8.1",
-        "source-map": "~0.6.1"
-      },
-      "dependencies": {
-        "source-map": {
-          "version": "0.6.1",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
-          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
-          "dev": true,
-          "optional": true
-        }
-      }
-    },
-    "eslint": {
-      "version": "4.19.1",
-      "resolved": "http://registry.npmjs.org/eslint/-/eslint-4.19.1.tgz",
-      "integrity": "sha512-bT3/1x1EbZB7phzYu7vCr1v3ONuzDtX8WjuM9c0iYxe+cq+pwcKEoQjl7zd3RpC6YOLgnSy3cTN58M2jcoPDIQ==",
-      "dev": true,
-      "requires": {
-        "ajv": "^5.3.0",
-        "babel-code-frame": "^6.22.0",
-        "chalk": "^2.1.0",
-        "concat-stream": "^1.6.0",
-        "cross-spawn": "^5.1.0",
-        "debug": "^3.1.0",
-        "doctrine": "^2.1.0",
-        "eslint-scope": "^3.7.1",
-        "eslint-visitor-keys": "^1.0.0",
-        "espree": "^3.5.4",
-        "esquery": "^1.0.0",
-        "esutils": "^2.0.2",
-        "file-entry-cache": "^2.0.0",
-        "functional-red-black-tree": "^1.0.1",
-        "glob": "^7.1.2",
-        "globals": "^11.0.1",
-        "ignore": "^3.3.3",
-        "imurmurhash": "^0.1.4",
-        "inquirer": "^3.0.6",
-        "is-resolvable": "^1.0.0",
-        "js-yaml": "^3.9.1",
-        "json-stable-stringify-without-jsonify": "^1.0.1",
-        "levn": "^0.3.0",
-        "lodash": "^4.17.4",
-        "minimatch": "^3.0.2",
-        "mkdirp": "^0.5.1",
-        "natural-compare": "^1.4.0",
-        "optionator": "^0.8.2",
-        "path-is-inside": "^1.0.2",
-        "pluralize": "^7.0.0",
-        "progress": "^2.0.0",
-        "regexpp": "^1.0.1",
-        "require-uncached": "^1.0.3",
-        "semver": "^5.3.0",
-        "strip-ansi": "^4.0.0",
-        "strip-json-comments": "~2.0.1",
-        "table": "4.0.2",
-        "text-table": "~0.2.0"
-      },
-      "dependencies": {
-        "ajv": {
-          "version": "5.5.2",
-          "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz",
-          "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=",
-          "dev": true,
-          "requires": {
-            "co": "^4.6.0",
-            "fast-deep-equal": "^1.0.0",
-            "fast-json-stable-stringify": "^2.0.0",
-            "json-schema-traverse": "^0.3.0"
-          }
-        },
-        "co": {
-          "version": "4.6.0",
-          "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
-          "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=",
-          "dev": true
-        },
-        "cross-spawn": {
-          "version": "5.1.0",
-          "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
-          "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
-          "dev": true,
-          "requires": {
-            "lru-cache": "^4.0.1",
-            "shebang-command": "^1.2.0",
-            "which": "^1.2.9"
-          }
-        },
-        "debug": {
-          "version": "3.2.6",
-          "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
-          "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
-          "dev": true,
-          "requires": {
-            "ms": "^2.1.1"
-          }
-        },
-        "fast-deep-equal": {
-          "version": "1.1.0",
-          "resolved": "http://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz",
-          "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=",
-          "dev": true
-        },
-        "json-schema-traverse": {
-          "version": "0.3.1",
-          "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz",
-          "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=",
-          "dev": true
-        }
-      }
-    },
-    "eslint-config-airbnb-base": {
-      "version": "12.1.0",
-      "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-12.1.0.tgz",
-      "integrity": "sha512-/vjm0Px5ZCpmJqnjIzcFb9TKZrKWz0gnuG/7Gfkt0Db1ELJR51xkZth+t14rYdqWgX836XbuxtArbIHlVhbLBA==",
-      "dev": true,
-      "requires": {
-        "eslint-restricted-globals": "^0.1.1"
-      }
-    },
-    "eslint-import-resolver-node": {
-      "version": "0.3.2",
-      "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz",
-      "integrity": "sha512-sfmTqJfPSizWu4aymbPr4Iidp5yKm8yDkHp+Ir3YiTHiiDfxh69mOUsmiqW6RZ9zRXFaF64GtYmN7e+8GHBv6Q==",
-      "dev": true,
-      "requires": {
-        "debug": "^2.6.9",
-        "resolve": "^1.5.0"
-      },
-      "dependencies": {
-        "debug": {
-          "version": "2.6.9",
-          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
-          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
-          "dev": true,
-          "requires": {
-            "ms": "2.0.0"
-          }
-        },
-        "ms": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
-          "dev": true
-        }
-      }
-    },
-    "eslint-import-resolver-webpack": {
-      "version": "0.9.0",
-      "resolved": "https://registry.npmjs.org/eslint-import-resolver-webpack/-/eslint-import-resolver-webpack-0.9.0.tgz",
-      "integrity": "sha1-IxzhV4rVEk2leZ8Cm9M9KBN2I+M=",
-      "dev": true,
-      "requires": {
-        "array-find": "^1.0.0",
-        "debug": "^2.6.8",
-        "enhanced-resolve": "~0.9.0",
-        "find-root": "^1.1.0",
-        "has": "^1.0.1",
-        "interpret": "^1.0.0",
-        "is-absolute": "^0.2.3",
-        "lodash.get": "^4.4.2",
-        "node-libs-browser": "^1.0.0 || ^2.0.0",
-        "resolve": "^1.4.0",
-        "semver": "^5.3.0"
-      },
-      "dependencies": {
-        "debug": {
-          "version": "2.6.9",
-          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
-          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
-          "dev": true,
-          "requires": {
-            "ms": "2.0.0"
-          }
-        },
-        "enhanced-resolve": {
-          "version": "0.9.1",
-          "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-0.9.1.tgz",
-          "integrity": "sha1-TW5omzcl+GCQknzMhs2fFjW4ni4=",
-          "dev": true,
-          "requires": {
-            "graceful-fs": "^4.1.2",
-            "memory-fs": "^0.2.0",
-            "tapable": "^0.1.8"
-          }
-        },
-        "memory-fs": {
-          "version": "0.2.0",
-          "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.2.0.tgz",
-          "integrity": "sha1-8rslNovBIeORwlIN6Slpyu4KApA=",
-          "dev": true
-        },
-        "ms": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
-          "dev": true
-        },
-        "tapable": {
-          "version": "0.1.10",
-          "resolved": "https://registry.npmjs.org/tapable/-/tapable-0.1.10.tgz",
-          "integrity": "sha1-KcNXB8K3DlDQdIK10gLo7URtr9Q=",
-          "dev": true
-        }
-      }
-    },
-    "eslint-loader": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/eslint-loader/-/eslint-loader-2.1.1.tgz",
-      "integrity": "sha512-1GrJFfSevQdYpoDzx8mEE2TDWsb/zmFuY09l6hURg1AeFIKQOvZ+vH0UPjzmd1CZIbfTV5HUkMeBmFiDBkgIsQ==",
-      "dev": true,
-      "requires": {
-        "loader-fs-cache": "^1.0.0",
-        "loader-utils": "^1.0.2",
-        "object-assign": "^4.0.1",
-        "object-hash": "^1.1.4",
-        "rimraf": "^2.6.1"
-      }
-    },
-    "eslint-module-utils": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.2.0.tgz",
-      "integrity": "sha1-snA2LNiLGkitMIl2zn+lTphBF0Y=",
-      "dev": true,
-      "requires": {
-        "debug": "^2.6.8",
-        "pkg-dir": "^1.0.0"
-      },
-      "dependencies": {
-        "debug": {
-          "version": "2.6.9",
-          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
-          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
-          "dev": true,
-          "requires": {
-            "ms": "2.0.0"
-          }
-        },
-        "find-up": {
-          "version": "1.1.2",
-          "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
-          "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=",
-          "dev": true,
-          "requires": {
-            "path-exists": "^2.0.0",
-            "pinkie-promise": "^2.0.0"
-          }
-        },
-        "ms": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
-          "dev": true
-        },
-        "path-exists": {
-          "version": "2.1.0",
-          "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz",
-          "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=",
-          "dev": true,
-          "requires": {
-            "pinkie-promise": "^2.0.0"
-          }
-        },
-        "pkg-dir": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-1.0.0.tgz",
-          "integrity": "sha1-ektQio1bstYp1EcFb/TpyTFM89Q=",
-          "dev": true,
-          "requires": {
-            "find-up": "^1.0.0"
-          }
-        }
-      }
-    },
-    "eslint-plugin-import": {
-      "version": "2.14.0",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.14.0.tgz",
-      "integrity": "sha512-FpuRtniD/AY6sXByma2Wr0TXvXJ4nA/2/04VPlfpmUDPOpOY264x+ILiwnrk/k4RINgDAyFZByxqPUbSQ5YE7g==",
-      "dev": true,
-      "requires": {
-        "contains-path": "^0.1.0",
-        "debug": "^2.6.8",
-        "doctrine": "1.5.0",
-        "eslint-import-resolver-node": "^0.3.1",
-        "eslint-module-utils": "^2.2.0",
-        "has": "^1.0.1",
-        "lodash": "^4.17.4",
-        "minimatch": "^3.0.3",
-        "read-pkg-up": "^2.0.0",
-        "resolve": "^1.6.0"
-      },
-      "dependencies": {
-        "debug": {
-          "version": "2.6.9",
-          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
-          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
-          "dev": true,
-          "requires": {
-            "ms": "2.0.0"
-          }
-        },
-        "doctrine": {
-          "version": "1.5.0",
-          "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz",
-          "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=",
-          "dev": true,
-          "requires": {
-            "esutils": "^2.0.2",
-            "isarray": "^1.0.0"
-          }
-        },
-        "ms": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
-          "dev": true
-        },
-        "path-type": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz",
-          "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=",
-          "dev": true,
-          "requires": {
-            "pify": "^2.0.0"
-          }
-        },
-        "pify": {
-          "version": "2.3.0",
-          "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
-          "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
-          "dev": true
-        },
-        "read-pkg": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz",
-          "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=",
-          "dev": true,
-          "requires": {
-            "load-json-file": "^2.0.0",
-            "normalize-package-data": "^2.3.2",
-            "path-type": "^2.0.0"
-          }
-        },
-        "read-pkg-up": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz",
-          "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=",
+      "version": "1.11.0",
+      "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.11.0.tgz",
+      "integrity": "sha512-IeMV45ReixHS53K/OmfKAIztN/igDHzTJUhZM3k1jMhIZWjk45SMwAtBsEXiJp3vSPmTcu6CXn7mDvFHRN66fw==",
+      "dev": true,
+      "requires": {
+        "esprima": "^3.1.3",
+        "estraverse": "^4.2.0",
+        "esutils": "^2.0.2",
+        "optionator": "^0.8.1",
+        "source-map": "~0.6.1"
+      },
+      "dependencies": {
+        "source-map": {
+          "version": "0.6.1",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
           "dev": true,
-          "requires": {
-            "find-up": "^2.0.0",
-            "read-pkg": "^2.0.0"
-          }
+          "optional": true
         }
       }
     },
@@ -5647,31 +5166,6 @@
         "requireindex": "~1.1.0"
       }
     },
-    "eslint-plugin-vue": {
-      "version": "4.7.1",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-4.7.1.tgz",
-      "integrity": "sha512-esETKhVMI7Vdli70Wt4bvAwnZBJeM0pxVX9Yb0wWKxdCJc2EADalVYK/q2FzMw8oKN0wPMdqVCKS8kmR89recA==",
-      "dev": true,
-      "requires": {
-        "vue-eslint-parser": "^2.0.3"
-      }
-    },
-    "eslint-restricted-globals": {
-      "version": "0.1.1",
-      "resolved": "https://registry.npmjs.org/eslint-restricted-globals/-/eslint-restricted-globals-0.1.1.tgz",
-      "integrity": "sha1-NfDVy8ZMLj7WLpO0saevBbp+1Nc=",
-      "dev": true
-    },
-    "eslint-scope": {
-      "version": "3.7.1",
-      "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.1.tgz",
-      "integrity": "sha1-PWPD7f2gLgbgGkUq2IyqzHzctug=",
-      "dev": true,
-      "requires": {
-        "esrecurse": "^4.1.0",
-        "estraverse": "^4.1.1"
-      }
-    },
     "eslint-visitor-keys": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz",
@@ -5684,31 +5178,12 @@
       "integrity": "sha512-SzSGoZc17S7P+12R9cg21Bdb7eybX25RnIeRZ80xZs+VZ3kdQKzqTp2k4hZJjR7p9l0186TTXSgrxzlMDBktlw==",
       "dev": true
     },
-    "espree": {
-      "version": "3.5.4",
-      "resolved": "http://registry.npmjs.org/espree/-/espree-3.5.4.tgz",
-      "integrity": "sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A==",
-      "dev": true,
-      "requires": {
-        "acorn": "^5.5.0",
-        "acorn-jsx": "^3.0.0"
-      }
-    },
     "esprima": {
       "version": "3.1.3",
       "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz",
       "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=",
       "dev": true
     },
-    "esquery": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz",
-      "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==",
-      "dev": true,
-      "requires": {
-        "estraverse": "^4.0.0"
-      }
-    },
     "esrecurse": {
       "version": "4.2.1",
       "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz",
@@ -5925,17 +5400,6 @@
         }
       }
     },
-    "external-editor": {
-      "version": "2.2.0",
-      "resolved": "http://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz",
-      "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==",
-      "dev": true,
-      "requires": {
-        "chardet": "^0.4.0",
-        "iconv-lite": "^0.4.17",
-        "tmp": "^0.0.33"
-      }
-    },
     "extglob": {
       "version": "2.0.4",
       "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
@@ -6109,25 +5573,6 @@
       "integrity": "sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w==",
       "dev": true
     },
-    "figures": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz",
-      "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=",
-      "dev": true,
-      "requires": {
-        "escape-string-regexp": "^1.0.5"
-      }
-    },
-    "file-entry-cache": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz",
-      "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=",
-      "dev": true,
-      "requires": {
-        "flat-cache": "^1.2.1",
-        "object-assign": "^4.0.1"
-      }
-    },
     "file-loader": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-2.0.0.tgz",
@@ -6241,12 +5686,6 @@
         "pkg-dir": "^2.0.0"
       }
     },
-    "find-root": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz",
-      "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==",
-      "dev": true
-    },
     "find-up": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
@@ -6256,18 +5695,6 @@
         "locate-path": "^2.0.0"
       }
     },
-    "flat-cache": {
-      "version": "1.3.4",
-      "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.4.tgz",
-      "integrity": "sha512-VwyB3Lkgacfik2vhqR4uv2rvebqmDvFu4jlN/C1RzWoJEo8I7z4Q404oiqYCkq41mni8EzQnm95emU9seckwtg==",
-      "dev": true,
-      "requires": {
-        "circular-json": "^0.3.1",
-        "graceful-fs": "^4.1.2",
-        "rimraf": "~2.6.2",
-        "write": "^0.2.1"
-      }
-    },
     "flush-write-stream": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.0.3.tgz",
@@ -7036,12 +6463,6 @@
       "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
       "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
     },
-    "functional-red-black-tree": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
-      "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=",
-      "dev": true
-    },
     "gauge": {
       "version": "2.7.4",
       "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
@@ -8064,28 +7485,6 @@
       "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
       "dev": true
     },
-    "inquirer": {
-      "version": "3.3.0",
-      "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.3.0.tgz",
-      "integrity": "sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==",
-      "dev": true,
-      "requires": {
-        "ansi-escapes": "^3.0.0",
-        "chalk": "^2.0.0",
-        "cli-cursor": "^2.1.0",
-        "cli-width": "^2.0.0",
-        "external-editor": "^2.0.4",
-        "figures": "^2.0.0",
-        "lodash": "^4.3.0",
-        "mute-stream": "0.0.7",
-        "run-async": "^2.2.0",
-        "rx-lite": "^4.0.8",
-        "rx-lite-aggregates": "^4.0.8",
-        "string-width": "^2.1.0",
-        "strip-ansi": "^4.0.0",
-        "through": "^2.3.6"
-      }
-    },
     "internal-ip": {
       "version": "3.0.1",
       "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-3.0.1.tgz",
@@ -8141,24 +7540,6 @@
       "integrity": "sha1-6qM9bd16zo9/b+DJygRA5wZzix4=",
       "dev": true
     },
-    "is-absolute": {
-      "version": "0.2.6",
-      "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-0.2.6.tgz",
-      "integrity": "sha1-IN5p89uULvLYe5wto28XIjWxtes=",
-      "dev": true,
-      "requires": {
-        "is-relative": "^0.2.1",
-        "is-windows": "^0.2.0"
-      },
-      "dependencies": {
-        "is-windows": {
-          "version": "0.2.0",
-          "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-0.2.0.tgz",
-          "integrity": "sha1-3hqm1j6indJIc3tp8f+LgALSEIw=",
-          "dev": true
-        }
-      }
-    },
     "is-absolute-url": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz",
@@ -8420,15 +7801,6 @@
       "integrity": "sha1-/S2INUXEa6xaYz57mgnof6LLUGk=",
       "dev": true
     },
-    "is-relative": {
-      "version": "0.2.1",
-      "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-0.2.1.tgz",
-      "integrity": "sha1-0n9MfVFtF1+2ENuEu+7yPDvJeqU=",
-      "dev": true,
-      "requires": {
-        "is-unc-path": "^0.1.1"
-      }
-    },
     "is-resolvable": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz",
@@ -8471,15 +7843,6 @@
       "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
       "dev": true
     },
-    "is-unc-path": {
-      "version": "0.1.2",
-      "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-0.1.2.tgz",
-      "integrity": "sha1-arBTpyVzwQJQ/0FqOBTDUXivObk=",
-      "dev": true,
-      "requires": {
-        "unc-path-regex": "^0.1.0"
-      }
-    },
     "is-url": {
       "version": "1.2.4",
       "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz",
@@ -8721,12 +8084,6 @@
         "jsonify": "~0.0.0"
       }
     },
-    "json-stable-stringify-without-jsonify": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
-      "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=",
-      "dev": true
-    },
     "json-stringify-safe": {
       "version": "5.0.1",
       "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
@@ -8863,77 +8220,6 @@
         "uc.micro": "^1.0.1"
       }
     },
-    "load-json-file": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz",
-      "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=",
-      "dev": true,
-      "requires": {
-        "graceful-fs": "^4.1.2",
-        "parse-json": "^2.2.0",
-        "pify": "^2.0.0",
-        "strip-bom": "^3.0.0"
-      },
-      "dependencies": {
-        "pify": {
-          "version": "2.3.0",
-          "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
-          "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
-          "dev": true
-        }
-      }
-    },
-    "loader-fs-cache": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/loader-fs-cache/-/loader-fs-cache-1.0.1.tgz",
-      "integrity": "sha1-VuC/CL2XCLJqdltoUJhAyN7J/bw=",
-      "dev": true,
-      "requires": {
-        "find-cache-dir": "^0.1.1",
-        "mkdirp": "0.5.1"
-      },
-      "dependencies": {
-        "find-cache-dir": {
-          "version": "0.1.1",
-          "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-0.1.1.tgz",
-          "integrity": "sha1-yN765XyKUqinhPnjHFfHQumToLk=",
-          "dev": true,
-          "requires": {
-            "commondir": "^1.0.1",
-            "mkdirp": "^0.5.1",
-            "pkg-dir": "^1.0.0"
-          }
-        },
-        "find-up": {
-          "version": "1.1.2",
-          "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
-          "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=",
-          "dev": true,
-          "requires": {
-            "path-exists": "^2.0.0",
-            "pinkie-promise": "^2.0.0"
-          }
-        },
-        "path-exists": {
-          "version": "2.1.0",
-          "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz",
-          "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=",
-          "dev": true,
-          "requires": {
-            "pinkie-promise": "^2.0.0"
-          }
-        },
-        "pkg-dir": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-1.0.0.tgz",
-          "integrity": "sha1-ektQio1bstYp1EcFb/TpyTFM89Q=",
-          "dev": true,
-          "requires": {
-            "find-up": "^1.0.0"
-          }
-        }
-      }
-    },
     "loader-runner": {
       "version": "2.3.1",
       "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.3.1.tgz",
@@ -9112,12 +8398,6 @@
         }
       }
     },
-    "lodash.get": {
-      "version": "4.4.2",
-      "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
-      "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=",
-      "dev": true
-    },
     "lodash.isarguments": {
       "version": "3.1.0",
       "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz",
@@ -9981,12 +9261,6 @@
       "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=",
       "dev": true
     },
-    "mute-stream": {
-      "version": "0.0.7",
-      "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz",
-      "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=",
-      "dev": true
-    },
     "nan": {
       "version": "2.11.1",
       "resolved": "https://registry.npmjs.org/nan/-/nan-2.11.1.tgz",
@@ -10020,12 +9294,6 @@
         }
       }
     },
-    "natural-compare": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
-      "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
-      "dev": true
-    },
     "negotiator": {
       "version": "0.6.1",
       "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz",
@@ -10045,9 +9313,9 @@
       "dev": true
     },
     "ngeohash": {
-      "version": "0.6.0",
-      "resolved": "https://registry.npmjs.org/ngeohash/-/ngeohash-0.6.0.tgz",
-      "integrity": "sha1-MpcT6ec9HxpG2SqrC5StuUUz9oc="
+      "version": "0.6.3",
+      "resolved": "https://registry.npmjs.org/ngeohash/-/ngeohash-0.6.3.tgz",
+      "integrity": "sha512-kltF0cOxgx1AbmVzKxYZaoB0aj7mOxZeHaerEtQV0YaqnkXNq26WWqMmJ6lTqShYxVRWZ/mwvvTrNeOwdslWiw=="
     },
     "nice-try": {
       "version": "1.0.5",
@@ -10420,12 +9688,6 @@
         }
       }
     },
-    "object-hash": {
-      "version": "1.3.1",
-      "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-1.3.1.tgz",
-      "integrity": "sha512-OSuu/pU4ENM9kmREg0BdNrUDIl1heYa4mBZacJc+vVWz4GtAwu7jO8s4AIt2aGRUTqxykpWzI3Oqnsm13tTMDA==",
-      "dev": true
-    },
     "object-keys": {
       "version": "1.0.12",
       "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.12.tgz",
@@ -10978,12 +10240,6 @@
         "find-up": "^2.1.0"
       }
     },
-    "pluralize": {
-      "version": "7.0.0",
-      "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz",
-      "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==",
-      "dev": true
-    },
     "pn": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz",
@@ -12222,12 +11478,6 @@
         "safe-regex": "^1.1.0"
       }
     },
-    "regexpp": {
-      "version": "1.1.0",
-      "resolved": "http://registry.npmjs.org/regexpp/-/regexpp-1.1.0.tgz",
-      "integrity": "sha512-LOPw8FpgdQF9etWMaAfG/WRthIdXJGYp4mJ2Jgn/2lpkbod9jPn0t9UqN7AxBOKNfzRbYyVfgc7Vk4t/MpnXgw==",
-      "dev": true
-    },
     "regexpu-core": {
       "version": "4.2.0",
       "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.2.0.tgz",
@@ -12439,16 +11689,6 @@
       "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=",
       "dev": true
     },
-    "require-uncached": {
-      "version": "1.0.3",
-      "resolved": "http://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz",
-      "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=",
-      "dev": true,
-      "requires": {
-        "caller-path": "^0.1.0",
-        "resolve-from": "^1.0.0"
-      }
-    },
     "requireindex": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/requireindex/-/requireindex-1.1.0.tgz",
@@ -12486,12 +11726,6 @@
         }
       }
     },
-    "resolve-from": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz",
-      "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=",
-      "dev": true
-    },
     "resolve-url": {
       "version": "0.2.1",
       "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
@@ -12559,15 +11793,6 @@
         "inherits": "^2.0.1"
       }
     },
-    "run-async": {
-      "version": "2.3.0",
-      "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz",
-      "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=",
-      "dev": true,
-      "requires": {
-        "is-promise": "^2.1.0"
-      }
-    },
     "run-queue": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz",
@@ -12577,21 +11802,6 @@
         "aproba": "^1.1.1"
       }
     },
-    "rx-lite": {
-      "version": "4.0.8",
-      "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-4.0.8.tgz",
-      "integrity": "sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ=",
-      "dev": true
-    },
-    "rx-lite-aggregates": {
-      "version": "4.0.8",
-      "resolved": "https://registry.npmjs.org/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz",
-      "integrity": "sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74=",
-      "dev": true,
-      "requires": {
-        "rx-lite": "*"
-      }
-    },
     "rxjs": {
       "version": "6.2.2",
       "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.2.2.tgz",
@@ -13061,15 +12271,6 @@
       "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=",
       "dev": true
     },
-    "slice-ansi": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz",
-      "integrity": "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==",
-      "dev": true,
-      "requires": {
-        "is-fullwidth-code-point": "^2.0.0"
-      }
-    },
     "smart-buffer": {
       "version": "1.1.15",
       "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-1.1.15.tgz",
@@ -13608,12 +12809,6 @@
         "ansi-regex": "^3.0.0"
       }
     },
-    "strip-bom": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
-      "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
-      "dev": true
-    },
     "strip-comments": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/strip-comments/-/strip-comments-1.0.2.tgz",
@@ -13731,52 +12926,6 @@
       "integrity": "sha1-rifbOPZgp64uHDt9G8KQgZuFGeY=",
       "dev": true
     },
-    "table": {
-      "version": "4.0.2",
-      "resolved": "https://registry.npmjs.org/table/-/table-4.0.2.tgz",
-      "integrity": "sha512-UUkEAPdSGxtRpiV9ozJ5cMTtYiqz7Ni1OGqLXRCynrvzdtR1p+cfOWe2RJLwvUG8hNanaSRjecIqwOjqeatDsA==",
-      "dev": true,
-      "requires": {
-        "ajv": "^5.2.3",
-        "ajv-keywords": "^2.1.0",
-        "chalk": "^2.1.0",
-        "lodash": "^4.17.4",
-        "slice-ansi": "1.0.0",
-        "string-width": "^2.1.1"
-      },
-      "dependencies": {
-        "ajv": {
-          "version": "5.5.2",
-          "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz",
-          "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=",
-          "dev": true,
-          "requires": {
-            "co": "^4.6.0",
-            "fast-deep-equal": "^1.0.0",
-            "fast-json-stable-stringify": "^2.0.0",
-            "json-schema-traverse": "^0.3.0"
-          }
-        },
-        "co": {
-          "version": "4.6.0",
-          "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
-          "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=",
-          "dev": true
-        },
-        "fast-deep-equal": {
-          "version": "1.1.0",
-          "resolved": "http://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz",
-          "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=",
-          "dev": true
-        },
-        "json-schema-traverse": {
-          "version": "0.3.1",
-          "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz",
-          "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=",
-          "dev": true
-        }
-      }
-    },
     "tapable": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.1.tgz",
@@ -14022,12 +13171,6 @@
         }
       }
     },
-    "text-table": {
-      "version": "0.2.0",
-      "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
-      "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=",
-      "dev": true
-    },
     "thread-loader": {
       "version": "1.2.0",
       "resolved": "https://registry.npmjs.org/thread-loader/-/thread-loader-1.2.0.tgz",
@@ -14055,12 +13198,6 @@
       "resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-2.0.1.tgz",
       "integrity": "sha512-Sr6jZBlWShsAaSXKyNXyNicOrJW/KtkDqIEwHt4wYwWA2wa/q67Luhqoujg48V8hTk60wB56tYrJJn6jc2R7VA=="
     },
-    "through": {
-      "version": "2.3.8",
-      "resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz",
-      "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
-      "dev": true
-    },
     "through2": {
       "version": "2.0.5",
       "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
@@ -14104,15 +13241,6 @@
       "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=",
       "dev": true
     },
-    "tmp": {
-      "version": "0.0.33",
-      "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
-      "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
-      "dev": true,
-      "requires": {
-        "os-tmpdir": "~1.0.2"
-      }
-    },
     "to-arraybuffer": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz",
@@ -14561,12 +13689,6 @@
       "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=",
       "optional": true
     },
-    "unc-path-regex": {
-      "version": "0.1.2",
-      "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz",
-      "integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo=",
-      "dev": true
-    },
     "undefsafe": {
       "version": "2.0.2",
       "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.2.tgz",
@@ -15058,31 +14180,6 @@
         }
       }
     },
-    "vue-eslint-parser": {
-      "version": "2.0.3",
-      "resolved": "http://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-2.0.3.tgz",
-      "integrity": "sha512-ZezcU71Owm84xVF6gfurBQUGg8WQ+WZGxgDEQu1IHFBZNx7BFZg3L1yHxrCBNNwbwFtE1GuvfJKMtb6Xuwc/Bw==",
-      "dev": true,
-      "requires": {
-        "debug": "^3.1.0",
-        "eslint-scope": "^3.7.1",
-        "eslint-visitor-keys": "^1.0.0",
-        "espree": "^3.5.2",
-        "esquery": "^1.0.0",
-        "lodash": "^4.17.4"
-      },
-      "dependencies": {
-        "debug": {
-          "version": "3.2.6",
-          "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
-          "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
-          "dev": true,
-          "requires": {
-            "ms": "^2.1.1"
-          }
-        }
-      }
-    },
     "vue-gettext": {
       "version": "2.1.1",
       "resolved": "https://registry.npmjs.org/vue-gettext/-/vue-gettext-2.1.1.tgz",
@@ -15996,15 +15093,6 @@
       "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
       "dev": true
     },
-    "write": {
-      "version": "0.2.1",
-      "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz",
-      "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=",
-      "dev": true,
-      "requires": {
-        "mkdirp": "^0.5.1"
-      }
-    },
     "write-file-atomic": {
       "version": "2.3.0",
       "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.3.0.tgz",
diff --git a/js/package.json b/js/package.json
index 36c53616e..cd3512c38 100644
--- a/js/package.json
+++ b/js/package.json
@@ -19,7 +19,7 @@
     "graphql-tag": "^2.9.0",
     "material-design-icons": "^3.0.1",
     "moment": "^2.22.2",
-    "ngeohash": "^0.6.0",
+    "ngeohash": "^0.6.3",
     "register-service-worker": "^1.4.1",
     "vue": "^2.5.17",
     "vue-apollo": "^3.0.0-beta.26",
@@ -38,12 +38,10 @@
     "@types/mocha": "^5.2.4",
     "@vue/cli-plugin-babel": "^3.1.1",
     "@vue/cli-plugin-e2e-nightwatch": "^3.1.1",
-    "@vue/cli-plugin-eslint": "^3.1.5",
     "@vue/cli-plugin-pwa": "^3.1.2",
     "@vue/cli-plugin-typescript": "^3.2.0",
     "@vue/cli-plugin-unit-mocha": "^3.1.1",
     "@vue/cli-service": "^3.1.4",
-    "@vue/eslint-config-airbnb": "^3.0.5",
     "@vue/eslint-config-typescript": "^3.1.0",
     "@vue/test-utils": "^1.0.0-beta.26",
     "chai": "^4.2.0",
diff --git a/js/src/App.vue b/js/src/App.vue
index 38879d4c3..3aefce0ad 100644
--- a/js/src/App.vue
+++ b/js/src/App.vue
@@ -150,64 +150,62 @@
 </template>
 
 <script lang="ts">
-  import NavBar from '@/components/NavBar.vue';
-  import { Component, Vue } from 'vue-property-decorator';
-  import { AUTH_USER_ACTOR, AUTH_USER_ID } from '@/constants';
+import NavBar from '@/components/NavBar.vue';
+import { Component, Vue } from 'vue-property-decorator';
+import { AUTH_USER_ACTOR, AUTH_USER_ID } from '@/constants';
 
-  @Component({
-    components: {
-      NavBar
-    }
-  })
-  export default class App extends Vue {
-    drawer = false
-    fab = false
-    user = localStorage.getItem(AUTH_USER_ID)
-    items = [
-      {
-        icon: 'poll', text: 'Events', route: 'EventList', role: null
-      },
-      {
-        icon: 'group', text: 'Groups', route: 'GroupList', role: null
-      },
-      {
-        icon: 'content_copy', text: 'Categories', route: 'CategoryList', role: 'ROLE_ADMIN'
-      },
-      { icon: 'settings', text: 'Settings', role: 'ROLE_USER' },
-      { icon: 'chat_bubble', text: 'Send feedback', role: 'ROLE_USER' },
-      { icon: 'help', text: 'Help', role: null },
-      { icon: 'phonelink', text: 'App downloads', role: null }
-    ]
-    error = {
-      timeout: 3000,
-      show: false,
-      text: ''
-    }
+@Component({
+  components: {
+    NavBar,
+  },
+})
+export default class App extends Vue {
+  drawer = false;
+  fab = false;
+  user = localStorage.getItem(AUTH_USER_ID);
+  items = [
+    {
+      icon: 'poll', text: 'Events', route: 'EventList', role: null,
+    },
+    {
+      icon: 'group', text: 'Groups', route: 'GroupList', role: null,
+    },
+    {
+      icon: 'content_copy', text: 'Categories', route: 'CategoryList', role: 'ROLE_ADMIN',
+    },
+    { icon: 'settings', text: 'Settings', role: 'ROLE_USER' },
+    { icon: 'chat_bubble', text: 'Send feedback', role: 'ROLE_USER' },
+    { icon: 'help', text: 'Help', role: null },
+    { icon: 'phonelink', text: 'App downloads', role: null },
+  ];
+  error = {
+    timeout: 3000,
+    show: false,
+    text: '',
+  };
 
-    show_new_event_button = false
-    actor = localStorage.getItem(AUTH_USER_ACTOR)
+  actor = localStorage.getItem(AUTH_USER_ACTOR);
 
-    get displayed_name () {
-      // FIXME: load actor
-      return 'no implemented'
-      // return this.actor.display_name === null ? this.actor.username : this.actor.display_name
-    }
-
-    showMenuItem (elem) {
-      // FIXME: load actor
-      return false
-      // return elem !== null && this.user && this.user.roles !== undefined ? this.user.roles.includes(elem) : true
-    }
+  get displayed_name () {
+    // FIXME: load actor
+    return 'no implemented';
+    // return this.actor.display_name === null ? this.actor.username : this.actor.display_name
+  }
 
-    getUser () {
-      return this.user === undefined ? false : this.user
-    }
+  showMenuItem (elem) {
+    // FIXME: load actor
+    return false;
+    // return elem !== null && this.user && this.user.roles !== undefined ? this.user.roles.includes(elem) : true
+  }
 
-    toggleDrawer () {
-      this.drawer = !this.drawer
-    }
+  getUser () {
+    return this.user === undefined ? false : this.user;
   }
 
+  toggleDrawer () {
+    this.drawer = !this.drawer;
+  }
+}
 </script>
 
 <style>
diff --git a/js/src/components/Account/Account.vue b/js/src/components/Account/Account.vue
index 068348637..0f9b06a11 100644
--- a/js/src/components/Account/Account.vue
+++ b/js/src/components/Account/Account.vue
@@ -31,12 +31,12 @@
             <div class="text-xs-center">
               <v-avatar size="125px">
                 <img v-if="!actor.avatarUrl"
-                  class="img-circle elevation-7 mb-1"
-                  src="https://picsum.photos/125/125/"
+                     class="img-circle elevation-7 mb-1"
+                     src="https://picsum.photos/125/125/"
                 >
                 <img v-else
-                      class="img-circle elevation-7 mb-1"
-                      :src="actor.avatarUrl"
+                     class="img-circle elevation-7 mb-1"
+                     :src="actor.avatarUrl"
                 >
               </v-avatar>
             </div>
@@ -44,7 +44,8 @@
               <v-layout row>
                 <v-flex xs7>
                   <div class="headline">{{ actor.name }}</div>
-                  <div><span class="subheading">@{{ actor.preferredUsername }}<span v-if="actor.domain">@{{ actor.domain }}</span></span></div>
+                  <div><span class="subheading">@{{ actor.preferredUsername }}<span v-if="actor.domain">@{{ actor.domain }}</span></span>
+                  </div>
                   <v-card-text v-if="actor.description" v-html="actor.description"></v-card-text>
                 </v-flex>
               </v-layout>
@@ -107,7 +108,9 @@
                   <div>
                     <span class="grey--text">{{ event.startDate | formatDate }} à {{ event.location }}</span><br>
                     <p>{{ event.description }}</p>
-                    <p v-if="event.organizer">Organisé par <router-link :to="{name: 'Account', params: {'id': event.organizer.id}}">{{ event.organizer.username }}</router-link></p>
+                    <p v-if="event.organizer">Organisé par
+                      <router-link :to="{name: 'Account', params: {'id': event.organizer.id}}">{{ event.organizer.username }}</router-link>
+                    </p>
                   </div>
                 </v-card-title>
                 <v-card-actions>
@@ -143,7 +146,7 @@
                     <span class="grey--text" v-html="nl2br(event.description)"></span>
                   </div>
                 </v-card-title>
-                
+
                 <!-- <v-card-title>
                   <div>
                     <span class="grey--text" v-if="event.addressType === 'physical'">{{ event.startDate }} à {{ event.location }}</span><br>
@@ -171,46 +174,40 @@
   </v-layout>
 </template>
 
-<script>
-import { FETCH_ACTOR } from '@/graphql/actor';
+<script lang="ts">
+  import { FETCH_ACTOR } from '@/graphql/actor';
+  import { Component, Prop, Vue, Watch } from 'vue-property-decorator';
 
-export default {
-  name: 'Account',
-  data() {
-    return {
-      actor: null,
-    };
-  },
-  props: {
-    name: {
-      type: String,
-      required: true,
-    },
-  },
-  apollo: {
-    actor: {
-      query: FETCH_ACTOR,
-      variables() {
-        return {
-          name: this.$route.params.name,
-        };
+  @Component({
+    apollo: {
+      actor: {
+        query: FETCH_ACTOR,
+        variables() {
+          return {
+            name: this.$route.params.name,
+          };
+        },
       },
     },
-  },
-  created() {
-  },
-  watch: {
+  })
+  export default class Account extends Vue {
+    @Prop({ type: String, required: true }) name!: string;
+
+    actor = null;
+
     // call again the method if the route changes
-    $route: 'fetchData',
-  },
-  methods: {
+    @Watch('$route')
+    onRouteChange() {
+      // this.fetchData()
+    }
+
     logoutUser() {
       // TODO : implement logout
       this.$router.push({ name: 'Home' });
-    },
-    nl2br: function(text) {
+    }
+
+    nl2br(text) {
       return text.replace(/(?:\r\n|\r|\n)/g, '<br>');
     }
-  },
-};
+  };
 </script>
diff --git a/js/src/components/Account/Identities.vue b/js/src/components/Account/Identities.vue
index 1e65e5437..0c468c26a 100644
--- a/js/src/components/Account/Identities.vue
+++ b/js/src/components/Account/Identities.vue
@@ -1,11 +1,11 @@
 <template>
   <v-layout row>
-  <v-flex xs12 sm6 offset-sm3>
-    <v-progress-circular v-if="loading" indeterminate color="primary"></v-progress-circular>
-    <v-card v-if="!loading">
-      <v-toolbar dark color="primary">
-        <v-toolbar-title>Identities</v-toolbar-title>
-      </v-toolbar>
+    <v-flex xs12 sm6 offset-sm3>
+      <v-progress-circular v-if="loading" indeterminate color="primary"></v-progress-circular>
+      <v-card v-if="!loading">
+        <v-toolbar dark color="primary">
+          <v-toolbar-title>Identities</v-toolbar-title>
+        </v-toolbar>
         <v-card-text>
           <v-list two-line>
             <v-list-tile
@@ -31,22 +31,22 @@
           <v-divider v-if="showForm"></v-divider>
           <v-form v-if="showForm">
             <v-text-field
-                label="Username"
-                required
-                type="text"
-                v-model="newActor.preferred_username"
-                :rules="[rules.required]"
-                :error="this.state.username.status"
-                :error-messages="this.state.username.msg"
-                :suffix="this.host()"
-                hint="You will be able to create more identities once registered"
-                persistent-hint
+              label="Username"
+              required
+              type="text"
+              v-model="newActor.preferred_username"
+              :rules="[rules.required]"
+              :error="this.state.username.status"
+              :error-messages="this.state.username.msg"
+              :suffix="this.host()"
+              hint="You will be able to create more identities once registered"
+              persistent-hint
             >
             </v-text-field>
             <v-textarea
-                name="input-7-1"
-                label="Profile description"
-                hint="Will be displayed publicly on your profile"
+              name="input-7-1"
+              label="Profile description"
+              hint="Will be displayed publicly on your profile"
             ></v-textarea>
           </v-form>
           <v-btn
@@ -57,73 +57,77 @@
             right
             fab
             @click="toggleForm()"
-            >
+          >
             <v-icon>{{ showForm ? 'check' : 'add' }}</v-icon>
           </v-btn>
         </v-card-text>
-    </v-card>
-  </v-flex>
+      </v-card>
+    </v-flex>
   </v-layout>
 </template>
 
-<script>
-export default {
-  name: 'Identities',
-  data() {
-    return {
-      actors: [],
-      newActor: {
-        preferred_username: '',
-        summary: '',
-      },
-      loading: true,
-      showForm: false,
-      rules: {
-        required: value => !!value || 'Required.',
-      },
-      state: {
-        username: {
-          status: false,
-          msg: [],
-        },
+<script lang="ts">
+  import { Component, Vue } from 'vue-property-decorator';
+
+  @Component
+  export default class Identities extends Vue {
+    actors = [];
+    newActor = {
+      preferred_username: '',
+      summary: '',
+    };
+    loading = true;
+    showForm = false;
+    rules = {
+      required: value => !!value || 'Required.',
+    };
+    state = {
+      username: {
+        status: false,
+        msg: [],
       },
     };
-  },
-  created() {
-    this.fetchData();
-  },
-  methods: {
+
+    created() {
+      this.fetchData();
+    }
+
     fetchData() {
-      eventFetch('/user', this.$store)
-        .then(response => response.json())
-        .then((response) => {
-          this.actors = response.data.actors;
-          this.loading = false;
-        });
-    },
+      // Implements eventFetch
+      // eventFetch('/user', this.$store)
+      //   .then(response => response.json())
+      //   .then((response) => {
+      //     this.actors = response.data.actors;
+      //     this.loading = false;
+      //   });
+    }
+
     sendData() {
       this.loading = true;
       this.showForm = false;
-      eventFetch('/actors', this.$store, {
-        method: 'POST',
-        body: JSON.stringify({ actor: this.newActor }),
-      })
-        .then(response => response.json())
-        .then((response) => {
-          this.actors.push(response.data);
-          this.loading = false;
-        });
-    },
+
+      // Implements eventFetch
+      // eventFetch('/actors', this.$store, {
+      //   method: 'POST',
+      //   body: JSON.stringify({ actor: this.newActor }),
+      // })
+      //   .then(response => response.json())
+      //   .then((response) => {
+      //     this.actors.push(response.data);
+      //     this.loading = false;
+      //   });
+    }
+
     toggleForm() {
       if (this.showForm === true) {
         this.sendData();
       } else {
         this.showForm = true;
       }
-    },
+    }
+
     host() {
       return `@${window.location.host}`;
-    },
-  },
-};
+    }
+  }
 </script>
diff --git a/js/src/components/Account/Login.vue b/js/src/components/Account/Login.vue
index c730c3915..dcc74a40d 100644
--- a/js/src/components/Account/Login.vue
+++ b/js/src/components/Account/Login.vue
@@ -58,90 +58,84 @@
   </v-container>
 </template>
 
-<script>
+<script lang="ts">
 
-import Gravatar from 'vue-gravatar';
-import RegisterAvatar from './RegisterAvatar';
-import { AUTH_TOKEN, AUTH_USER_ID, AUTH_USER_ACTOR } from '@/constants';
-import { LOGIN } from '@/graphql/auth';
+  import Gravatar from 'vue-gravatar';
+  import RegisterAvatar from './RegisterAvatar.vue';
+  import { AUTH_TOKEN, AUTH_USER_ACTOR, AUTH_USER_ID } from '@/constants';
+  import { Component, Prop, Vue } from 'vue-property-decorator';
+  import { LOGIN } from '@/graphql/auth';
 
-export default {
-  props: {
-    email: {
-      type: String,
-      required: false,
-      default: '',
+  @Component({
+    components: {
+      'v-gravatar': Gravatar,
+      avatar: RegisterAvatar,
     },
-    password: {
-      type: String,
-      required: false,
-      default: '',
-    },
-  },
-  beforeCreate() {
-    if (this.user) {
-      this.$router.push('/');
-    }
-  },
-  components: {
-    'v-gravatar': Gravatar,
-    avatar: RegisterAvatar,
-  },
-  mounted() {
-    this.credentials.email = this.email;
-    this.credentials.password = this.password;
-  },
-  data() {
-    return {
-      credentials: {
-        email: '',
-        password: '',
-      },
-      validationSent: false,
-      error: {
-        show: false,
-        text: '',
-        timeout: 3000,
-        field: {
-          email: false,
-          password: false,
-        },
+  })
+  export default class Login extends Vue {
+    @Prop({ type: String, required: false, default: '' }) email!: string;
+    @Prop({ type: String, required: false, default: '' }) password!: string;
+
+    credentials = {
+      email: '',
+      password: '',
+    };
+    validationSent = false;
+    error = {
+      show: false,
+      text: '',
+      timeout: 3000,
+      field: {
+        email: false,
+        password: false,
       },
-      rules: {
-        required: value => !!value || 'Required.',
-        email: (value) => {
-          const pattern = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
-          return pattern.test(value) || 'Invalid e-mail.';
-        },
+    };
+    rules = {
+      required: value => !!value || 'Required.',
+      email: (value) => {
+        const pattern = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
+        return pattern.test(value) || 'Invalid e-mail.';
       },
     };
-  },
-  methods: {
+    user: any;
+
+    beforeCreate() {
+      if (this.user) {
+        this.$router.push('/');
+      }
+    }
+
+    mounted() {
+      this.credentials.email = this.email;
+      this.credentials.password = this.password;
+    }
+
     loginAction(e) {
       e.preventDefault();
       this.$apollo.mutate({
         mutation: LOGIN,
         variables: {
           email: this.credentials.email,
-          password: this.credentials.password
-        }
+          password: this.credentials.password,
+        },
       }).then((result) => {
         this.saveUserData(result.data);
-        this.$router.push({name: 'Home'});
+        this.$router.push({ name: 'Home' });
       }).catch((e) => {
         console.log(e);
         this.error.show = true;
         this.error.text = e.message;
       });
-    },
+    }
+
     validEmail() {
       return this.rules.email(this.credentials.email) === true ? 'v-gravatar' : 'avatar';
-    },
-    saveUserData({login: login}) {
+    }
+
+    saveUserData({ login: login }) {
       localStorage.setItem(AUTH_USER_ID, login.user.id);
       localStorage.setItem(AUTH_USER_ACTOR, JSON.stringify(login.actor));
       localStorage.setItem(AUTH_TOKEN, login.token);
     }
-  },
-};
+  }
 </script>
diff --git a/js/src/components/Account/PasswordReset.vue b/js/src/components/Account/PasswordReset.vue
index 939cc4e66..12ffe0039 100644
--- a/js/src/components/Account/PasswordReset.vue
+++ b/js/src/components/Account/PasswordReset.vue
@@ -36,75 +36,74 @@
   </v-container>
 </template>
 
-<script>
-export default {
-  name: 'PasswordReset',
-  props: {
-    token: {
-      type: String,
-      required: true,
-    },
-  },
-  computed: {
-    samePasswords() {
-      return this.rules.password_length(this.credentials.password) === true &&
-          this.credentials.password === this.credentials.password_confirmation;
-    },
-  },
-  data() {
-    return {
-      credentials: {
-        password: '',
-        password_confirmation: '',
-      },
-      error: {
-        show: false,
+<script lang="ts">
+
+  import { Component, Prop, Vue } from 'vue-property-decorator';
+
+  @Component
+  export default class PasswordReset extends Vue {
+    @Prop({ type: String, required: true }) token!: string;
+
+    credentials = {
+      password: '',
+      password_confirmation: '',
+    };
+    error = {
+      show: false,
+    };
+    state = {
+      token: {
+        status: null,
+        msg: '',
       },
-      state: {
-        token: {
-          status: null,
-          msg: '',
-        },
-        password: {
-          status: null,
-          msg: '',
-        },
-        password_confirmation: {
-          status: null,
-          msg: '',
-        },
+      password: {
+        status: null,
+        msg: '',
       },
-      rules: {
-        password_length: value => value.length > 6 || 'Password must be at least 6 caracters long',
-        required: value => !!value || 'Required.',
-        password_equal: value => value === this.credentials.password || 'Passwords must be the same',
+      password_confirmation: {
+        status: null,
+        msg: '',
       },
     };
-  },
-  methods: {
+    rules = {
+      password_length: value => value.length > 6 || 'Password must be at least 6 caracters long',
+      required: value => !!value || 'Required.',
+      password_equal: value => value === this.credentials.password || 'Passwords must be the same',
+    };
+
+    get samePasswords() {
+      return this.rules.password_length(this.credentials.password) === true &&
+        this.credentials.password === this.credentials.password_confirmation;
+    }
+
     resetAction(e) {
       this.resetState();
       e.preventDefault();
       console.log(this.token);
-      fetchStory('/users/password-reset/post', this.$store, { method: 'POST', body: JSON.stringify({ password: this.credentials.password, token: this.token }) }).then((data) => {
-        localStorage.setItem('token', data.token);
-        localStorage.setItem('refresh_token', data.refresh_token);
-        this.$store.commit('LOGIN_USER', data.account);
-        this.$snotify.success(this.$t('registration.success.login', { username: data.account.username }));
-        this.$router.push({ name: 'Home' });
-      }, (error) => {
-        Promise.resolve(error).then((errormsg) => {
-          console.log('errormsg', errormsg);
-          this.error.show = true;
-          Object.entries(JSON.parse(errormsg).errors).forEach(([key, val]) => {
-            console.log('key', key);
-            console.log('val', val[0]);
-            this.state[key] = { status: false, msg: val[0] };
-            console.log('state', this.state);
-          });
-        });
-      });
-    },
+      // FIXME: implements fetchStory
+      // fetchStory('/users/password-reset/post', this.$store, {
+      //   method: 'POST',
+      //   body: JSON.stringify({ password: this.credentials.password, token: this.token }),
+      // }).then((data) => {
+      //   localStorage.setItem('token', data.token);
+      //   localStorage.setItem('refresh_token', data.refresh_token);
+      //   this.$store.commit('LOGIN_USER', data.account);
+      //   this.$snotify.success(this.$t('registration.success.login', { username: data.account.username }));
+      //   this.$router.push({ name: 'Home' });
+      // }, (error) => {
+      //   Promise.resolve(error).then((errormsg) => {
+      //     console.log('errormsg', errormsg);
+      //     this.error.show = true;
+      //     Object.entries(JSON.parse(errormsg).errors).forEach(([ key, val ]) => {
+      //       console.log('key', key);
+      //       console.log('val', val[ 0 ]);
+      //       this.state[ key ] = { status: false, msg: val[ 0 ] };
+      //       console.log('state', this.state);
+      //     });
+      //   });
+      // });
+    }
+
     resetState() {
       this.state = {
         token: {
@@ -120,7 +119,6 @@ export default {
           msg: '',
         },
       };
-    },
-  },
-};
+    }
+  };
 </script>
diff --git a/js/src/components/Account/Register.vue b/js/src/components/Account/Register.vue
index 49bbdd931..d3010c55c 100644
--- a/js/src/components/Account/Register.vue
+++ b/js/src/components/Account/Register.vue
@@ -68,8 +68,12 @@
               <router-link :to="{ name: 'ResendConfirmation', params: { email }}">Didn't receive the instructions ?</router-link>
             </v-form>
             <div v-if="validationSent">
-              <h2><translate>A validation email was sent to %{email}</translate></h2>
-              <v-alert :value="true" type="info"><translate>Before you can login, you need to click on the link inside it to validate your account</translate></v-alert>
+              <h2>
+                <translate>A validation email was sent to %{email}</translate>
+              </h2>
+              <v-alert :value="true" type="info">
+                <translate>Before you can login, you need to click on the link inside it to validate your account</translate>
+              </v-alert>
             </div>
           </v-card-text>
         </v-card>
@@ -78,85 +82,78 @@
   </v-container>
 </template>
 
-<script>
-import Gravatar from 'vue-gravatar';
-import RegisterAvatar from './RegisterAvatar';
-import { CREATE_USER } from '@/graphql/user';
+<script lang="ts">
+  import Gravatar from 'vue-gravatar';
+  import RegisterAvatar from './RegisterAvatar.vue';
+  import { CREATE_USER } from '@/graphql/user';
+  import { Component, Prop, Vue } from 'vue-property-decorator';
 
-export default {
-  props: {
-    default_email: {
-      type: String,
-      required: false,
-      default: '',
+  @Component({
+    components: {
+      'v-gravatar': Gravatar,
+      avatar: RegisterAvatar,
     },
-    default_password: {
-      type: String,
-      required: false,
-      default: '',
-    },
-  },
-  components: {
-    'v-gravatar': Gravatar,
-    avatar: RegisterAvatar,
-  },
-  data() {
-    return {
-      username: '',
-      email: this.default_email,
-      password: this.default_password,
-      error: {
-        show: false,
+  })
+  export default class Register extends Vue {
+    @Prop({ type: String, required: false, default: '' }) default_email!: string;
+    @Prop({ type: String, required: false, default: '' }) default_password!: string;
+
+    username = '';
+    email = this.default_email;
+    password = this.default_password;
+    error = {
+      show: false,
+    };
+    showPassword = false;
+    validationSent = false;
+    state = {
+      email: {
+        status: false,
+        msg: [],
       },
-      showPassword: false,
-      validationSent: false,
-      state: {
-        email: {
-          status: false,
-          msg: [],
-        },
-        username: {
-          status: false,
-          msg: [],
-        },
-        password: {
-          status: false,
-          msg: [],
-        },
+      username: {
+        status: false,
+        msg: [],
       },
-      rules: {
-        password_length: value => value.length > 6 || 'Password must be at least 6 caracters long',
-        required: value => !!value || 'Required.',
-        email: (value) => {
-          const pattern = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
-          return pattern.test(value) || 'Invalid e-mail.';
-        },
+      password: {
+        status: false,
+        msg: [],
       },
     };
-  },
-  methods: {
+    rules = {
+      password_length: value => value.length > 6 || 'Password must be at least 6 caracters long',
+      required: value => !!value || 'Required.',
+      email: (value) => {
+        const pattern = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
+        return pattern.test(value) || 'Invalid e-mail.';
+      },
+    };
+
     resetState() {
       this.state = {
         email: {
           status: false,
-          msg: '',
+          msg: [],
         },
         username: {
           status: false,
-          msg: '',
+          msg: [],
         },
         password: {
           status: false,
-          msg: '',
+          msg: [],
         },
       };
-    },
+    }
+
     host() {
       return `@${window.location.host}`;
-    },
+    }
+
     validEmail() {
       return this.rules.email(this.email) === true ? 'v-gravatar' : 'avatar';
-    },
+    }
+
     submit() {
       this.$apollo.mutate({
         mutation: CREATE_USER,
@@ -171,19 +168,20 @@ export default {
       }).catch((error) => {
         console.error(error);
       });
-    },
-  },
-};
+    }
+  };
 </script>
+
 <style lang="scss">
-.avatar-enter-active {
-  transition: opacity 1s ease;
-}
-.avatar-enter, .avatar-leave-to {
-  opacity: 0;
-}
+  .avatar-enter-active {
+    transition: opacity 1s ease;
+  }
+
+  .avatar-enter, .avatar-leave-to {
+    opacity: 0;
+  }
 
-.avatar-leave {
-  display: none;
-}
+  .avatar-leave {
+    display: none;
+  }
 </style>
diff --git a/js/src/components/Account/RegisterAvatar.vue b/js/src/components/Account/RegisterAvatar.vue
index d54e920a9..db44bb544 100644
--- a/js/src/components/Account/RegisterAvatar.vue
+++ b/js/src/components/Account/RegisterAvatar.vue
@@ -1,9 +1,12 @@
 <template>
-    <img class="img-circle elevation-7 mb-1" src="@/assets/profile.svg">
+  <img class="img-circle elevation-7 mb-1" src="@/assets/profile.svg">
 </template>
-<script>
-export default {
-  name: 'RegisterAvatar',
-};
+
+<script lang="ts">
+  import { Component, Vue } from 'vue-property-decorator';
+
+  @Component
+  export default class RegisterAvatar extends Vue {
+  }
 </script>
 
diff --git a/js/src/components/Account/ResendConfirmation.vue b/js/src/components/Account/ResendConfirmation.vue
index 0dc3d1a9d..119f90182 100644
--- a/js/src/components/Account/ResendConfirmation.vue
+++ b/js/src/components/Account/ResendConfirmation.vue
@@ -30,53 +30,48 @@
   </v-container>
 </template>
 
-<script>
-export default {
-  name: 'ResendConfirmation',
-  props: {
-    email: {
-      type: String,
-      required: false,
-      default: '',
-    },
-  },
-  data() {
-    return {
-      credentials: {
-        email: '',
-      },
-      validationSent: false,
-      error: false,
-      state: {
-        email: {
-          status: null,
-          msg: '',
-        },
+<script lang="ts">
+  import { Component, Prop, Vue } from 'vue-property-decorator';
+
+  @Component
+  export default class ResendConfirmation extends Vue {
+    @Prop({ type: String, required: false, default: '' }) email!: string;
+
+    credentials = {
+      email: '',
+    };
+    validationSent = false;
+    error = false;
+    state = {
+      email: {
+        status: null,
+        msg: '',
       },
-      rules: {
-        required: value => !!value || 'Required.',
-        email: (value) => {
-          const pattern = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
-          return pattern.test(value) || 'Invalid e-mail.';
-        },
+    };
+    rules = {
+      required: value => !!value || 'Required.',
+      email: (value) => {
+        const pattern = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
+        return pattern.test(value) || 'Invalid e-mail.';
       },
     };
-  },
-  mounted() {
-    this.credentials.email = this.email;
-  },
-  methods: {
+
+    mounted() {
+      this.credentials.email = this.email;
+    }
+
     resendConfirmationAction(e) {
       e.preventDefault();
-      fetchStory('/users/resend', this.$store, { method: 'POST', body: JSON.stringify(this.credentials) }).then(() => {
-        this.validationSent = true;
-      }).catch((err) => {
-        Promise.resolve(err).then(() => {
-          this.error = true;
-          this.validationSent = true;
-        });
-      });
-    },
-  },
-};
+
+      // FIXME: implement fetchStory
+      // fetchStory('/users/resend', this.$store, { method: 'POST', body: JSON.stringify(this.credentials) }).then(() => {
+      //   this.validationSent = true;
+      // }).catch((err) => {
+      //   Promise.resolve(err).then(() => {
+      //     this.error = true;
+      //     this.validationSent = true;
+      //   });
+      // });
+    }
+  };
 </script>
diff --git a/js/src/components/Account/SendPasswordReset.vue b/js/src/components/Account/SendPasswordReset.vue
index e57376a73..bedc389b7 100644
--- a/js/src/components/Account/SendPasswordReset.vue
+++ b/js/src/components/Account/SendPasswordReset.vue
@@ -30,54 +30,51 @@
   </v-container>
 </template>
 
-<script>
-export default {
-  name: 'SendPasswordReset',
-  props: {
-    email: {
-      type: String,
-      required: false,
-      default: '',
-    },
-  },
-  mounted() {
-    this.credentials.email = this.email;
-  },
-  data() {
-    return {
-      credentials: {
-        email: '',
-      },
-      validationSent: false,
-      error: false,
-      state: {
-        email: {
-          status: null,
-          msg: '',
-        },
+<script lang="ts">
+  import { Component, Prop, Vue } from 'vue-property-decorator';
+
+  @Component
+  export default class SendPasswordReset extends Vue {
+    @Prop({ type: String, required: false, default: '' }) email!: string;
+
+    credentials = {
+      email: '',
+    };
+    validationSent = false;
+    error = false;
+    state = {
+      email: {
+        status: null,
+        msg: '',
       },
-      rules: {
-        required: value => !!value || 'Required.',
-        email: (value) => {
-          const pattern = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
-          return pattern.test(value) || 'Invalid e-mail.';
-        },
+    };
+
+    rules = {
+      required: value => !!value || 'Required.',
+      email: (value) => {
+        const pattern = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
+        return pattern.test(value) || 'Invalid e-mail.';
       },
     };
-  },
-  methods: {
+
+    mounted() {
+      this.credentials.email = this.email;
+    }
+
     resendConfirmationAction(e) {
       e.preventDefault();
-      fetchStory('/users/password-reset/send', this.$store, { method: 'POST', body: JSON.stringify(this.credentials) }).then(() => {
-        this.error = false;
-        this.validationSent = true;
-      }).catch((err) => {
-        Promise.resolve(err).then((data) => {
-          this.error = true;
-          this.state.email = { status: false, msg: data.errors };
-        });
-      });
-    },
+      // FIXME: implement fetchStory
+      // fetchStory('/users/password-reset/send', this.$store, { method: 'POST', body: JSON.stringify(this.credentials) }).then(() => {
+      //   this.error = false;
+      //   this.validationSent = true;
+      // }).catch((err) => {
+      //   Promise.resolve(err).then((data) => {
+      //     this.error = true;
+      //     this.state.email = { status: false, msg: data.errors };
+      //   });
+      // });
+    }
+
     resetState() {
       this.state = {
         email: {
@@ -85,7 +82,6 @@ export default {
           msg: '',
         },
       };
-    },
-  },
-};
+    }
+  };
 </script>
diff --git a/js/src/components/Account/Validate.vue b/js/src/components/Account/Validate.vue
index b7163500d..32815d7f5 100644
--- a/js/src/components/Account/Validate.vue
+++ b/js/src/components/Account/Validate.vue
@@ -1,36 +1,37 @@
 <template>
   <v-container>
-    <h1 v-if="loading"><translate>Your account is being validated</translate></h1>
+    <h1 v-if="loading">
+      <translate>Your account is being validated</translate>
+    </h1>
     <div v-else>
       <div v-if="failed">
-        <v-alert :value="true" variant="danger"><translate>Error while validating account</translate></v-alert>
+        <v-alert :value="true" variant="danger">
+          <translate>Error while validating account</translate>
+        </v-alert>
       </div>
-      <h1 v-else><translate>Your account has been validated</translate></h1>
+      <h1 v-else>
+        <translate>Your account has been validated</translate>
+      </h1>
     </div>
   </v-container>
 </template>
 
-<script>
-import { VALIDATE_USER } from '@/graphql/user';
+<script lang="ts">
+  import { VALIDATE_USER } from '@/graphql/user';
+  import { Component, Prop, Vue } from 'vue-property-decorator';
+  import { AUTH_TOKEN, AUTH_USER_ACTOR, AUTH_USER_ID } from '@/constants';
+
+  @Component
+  export default class Validate extends Vue {
+    @Prop({ type: String, required: true }) token!: string;
+
+    loading = true;
+    failed = false;
+
+    created() {
+      this.validateAction();
+    }
 
-export default {
-  name: 'Validate',
-  data() {
-    return {
-      loading: true,
-      failed: false,
-    };
-  },
-  props: {
-    token: {
-      type: String,
-      required: true,
-    },
-  },
-  created() {
-    this.validateAction();
-  },
-  methods: {
     validateAction() {
       this.$apollo.mutate({
         mutation: VALIDATE_USER,
@@ -41,18 +42,19 @@ export default {
         this.loading = false;
         console.log(data);
         this.saveUserData(data.data);
-        this.$router.push({name: 'Home'});
+        this.$router.push({ name: 'Home' });
       }).catch((error) => {
         this.loading = false;
         console.log(error);
         this.failed = true;
       });
-    },
-    saveUserData({validateUser: login}) {
+    }
+
+    saveUserData({ validateUser: login }) {
       localStorage.setItem(AUTH_USER_ID, login.user.id);
       localStorage.setItem(AUTH_USER_ACTOR, JSON.stringify(login.actor));
       localStorage.setItem(AUTH_TOKEN, login.token);
     }
-  },
-};
+
+  };
 </script>
diff --git a/js/src/components/Category/Create.vue b/js/src/components/Category/Create.vue
index 05aa1530f..c36ddec53 100644
--- a/js/src/components/Category/Create.vue
+++ b/js/src/components/Category/Create.vue
@@ -4,7 +4,9 @@
       <v-flex xs12 sm8 md4>
         <v-card class="elevation-12">
           <v-toolbar dark color="primary">
-              <v-toolbar-title><translate>Create a new category</translate></v-toolbar-title>
+            <v-toolbar-title>
+              <translate>Create a new category</translate>
+            </v-toolbar-title>
           </v-toolbar>
           <v-card-text>
             <v-form>
@@ -29,7 +31,9 @@
                   @change="onFilePicked"
                 >
               </v-flex>
-              <v-btn color="primary" @click="create"><translate>Create category</translate></v-btn>
+              <v-btn color="primary" @click="create">
+                <translate>Create category</translate>
+              </v-btn>
             </v-form>
           </v-card-text>
         </v-card>
@@ -38,50 +42,48 @@
   </v-container>
 </template>
 
-<script>
-import { UPLOAD_PICTURE } from '@/graphql/upload';
-import { CREATE_CATEGORY } from '@/graphql/category';
+<script lang="ts">
+  import { CREATE_CATEGORY } from '@/graphql/category';
+  import { Component, Vue } from 'vue-property-decorator';
 
-export default {
-  name: 'create-category',
-  data() {
-    return {
-      title: '',
-      description: '',
-      image: {
-        url: '',
-        name: '',
-        file: '',
-      },
+  @Component
+  export default class CreateCategory extends Vue {
+    title = '';
+    description = '';
+    image = {
+      url: '',
+      name: '',
+      file: '',
     };
-  },
-  methods: {
+
     create() {
       this.$apollo.mutate({
         mutation: CREATE_CATEGORY,
         variables: {
           title: this.title,
           description: this.description,
-          picture: this.$refs.image.files[0],
-        }
+          picture: (this.$refs['image'] as any).files[ 0 ],
+        },
       }).then((data) => {
         console.log(data);
       }).catch((error) => {
         console.error(error);
       });
-    },
-    pickFile () {
-      this.$refs.image.click ()
-    },
+    }
+
+    pickFile() {
+      (this.$refs['image'] as any).click();
+    }
+
     onFilePicked(e) {
       const files = e.target.files;
-      if(files[0] === undefined || files[0].name.lastIndexOf('.') <= 0) {
-					console.error("File is incorrect")
+      if (files[ 0 ] === undefined || files[ 0 ].name.lastIndexOf('.') <= 0) {
+        console.error('File is incorrect');
       }
-      this.image.name = files[0].name;
-    },
-  },
-};
+      this.image.name = files[ 0 ].name;
+    }
+
+  };
 </script>
 
 <style>
diff --git a/js/src/components/Category/List.vue b/js/src/components/Category/List.vue
index 2155d1213..072f48082 100644
--- a/js/src/components/Category/List.vue
+++ b/js/src/components/Category/List.vue
@@ -15,8 +15,12 @@
               </div>
             </v-card-title>
             <v-card-actions>
-              <v-btn flat class="orange--text"><translate>Explore</translate></v-btn>
-              <v-btn flat class="red--text" v-on:click="deleteCategory(category.id)"><translate>Delete</translate></v-btn>
+              <v-btn flat class="orange--text">
+                <translate>Explore</translate>
+              </v-btn>
+              <v-btn flat class="red--text" v-on:click="deleteCategory(category.id)">
+                <translate>Delete</translate>
+              </v-btn>
             </v-card-actions>
           </v-card>
         </v-flex>
@@ -30,37 +34,34 @@
   </v-container>
 </template>
 
-<script>
-import { FETCH_CATEGORIES } from '@/graphql/category';
+<script lang="ts">
+  import { FETCH_CATEGORIES } from '@/graphql/category';
+  import { Component, Vue } from 'vue-property-decorator';
 
-// TODO : remove this hardcode
+  // TODO : remove this hardcode
 
-
-export default {
-  name: 'Home',
-  data() {
-    return {
-      categories: [],
-      loading: true,
-      HTTP_ENDPOINT: 'http://localhost:4000',
-    };
-  },
-  apollo: {
-    categories: {
-      query: FETCH_CATEGORIES,
+  @Component({
+    apollo: {
+      categories: {
+        query: FETCH_CATEGORIES,
+      },
     },
-  },
-  methods: {
+  })
+  export default class List extends Vue {
+    categories = [];
+    loading = true;
+    HTTP_ENDPOINT = 'http://localhost:4000';
+
     deleteCategory(categoryId) {
       const router = this.$router;
-      eventFetch(`/categories/${categoryId}`, this.$store, { method: 'DELETE' })
-        .then(() => {
-          this.categories = this.categories.filter(category => category.id !== categoryId);
-          router.push('/category');
-        });
-    },
-  },
-};
+      // FIXME: remove eventFetch
+      // eventFetch(`/categories/${categoryId}`, this.$store, { method: 'DELETE' })
+      //   .then(() => {
+      //     this.categories = this.categories.filter(category => category.id !== categoryId);
+      //     router.push('/category');
+      //   });
+    }
+  };
 </script>
 
 <!-- Add "scoped" attribute to limit CSS to this component only -->
diff --git a/js/src/components/Event/Create.vue b/js/src/components/Event/Create.vue
index fc8d21e69..3796aa61a 100644
--- a/js/src/components/Event/Create.vue
+++ b/js/src/components/Event/Create.vue
@@ -4,7 +4,7 @@
       <v-flex xs12 sm8 md4>
         <v-card class="elevation-12">
           <v-toolbar dark color="primary">
-              <v-toolbar-title>Create a new event</v-toolbar-title>
+            <v-toolbar-title>Create a new event</v-toolbar-title>
           </v-toolbar>
           <v-card-text>
             <v-form>
@@ -49,11 +49,11 @@
                 :required="event.location_type === 'phone'"
               ></v-text-field>
               <v-autocomplete
-              :items="categories"
-              v-model="event.category"
-              item-text="title"
-              item-value="id"
-              label="Categories"
+                :items="categories"
+                v-model="event.category"
+                item-text="title"
+                item-value="id"
+                label="Categories"
               >
               </v-autocomplete>
               <v-btn color="primary" @click="create">Create event</v-btn>
@@ -65,62 +65,58 @@
   </v-container>
 </template>
 
-<script>
-// import Location from '@/components/Location';
-import VueMarkdown from 'vue-markdown';
-import { CREATE_EVENT, EDIT_EVENT } from '@/graphql/event';
-import { FETCH_CATEGORIES } from '@/graphql/category';
-import { AUTH_USER_ACTOR } from '@/constants';
+<script lang="ts">
+  // import Location from '@/components/Location';
+  import VueMarkdown from 'vue-markdown';
+  import { CREATE_EVENT, EDIT_EVENT } from '@/graphql/event';
+  import { FETCH_CATEGORIES } from '@/graphql/category';
+  import { AUTH_USER_ACTOR } from '@/constants';
+  import { Component, Prop, Vue } from 'vue-property-decorator';
 
-export default {
-  name: 'create-event',
-  props: {
-    uuid: {
-      required: false,
-      type: String,
+  @Component({
+    components: {
+      VueMarkdown
     },
-  },
-  components: {
-    /*      Location, */
-    VueMarkdown,
-  },
-  data() {
-    return {
-      e1: 0,
-      event: {
-        title: null,
-        description: '',
-        begins_on: (new Date()).toISOString().substr(0, 10),
-        ends_on: new Date(),
-        seats: null,
-        physical_address: null,
-        location_type: 'physical',
-        online_address: null,
-        tel_num: null,
-        price: null,
-        category: null,
-        category_id: null,
-        tags: [],
-        participants: [],
+    apollo: {
+      categories: {
+        query: FETCH_CATEGORIES,
       },
-      categories: [],
+    }
+  })
+  export default class CreateEvent extends Vue {
+    @Prop({required: false, type: String}) uuid!: string
+
+    e1 = 0
+    event = {
+      title: null,
+      organizer_actor_id: null,
+      description: '',
+      begins_on: (new Date()).toISOString().substr(0, 10),
+      ends_on: new Date(),
+      seats: null,
+      physical_address: null,
+      location_type: 'physical',
+      online_address: null,
+      tel_num: null,
+      price: null,
+      category: null,
+      category_id: null,
       tags: [],
-      tagsToSend: [],
-      tagsFetched: [],
-    };
-  },
-  // created() {
-  //   if (this.uuid) {
-  //     this.fetchEvent();
-  //   }
-  // },
-  apollo: {
-    categories: {
-      query: FETCH_CATEGORIES,
-    },
-  },
-  methods: {
-    create() {
+      participants: [],
+    } as any // FIXME: correctly type an event
+    categories = []
+    tags = []
+    tagsToSend = []
+    tagsFetched = []
+    loading = false
+
+    // created() {
+    //   if (this.uuid) {
+    //     this.fetchEvent();
+    //   }
+    // }
+
+    create () {
       // this.event.seats = parseInt(this.event.seats, 10);
       // this.tagsToSend.forEach((tag) => {
       //   this.event.tags.push({
@@ -128,10 +124,11 @@ export default {
       //     // '@type': 'Tag',
       //   });
       // });
-      const actor = JSON.parse(localStorage.getItem(AUTH_USER_ACTOR));
-      this.event.category_id = this.event.category;
-      this.event.organizer_actor_id = actor.id;
-      this.event.participants = [actor.id];
+      // FIXME: correctly parse actor JSON
+      const actor = JSON.parse(localStorage.getItem(AUTH_USER_ACTOR) || '{}')
+      this.event.category_id = this.event.category
+      this.event.organizer_actor_id = actor.id
+      this.event.participants = [actor.id]
       // this.event.price = parseFloat(this.event.price);
 
       if (this.uuid === undefined) {
@@ -146,33 +143,25 @@ export default {
             addressType: this.event.location_type,
           }
         }).then((data) => {
-          this.loading = false;
-          this.$router.push({ name: 'Event', params: { uuid: data.data.uuid } });
+          this.loading = false
+          this.$router.push({name: 'Event', params: {uuid: data.data.uuid}})
         }).catch((error) => {
-          console.log(error);
-        });
+          console.log(error)
+        })
       } else {
         this.$apollo.mutate({
           mutation: EDIT_EVENT,
         }).then((data) => {
-          this.loading = false;
-          this.$router.push({ name: 'Event', params: { uuid: data.data.uuid } });
+          this.loading = false
+          this.$router.push({name: 'Event', params: {uuid: data.data.uuid}})
         }).catch((error) => {
-          console.log(error);
-        });
+          console.log(error)
+        })
       }
-      this.event.tags = [];
-    },
-    // fetchEvent() {
-    //   eventFetch(`/events/${this.id}`, this.$store)
-    //     .then(response => response.json())
-    //     .then((data) => {
-    //       this.loading = false;
-    //       this.event = data;
-    //       console.log(this.event);
-    //     });
-    // },
-    getAddressData(addressData) {
+      this.event.tags = []
+    }
+
+    getAddressData (addressData) {
       if (addressData !== null) {
         this.event.address = {
           geom: {
@@ -187,11 +176,11 @@ export default {
           addressRegion: addressData.administrative_area_level_1,
           postalCode: addressData.postal_code,
           streetAddress: `${addressData.street_number} ${addressData.route}`,
-        };
+        }
       }
-    },
-  },
-};
+    }
+
+  };
 </script>
 
 <style>
diff --git a/js/src/components/Event/Edit.vue b/js/src/components/Event/Edit.vue
index 179657e68..cb23cc4ed 100644
--- a/js/src/components/Event/Edit.vue
+++ b/js/src/components/Event/Edit.vue
@@ -97,28 +97,29 @@
   </v-container>
 </template>
 
-<script>
-export default {
-  props: ['id'],
-  data() {
-    return {
-      loading: true,
-      event: null,
-    };
-  },
-  created() {
-    this.fetchData();
-  },
-  methods: {
+<script lang="ts">
+  import { Component, Prop, Vue } from 'vue-property-decorator';
+
+  @Component
+  export default class EventEdit extends Vue {
+    @Prop(String) id!: string;
+
+    loading = true;
+    event = null;
+
+    created() {
+      this.fetchData();
+    }
+
     fetchData() {
-      eventFetch(`/events/${this.id}`, this.$store)
-        .then(response => response.json())
-        .then((data) => {
-          this.loading = false;
-          this.event = data;
-          console.log(this.event);
-        });
-    },
-  },
-};
+      // FIXME: remove eventFetch
+      // eventFetch(`/events/${this.id}`, this.$store)
+      //   .then(response => response.json())
+      //   .then((data) => {
+      //     this.loading = false;
+      //     this.event = data;
+      //     console.log(this.event);
+      //   });
+    }
+  };
 </script>
diff --git a/js/src/components/Event/Event.vue b/js/src/components/Event/Event.vue
index a2f556e72..6fa6c9e7b 100644
--- a/js/src/components/Event/Event.vue
+++ b/js/src/components/Event/Event.vue
@@ -1,242 +1,245 @@
 <template>
   <v-layout row>
     <v-flex xs12 sm6 offset-sm3>
-          <v-progress-circular v-if="$apollo.loading" indeterminate color="primary"></v-progress-circular>
-          <div>{{ event }}</div>
-          <v-card v-if="event">
-            <!-- <v-img
-              src="https://picsum.photos/600/400/"
-              height="200px"
-            >
-              <v-container fill-height fluid>
-                <v-layout fill-height>
-                  <v-flex xs12 align-end flexbox>
-                    <v-card-title>
-                      <v-btn icon @click="$router.go(-1)" class="white--text">
-                        <v-icon>chevron_left</v-icon>
-                      </v-btn>
-                      <v-spacer></v-spacer>
-                      <v-btn icon class="mr-3 white--text" v-if="actorIsOrganizer()" :to="{ name: 'EditEvent', params: {uuid: event.uuid}}">
-                        <v-icon>edit</v-icon>
-                      </v-btn>
-                      <v-menu bottom left>
-                        <v-btn icon slot="activator" class="white--text">
-                          <v-icon>more_vert</v-icon>
-                        </v-btn>
-                        <v-list>
-                          <v-list-tile @click="downloadIcsEvent()">
-                            <v-list-tile-title>Download</v-list-tile-title>
-                          </v-list-tile>
-                          <v-list-tile @click="deleteEvent()" v-if="actorIsOrganizer()">
-                            <v-list-tile-title>Delete</v-list-tile-title>
-                          </v-list-tile>
-                        </v-list>
-                      </v-menu>
-                    </v-card-title>
-                  </v-flex>
-                </v-layout>
-              </v-container>
-            </v-img> -->
-            <v-container grid-list-md>
-              <v-layout row wrap>
-                <v-flex md10>
+      <v-progress-circular v-if="$apollo.loading" indeterminate color="primary"></v-progress-circular>
+      <div>{{ event }}</div>
+      <v-card v-if="event">
+        <!-- <v-img
+          src="https://picsum.photos/600/400/"
+          height="200px"
+        >
+          <v-container fill-height fluid>
+            <v-layout fill-height>
+              <v-flex xs12 align-end flexbox>
+                <v-card-title>
+                  <v-btn icon @click="$router.go(-1)" class="white--text">
+                    <v-icon>chevron_left</v-icon>
+                  </v-btn>
                   <v-spacer></v-spacer>
-                  <span class="subheading grey--text">{{ event.begins_on | formatDay }}</span>
-                  <h1 class="display-1">{{ event.title }}</h1>
-                  <div>
-                    <!-- <router-link :to="{name: 'Account', params: { name: event.organizerActor.preferredUsername } }">
-                        <v-avatar size="25px">
-                            <img class="img-circle elevation-7 mb-1"
-                                  :src="event.organizer_actor.avatarUrl"
-                            >
-                        </v-avatar>
-                    </router-link> -->
-                    <!-- <span v-if="event.organizerActor">Organisé par {{ event.organizerActor.name ? event.organizerActor.name : event.organizerActor.preferredUsername }}</span> -->
-                  </div>
-                <!-- <p><router-link :to="{ name: 'Account', params: {id: event.organizer.id} }"><span class="grey&#45;&#45;text">{{ event.organizer.username }}</span></router-link> organises {{ event.title }} <span v-if="event.address.addressLocality">in {{ event.address.addressLocality }}</span> on the {{ event.startDate | formatDate }}.</p> -->
-                <v-card-text v-if="event.description"><vue-markdown :source="event.description"></vue-markdown></v-card-text>
-                </v-flex>
-                <!-- <v-flex md2>
-                  <p v-if="actorIsOrganizer()">
-                    Vous êtes organisateur de cet événement.
-                  </p>
-                  <div v-else>
-                    <p v-if="actorIsParticipant()">
-                      Vous avez annoncé aller à cet événement.
-                    </p>
-                    <p v-else>Vous y allez ?
-                      <span class="text--darken-2 grey--text">{{ event.participants.length }} personnes y vont.</span>
-                    </p>
-                  </div>
-                  <v-card-actions v-if="!actorIsOrganizer()">
-                    <v-btn v-if="!actorIsParticipant()" @click="joinEvent" color="success"><v-icon>check</v-icon> Join</v-btn>
-                    <v-btn v-if="actorIsParticipant()" @click="leaveEvent" color="error">Leave</v-btn>
-                  </v-card-actions>
-                </v-flex> -->
-              </v-layout>
-            </v-container>
-            <v-divider></v-divider>
-            <v-container>
-              <v-layout row wrap>
-                <v-flex xs12 md4 order-md1>
-                  <v-layout
-                    column
-                    fill-height
-                  >
-                    <v-list two-line>
-                      <v-list-tile>
-                        <v-list-tile-action>
-                          <v-icon color="indigo">access_time</v-icon>
-                        </v-list-tile-action>
-
-                        <v-list-tile-content>
-                          <v-list-tile-title>{{ event.begins_on | formatDate }}</v-list-tile-title>
-                          <v-list-tile-sub-title>{{ event.ends_on | formatDate }}</v-list-tile-sub-title>
-                        </v-list-tile-content>
+                  <v-btn icon class="mr-3 white--text" v-if="actorIsOrganizer()" :to="{ name: 'EditEvent', params: {uuid: event.uuid}}">
+                    <v-icon>edit</v-icon>
+                  </v-btn>
+                  <v-menu bottom left>
+                    <v-btn icon slot="activator" class="white--text">
+                      <v-icon>more_vert</v-icon>
+                    </v-btn>
+                    <v-list>
+                      <v-list-tile @click="downloadIcsEvent()">
+                        <v-list-tile-title>Download</v-list-tile-title>
                       </v-list-tile>
+                      <v-list-tile @click="deleteEvent()" v-if="actorIsOrganizer()">
+                        <v-list-tile-title>Delete</v-list-tile-title>
+                      </v-list-tile>
+                    </v-list>
+                  </v-menu>
+                </v-card-title>
+              </v-flex>
+            </v-layout>
+          </v-container>
+        </v-img> -->
+        <v-container grid-list-md>
+          <v-layout row wrap>
+            <v-flex md10>
+              <v-spacer></v-spacer>
+              <span class="subheading grey--text">{{ event.begins_on | formatDay }}</span>
+              <h1 class="display-1">{{ event.title }}</h1>
+              <div>
+                <!-- <router-link :to="{name: 'Account', params: { name: event.organizerActor.preferredUsername } }">
+                    <v-avatar size="25px">
+                        <img class="img-circle elevation-7 mb-1"
+                              :src="event.organizer_actor.avatarUrl"
+                        >
+                    </v-avatar>
+                </router-link> -->
+                <!-- <span v-if="event.organizerActor">Organisé par {{ event.organizerActor.name ? event.organizerActor.name : event.organizerActor.preferredUsername }}</span> -->
+              </div>
+              <!-- <p><router-link :to="{ name: 'Account', params: {id: event.organizer.id} }"><span class="grey&#45;&#45;text">{{ event.organizer.username }}</span></router-link> organises {{ event.title }} <span v-if="event.address.addressLocality">in {{ event.address.addressLocality }}</span> on the {{ event.startDate | formatDate }}.</p> -->
+              <v-card-text v-if="event.description">
+                <vue-markdown :source="event.description"></vue-markdown>
+              </v-card-text>
+            </v-flex>
+            <!-- <v-flex md2>
+              <p v-if="actorIsOrganizer()">
+                Vous êtes organisateur de cet événement.
+              </p>
+              <div v-else>
+                <p v-if="actorIsParticipant()">
+                  Vous avez annoncé aller à cet événement.
+                </p>
+                <p v-else>Vous y allez ?
+                  <span class="text--darken-2 grey--text">{{ event.participants.length }} personnes y vont.</span>
+                </p>
+              </div>
+              <v-card-actions v-if="!actorIsOrganizer()">
+                <v-btn v-if="!actorIsParticipant()" @click="joinEvent" color="success"><v-icon>check</v-icon> Join</v-btn>
+                <v-btn v-if="actorIsParticipant()" @click="leaveEvent" color="error">Leave</v-btn>
+              </v-card-actions>
+            </v-flex> -->
+          </v-layout>
+        </v-container>
+        <v-divider></v-divider>
+        <v-container>
+          <v-layout row wrap>
+            <v-flex xs12 md4 order-md1>
+              <v-layout
+                column
+                fill-height
+              >
+                <v-list two-line>
+                  <v-list-tile>
+                    <v-list-tile-action>
+                      <v-icon color="indigo">access_time</v-icon>
+                    </v-list-tile-action>
 
-                      <v-divider inset></v-divider>
+                    <v-list-tile-content>
+                      <v-list-tile-title>{{ event.begins_on | formatDate }}</v-list-tile-title>
+                      <v-list-tile-sub-title>{{ event.ends_on | formatDate }}</v-list-tile-sub-title>
+                    </v-list-tile-content>
+                  </v-list-tile>
 
-                      <v-list-tile>
-                        <v-list-tile-action>
-                          <v-icon color="indigo">place</v-icon>
-                        </v-list-tile-action>
+                  <v-divider inset></v-divider>
 
-                        <v-list-tile-content>
-                          <v-list-tile-title><span v-if="event.address_type === 'physical'">
+                  <v-list-tile>
+                    <v-list-tile-action>
+                      <v-icon color="indigo">place</v-icon>
+                    </v-list-tile-action>
+
+                    <v-list-tile-content>
+                      <v-list-tile-title><span v-if="event.address_type === 'physical'">
                       {{ event.physical_address.streetAddress }}
                     </span></v-list-tile-title>
-                          <v-list-tile-sub-title>Mobile</v-list-tile-sub-title>
-                        </v-list-tile-content>
-                      </v-list-tile>
-                    </v-list>
-                  </v-layout>
-                </v-flex>
-                <v-flex md8 xs12>
-                  <p>
-                    <h2>Details</h2>
-                    <vue-markdown :source="event.description" v-if="event.description" :toc-first-level="3"></vue-markdown>
-                  </p>
-                  <v-subheader>Participants</v-subheader>
-                  <!-- <v-flex md2 v-for="participant in event.participants" :key="participant.actor.uuid">
-                    <router-link :to="{name: 'Account', params: { name: participant.actor.preferredUsername }}">
-                      <v-card>
-                        <v-avatar size="75px">
-                          <img v-if="!participant.actor.avatarUrl"
-                              class="img-circle elevation-7 mb-1"
-                              src="https://picsum.photos/125/125/"
-                          >
-                          <img v-else
-                              class="img-circle elevation-7 mb-1"
-                              :src="participant.actor.avatarUrl"
-                          >
-                        </v-avatar>
-                        <v-card-title>
-                          <span>{{ participant.actor.preferredUsername }}</span>
-                        </v-card-title>
-                      </v-card>
-                    </router-link>
-                  </v-flex> -->
-                </v-flex>
-                <span v-if="event.participants.length === 0">No participants yet.</span>
+                      <v-list-tile-sub-title>Mobile</v-list-tile-sub-title>
+                    </v-list-tile-content>
+                  </v-list-tile>
+                </v-list>
               </v-layout>
-            </v-container>
-          </v-card>
+            </v-flex>
+            <v-flex md8 xs12>
+              <p>
+              <h2>Details</h2>
+              <vue-markdown :source="event.description" v-if="event.description" :toc-first-level="3"></vue-markdown>
+              </p>
+              <v-subheader>Participants</v-subheader>
+              <!-- <v-flex md2 v-for="participant in event.participants" :key="participant.actor.uuid">
+                <router-link :to="{name: 'Account', params: { name: participant.actor.preferredUsername }}">
+                  <v-card>
+                    <v-avatar size="75px">
+                      <img v-if="!participant.actor.avatarUrl"
+                          class="img-circle elevation-7 mb-1"
+                          src="https://picsum.photos/125/125/"
+                      >
+                      <img v-else
+                          class="img-circle elevation-7 mb-1"
+                          :src="participant.actor.avatarUrl"
+                      >
+                    </v-avatar>
+                    <v-card-title>
+                      <span>{{ participant.actor.preferredUsername }}</span>
+                    </v-card-title>
+                  </v-card>
+                </router-link>
+              </v-flex> -->
+            </v-flex>
+            <span v-if="event.participants.length === 0">No participants yet.</span>
+          </v-layout>
+        </v-container>
+      </v-card>
     </v-flex>
   </v-layout>
 </template>
 
-<script>
-import VueMarkdown from 'vue-markdown';
-import { FETCH_EVENT } from '@/graphql/event';
-import { LOGGED_ACTOR } from '@/graphql/actor';
+<script lang="ts">
+  import { FETCH_EVENT } from '@/graphql/event';
+  import { Component, Prop, Vue } from 'vue-property-decorator';
+  import VueMarkdown from 'vue-markdown';
 
-export default {
-  name: 'Home',
-  components: {
-    VueMarkdown,
-  },
-  data() {
-    return {
+  @Component({
+    components: {
+      VueMarkdown,
+    },
+    apollo: {
       event: {
-        name: '',
-        slug: '',
-        title: '',
-        uuid: this.uuid,
-        description: '',
-        organizer: {
-          id: null,
-          username: null,
+        query: FETCH_EVENT,
+        variables() {
+          return {
+            uuid: this.uuid,
+          };
         },
-        participants: [],
-      },
-    };
-  },
-  apollo: {
-    event: {
-      query: FETCH_EVENT,
-      variables() {
-        return {
-          uuid: this.uuid,
-        };
       },
+      // loggedActor: {
+      //   query: LOGGED_ACTOR,
+      // }
     },
-    // loggedActor: {
-    //   query: LOGGED_ACTOR,
-    // }
-  },
-  methods: {
+  })
+  export default class Event extends Vue {
+    @Prop({ type: String, required: true }) uuid!: string;
+
+    event = {
+      name: '',
+      slug: '',
+      title: '',
+      uuid: this.uuid,
+      description: '',
+      organizer: {
+        id: null,
+        username: null,
+      },
+      participants: [],
+    };
+
     deleteEvent() {
       const router = this.$router;
-      eventFetch(`/events/${this.uuid}`, this.$store, { method: 'DELETE' })
-        .then(() => router.push({ name: 'EventList' }));
-    },
+      // FIXME: remove eventFetch
+      // eventFetch(`/events/${this.uuid}`, this.$store, { method: 'DELETE' })
+      //   .then(() => router.push({ name: 'EventList' }));
+    }
+
     joinEvent() {
-      eventFetch(`/events/${this.uuid}/join`, this.$store, { method: 'POST' })
-        .then(response => response.json())
-        .then((data) => {
-          console.log(data);
-        });
-    },
+      // FIXME: remove eventFetch
+      // eventFetch(`/events/${this.uuid}/join`, this.$store, { method: 'POST' })
+      //   .then(response => response.json())
+      //   .then((data) => {
+      //     console.log(data);
+      //   });
+    }
+
     leaveEvent() {
-      eventFetch(`/events/${this.uuid}/leave`, this.$store)
-        .then(response => response.json())
-        .then((data) => {
-          console.log(data);
-        });
-    },
+      // FIXME: remove eventFetch
+      // eventFetch(`/events/${this.uuid}/leave`, this.$store)
+      //   .then(response => response.json())
+      //   .then((data) => {
+      //     console.log(data);
+      //   });
+    }
+
     downloadIcsEvent() {
-      eventFetch(`/events/${this.uuid}/ics`, this.$store, { responseType: 'arraybuffer' })
-        .then(response => response.text())
-        .then((response) => {
-          const blob = new Blob([response], { type: 'text/calendar' });
-          const link = document.createElement('a');
-          link.href = window.URL.createObjectURL(blob);
-          link.download = `${this.event.title}.ics`;
-          document.body.appendChild(link);
-          link.click();
-          document.body.removeChild(link);
-        });
-    },
+      // FIXME: remove eventFetch
+      // eventFetch(`/events/${this.uuid}/ics`, this.$store, { responseType: 'arraybuffer' })
+      //   .then(response => response.text())
+      //   .then((response) => {
+      //     const blob = new Blob([ response ], { type: 'text/calendar' });
+      //     const link = document.createElement('a');
+      //     link.href = window.URL.createObjectURL(blob);
+      //     link.download = `${this.event.title}.ics`;
+      //     document.body.appendChild(link);
+      //     link.click();
+      //     document.body.removeChild(link);
+      //   });
+    }
+
     // actorIsParticipant() {
     //   return this.loggedActor && this.event.participants.map(participant => participant.actor.preferredUsername).includes(this.loggedActor.preferredUsername) || this.actorIsOrganizer();
-    // },
+    // }
+    //
     // actorIsOrganizer() {
     //   return this.loggedActor && this.loggedActor.preferredUsername === this.event.organizer.preferredUsername;
-    // },
-  },
-  props: {
-    uuid: {
-      type: String,
-      required: true,
-    },
-  },
-};
+    // }
+  }
 </script>
 
 <!-- Add "scoped" attribute to limit CSS to this component only -->
 <style>
-.v-card__media__background {
-  filter: contrast(0.4);
-}
+  .v-card__media__background {
+    filter: contrast(0.4);
+  }
 </style>
diff --git a/js/src/components/Event/EventList.vue b/js/src/components/Event/EventList.vue
index fe95558d5..8e3ba0408 100644
--- a/js/src/components/Event/EventList.vue
+++ b/js/src/components/Event/EventList.vue
@@ -1,98 +1,102 @@
 <template>
   <v-layout>
-      <v-flex xs12 sm8 offset-sm2>
-          <v-card>
-    <h1>{{ $t("event.list.title") }}</h1>
+    <v-flex xs12 sm8 offset-sm2>
+      <v-card>
+        <h1>{{ $t('event.list.title') }}</h1>
 
-    <v-progress-circular v-if="loading" indeterminate color="primary"></v-progress-circular>
-    <v-chip close v-model="locationChip" label color="pink" text-color="white" v-if="$router.currentRoute.params.location">
-      <v-icon left>location_city</v-icon>{{ locationText }}
-    </v-chip>
-              <v-container grid-list-sm fluid>
-    <v-layout row wrap>
-      <v-flex xs4 v-for="event in events" :key="event.id">
-        <v-card>
-          <v-card-media v-if="!event.image"
-            class="white--text"
-            height="200px"
-            src="https://picsum.photos/g/400/200/"
-          >
-            <v-container fill-height fluid>
-              <v-layout fill-height>
-                <v-flex xs12 align-end flexbox>
-                  <span class="headline black--text">{{ event.title }}</span>
-                </v-flex>
-              </v-layout>
-            </v-container>
-          </v-card-media>
-            <v-card-title primary-title>
-                <div>
+        <v-progress-circular v-if="loading" indeterminate color="primary"></v-progress-circular>
+        <v-chip close v-model="locationChip" label color="pink" text-color="white" v-if="$router.currentRoute.params.location">
+          <v-icon left>location_city</v-icon>
+          {{ locationText }}
+        </v-chip>
+        <v-container grid-list-sm fluid>
+          <v-layout row wrap>
+            <v-flex xs4 v-for="event in events" :key="event.id">
+              <v-card>
+                <v-card-media v-if="!event.image"
+                              class="white--text"
+                              height="200px"
+                              src="https://picsum.photos/g/400/200/"
+                >
+                  <v-container fill-height fluid>
+                    <v-layout fill-height>
+                      <v-flex xs12 align-end flexbox>
+                        <span class="headline black--text">{{ event.title }}</span>
+                      </v-flex>
+                    </v-layout>
+                  </v-container>
+                </v-card-media>
+                <v-card-title primary-title>
+                  <div>
                     <span class="grey--text">{{ event.begins_on | formatDate }}</span><br>
                     <router-link :to="{name: 'Account', params: { name: event.organizer.username } }">
-                        <v-avatar size="25px">
-                            <img class="img-circle elevation-7 mb-1"
-                                 :src="event.organizer.avatar"
-                            >
-                        </v-avatar>
+                      <v-avatar size="25px">
+                        <img class="img-circle elevation-7 mb-1"
+                             :src="event.organizer.avatar"
+                        >
+                      </v-avatar>
                     </router-link>
-                    <span v-if="event.organizer">Organisé par <router-link :to="{name: 'Account', params: {'name': event.organizer.username}}">{{ event.organizer.username }}</router-link></span>
-                </div>
-            </v-card-title>
-          <v-card-actions>
-            <v-btn flat color="orange" @click="downloadIcsEvent(event)">Share</v-btn>
-            <v-btn flat color="orange" @click="viewEvent(event)">Explore</v-btn>
-            <v-btn flat color="red" @click="deleteEvent(event)">Delete</v-btn>
-          </v-card-actions>
-        </v-card>
-      </v-flex>
-    </v-layout>
-              </v-container>
-    <router-link :to="{ name: 'CreateEvent' }" class="btn btn-default">Create</router-link>
-          </v-card>
-      </v-flex>
+                    <span v-if="event.organizer">Organisé par <router-link
+                      :to="{name: 'Account', params: {'name': event.organizer.username}}">{{ event.organizer.username }}</router-link></span>
+                  </div>
+                </v-card-title>
+                <v-card-actions>
+                  <v-btn flat color="orange" @click="downloadIcsEvent(event)">Share</v-btn>
+                  <v-btn flat color="orange" @click="viewEvent(event)">Explore</v-btn>
+                  <v-btn flat color="red" @click="deleteEvent(event)">Delete</v-btn>
+                </v-card-actions>
+              </v-card>
+            </v-flex>
+          </v-layout>
+        </v-container>
+        <router-link :to="{ name: 'CreateEvent' }" class="btn btn-default">Create</router-link>
+      </v-card>
+    </v-flex>
   </v-layout>
 </template>
 
-<script>
-import ngeohash from 'ngeohash';
-import VueMarkdown from 'vue-markdown';
-import VCardTitle from 'vuetify/es5/components/VCard/VCardTitle';
+<script lang="ts">
+  import ngeohash from 'ngeohash';
+  import VueMarkdown from 'vue-markdown';
+  import VCardTitle from 'vuetify/es5/components/VCard/VCardTitle';
+  import { Component, Prop, Vue, Watch } from 'vue-property-decorator';
 
-export default {
-  name: 'EventList',
-  components: {
-    VCardTitle,
-    VueMarkdown,
-  },
-  data() {
-    return {
-      events: [],
-      loading: true,
-      locationChip: false,
-      locationText: '',
-    };
-  },
-  props: ['location'],
-  created() {
-    this.fetchData(this.$router.currentRoute.params.location);
-  },
-  watch: {
-    locationChip(val) {
+  @Component({
+    components: {
+      VCardTitle: VCardTitle as any,
+      VueMarkdown,
+    },
+  })
+  export default class EventList extends Vue {
+    @Prop(String) location!: string;
+
+    events = [];
+    loading = true;
+    locationChip = false;
+    locationText = '';
+
+    created() {
+      this.fetchData(this.$router.currentRoute.params[ 'location' ]);
+    }
+
+    beforeRouteUpdate(to, from, next) {
+      this.fetchData(to.params.location);
+      next();
+    }
+
+    @Watch('locationChip')
+    onLocationChipChange(val) {
       if (val === false) {
         this.$router.push({ name: 'EventList' });
       }
-    },
-  },
-  beforeRouteUpdate(to, from, next) {
-    this.fetchData(to.params.location);
-    next();
-  },
-  methods: {
+    }
+
     geocode(lat, lon) {
       console.log({ lat, lon });
       console.log(ngeohash.encode(lat, lon, 10));
       return ngeohash.encode(lat, lon, 10);
-    },
+    }
+
     fetchData(location) {
       let queryString = '/events';
       if (location) {
@@ -101,37 +105,43 @@ export default {
         this.locationText = `${latitude.toString()} : ${longitude.toString()}`;
       }
       this.locationChip = true;
-      eventFetch(queryString, this.$store)
-        .then(response => response.json())
-        .then((response) => {
-          this.loading = false;
-          this.events = response.data;
-          console.log(this.events);
-        });
-    },
+      // FIXME: remove eventFetch
+      // eventFetch(queryString, this.$store)
+      //   .then(response => response.json())
+      //   .then((response) => {
+      //     this.loading = false;
+      //     this.events = response.data;
+      //     console.log(this.events);
+      //   });
+    }
+
     deleteEvent(event) {
       const router = this.$router;
-      eventFetch(`/events/${event.uuid}`, this.$store, { method: 'DELETE' })
-        .then(() => router.push('/events'));
-    },
+      // FIXME: remove eventFetch
+      // eventFetch(`/events/${event.uuid}`, this.$store, { method: 'DELETE' })
+      //   .then(() => router.push('/events'));
+    }
+
     viewEvent(event) {
       this.$router.push({ name: 'Event', params: { uuid: event.uuid } });
-    },
+    }
+
     downloadIcsEvent(event) {
-      eventFetch(`/events/${event.uuid}/ics`, this.$store, { responseType: 'arraybuffer' })
-        .then(response => response.text())
-        .then((response) => {
-          const blob = new Blob([response], { type: 'text/calendar' });
-          const link = document.createElement('a');
-          link.href = window.URL.createObjectURL(blob);
-          link.download = `${event.title}.ics`;
-          document.body.appendChild(link);
-          link.click();
-          document.body.removeChild(link);
-        });
-    },
-  },
-};
+      // FIXME: remove eventFetch
+      // eventFetch(`/events/${event.uuid}/ics`, this.$store, { responseType: 'arraybuffer' })
+      //   .then(response => response.text())
+      //   .then((response) => {
+      //     const blob = new Blob([ response ], { type: 'text/calendar' });
+      //     const link = document.createElement('a');
+      //     link.href = window.URL.createObjectURL(blob);
+      //     link.download = `${event.title}.ics`;
+      //     document.body.appendChild(link);
+      //     link.click();
+      //     document.body.removeChild(link);
+      //   });
+    }
+
+  };
 </script>
 
 <!-- Add "scoped" attribute to limit CSS to this component only -->
diff --git a/js/src/components/Group/Create.vue b/js/src/components/Group/Create.vue
index c4c2bc0bd..fb4dfa790 100644
--- a/js/src/components/Group/Create.vue
+++ b/js/src/components/Group/Create.vue
@@ -13,10 +13,10 @@
         </v-flex>
         <v-flex xs12>
           <v-text-field
-                  label="Title"
-                  v-model="group.name"
-                  :counter="100"
-                  required
+            label="Title"
+            v-model="group.name"
+            :counter="100"
+            required
           ></v-text-field>
         </v-flex>
         <v-flex md6>
@@ -36,27 +36,27 @@
           ></vue-markdown>
         </v-flex>
         <!--<v-flex md12>-->
-          <!--<vuetify-google-autocomplete-->
-            <!--id="map"-->
-            <!--append-icon="search"-->
-            <!--classname="form-control"-->
-            <!--placeholder="Start typing"-->
-            <!--enable-geolocation-->
-            <!--v-on:placechanged="getAddressData"-->
-          <!--&gt;-->
-          <!--</vuetify-google-autocomplete>-->
+        <!--<vuetify-google-autocomplete-->
+        <!--id="map"-->
+        <!--append-icon="search"-->
+        <!--classname="form-control"-->
+        <!--placeholder="Start typing"-->
+        <!--enable-geolocation-->
+        <!--v-on:placechanged="getAddressData"-->
+        <!--&gt;-->
+        <!--</vuetify-google-autocomplete>-->
         <!--</v-flex>-->
         <!--<v-flex md12>-->
-          <!--<v-select-->
-            <!--v-bind:items="categories"-->
-            <!--v-model="group.category"-->
-            <!--item-text="title"-->
-            <!--item-value="@id"-->
-            <!--label="Categories"-->
-            <!--single-line-->
-            <!--bottom-->
-            <!--types="(cities)"-->
-          <!--&gt;</v-select>-->
+        <!--<v-select-->
+        <!--v-bind:items="categories"-->
+        <!--v-model="group.category"-->
+        <!--item-text="title"-->
+        <!--item-value="@id"-->
+        <!--label="Categories"-->
+        <!--single-line-->
+        <!--bottom-->
+        <!--types="(cities)"-->
+        <!--&gt;</v-select>-->
         <!--</v-flex>-->
       </v-layout>
     </v-form>
@@ -64,51 +64,54 @@
   </v-container>
 </template>
 
-<script>
-import VueMarkdown from 'vue-markdown';
-import VuetifyGoogleAutocomplete from 'vuetify-google-autocomplete';
-
-export default {
-  name: 'create-group',
+<script lang="ts">
+  import VueMarkdown from 'vue-markdown';
+  import VuetifyGoogleAutocomplete from 'vuetify-google-autocomplete';
+  import { Component, Vue } from 'vue-property-decorator';
 
-  components: {
-    VueMarkdown,
-    VuetifyGoogleAutocomplete,
-  },
-  data() {
-    return {
-      e1: 0,
-      group: {
-        preferred_username: '',
-        name: '',
-        summary: '',
-        // category: null,
-      },
-      categories: [],
+  @Component({
+    components: {
+      VueMarkdown,
+      VuetifyGoogleAutocomplete,
+    },
+  })
+  export default class CreateGroup extends Vue {
+    e1 = 0;
+    // FIXME: correctly type group
+    group: { preferred_username: string, name: string, summary: string, address?: any } = {
+      preferred_username: '',
+      name: '',
+      summary: '',
+      // category: null,
     };
-  },
-  mounted() {
-    this.fetchCategories();
-  },
-  methods: {
+    categories = [];
+
+    mounted() {
+      this.fetchCategories();
+    }
+
     create() {
       // this.group.organizer = "/accounts/" + this.$store.state.user.id;
 
-      eventFetch('/groups', this.$store, { method: 'POST', body: JSON.stringify({ group: this.group }) })
-        .then(response => response.json())
-        .then((data) => {
-          this.loading = false;
-          this.$router.push({ path: 'Group', params: { id: data.id } });
-        });
-    },
+      // FIXME: remove eventFetch
+      // eventFetch('/groups', this.$store, { method: 'POST', body: JSON.stringify({ group: this.group }) })
+      //   .then(response => response.json())
+      //   .then((data) => {
+      //     this.loading = false;
+      //     this.$router.push({ path: 'Group', params: { id: data.id } });
+      //   });
+    }
+
     fetchCategories() {
-      eventFetch('/categories', this.$store)
-        .then(response => response.json())
-        .then((data) => {
-          this.loading = false;
-          this.categories = data.data;
-        });
-    },
+      // FIXME: remove eventFetch
+      // eventFetch('/categories', this.$store)
+      //   .then(response => response.json())
+      //   .then((data) => {
+      //     this.loading = false;
+      //     this.categories = data.data;
+      //   });
+    }
+
     getAddressData(addressData) {
       this.group.address = {
         geo: {
@@ -121,9 +124,9 @@ export default {
         postalCode: addressData.postal_code,
         streetAddress: `${addressData.street_number} ${addressData.route}`,
       };
-    },
-  },
-};
+    }
+
+  };
 </script>
 
 <style>
diff --git a/js/src/components/Group/Group.vue b/js/src/components/Group/Group.vue
index b2edec3b4..aec8085cb 100644
--- a/js/src/components/Group/Group.vue
+++ b/js/src/components/Group/Group.vue
@@ -12,7 +12,7 @@
                 </v-btn>
                 <v-spacer></v-spacer>
                 <!--<v-btn icon class="mr-3" v-if="$store.state.user && $store.state.actor.id === actor.id">-->
-                  <!--<v-icon>edit</v-icon>-->
+                <!--<v-icon>edit</v-icon>-->
                 <!--</v-btn>-->
                 <v-btn icon>
                   <v-icon>more_vert</v-icon>
@@ -42,12 +42,12 @@
                                 @{{ group.domain }}
                             </span>
                         </span>
-                        <v-chip color="indigo" text-color="white">
-                            <v-avatar>
-                                <v-icon>group</v-icon>
-                            </v-avatar>
-                            Group
-                        </v-chip>
+                      <v-chip color="indigo" text-color="white">
+                        <v-avatar>
+                          <v-icon>group</v-icon>
+                        </v-avatar>
+                        Group
+                      </v-chip>
                     </div>
                     <v-card-text v-if="group.description" v-html="group.description"></v-card-text>
                   </v-flex>
@@ -119,9 +119,9 @@
               <v-flex v-for="event in group.participatingEvents" :key="event.id">
                 <v-card>
                   <v-card-media
-                          class="black--text"
-                          height="200px"
-                          src="https://picsum.photos/400/200/"
+                    class="black--text"
+                    height="200px"
+                    src="https://picsum.photos/400/200/"
                   >
                     <v-container fill-height fluid>
                       <v-layout fill-height>
@@ -135,7 +135,10 @@
                     <div>
                       <span class="grey--text">{{ event.startDate | formatDate }} à {{ event.location }}</span><br>
                       <p>{{ event.description }}</p>
-                      <p v-if="event.organizer">Organisé par <router-link :to="{name: 'Account', params: {'id': event.organizer.id}}">{{ event.organizer.username }}</router-link></p>
+                      <p v-if="event.organizer">Organisé par
+                        <router-link :to="{name: 'Account', params: {'id': event.organizer.id}}">{{ event.organizer.username }}
+                        </router-link>
+                      </p>
                     </div>
                   </v-card-title>
                   <v-card-actions>
@@ -160,9 +163,9 @@
               <v-flex v-for="event in group.organizingEvents" :key="event.id">
                 <v-card>
                   <v-card-media
-                          class="black--text"
-                          height="200px"
-                          src="https://picsum.photos/400/200/"
+                    class="black--text"
+                    height="200px"
+                    src="https://picsum.photos/400/200/"
                   >
                     <v-container fill-height fluid>
                       <v-layout fill-height>
@@ -176,7 +179,10 @@
                     <div>
                       <span class="grey--text">{{ event.startDate | formatDate }} à {{ event.location }}</span><br>
                       <p>{{ event.description }}</p>
-                      <p v-if="event.organizer">Organisé par <router-link :to="{name: 'Account', params: {'id': event.organizer.id}}">{{ event.organizer.username }}</router-link></p>
+                      <p v-if="event.organizer">Organisé par
+                        <router-link :to="{name: 'Account', params: {'id': event.organizer.id}}">{{ event.organizer.username }}
+                        </router-link>
+                      </p>
                     </div>
                   </v-card-title>
                   <v-card-actions>
@@ -201,38 +207,35 @@
   </v-container>
 </template>
 
-<script>
-export default {
-  name: 'Group',
-  data() {
-    return {
-      group: null,
-      loading: true,
-    };
-  },
-  props: {
-    name: {
-      type: String,
-      required: true,
-    },
-  },
-  created() {
-    this.fetchData();
-  },
-  watch: {
-    // call again the method if the route changes
-    $route: 'fetchData',
-  },
-  methods: {
+<script lang="ts">
+  import { Component, Prop, Vue, Watch } from 'vue-property-decorator';
+
+  @Component
+  export default class Group extends Vue {
+    @Prop({ type: String, required: true }) name!: string;
+
+    group = null;
+    loading = true;
+
+    created() {
+      this.fetchData();
+    }
+
+    @Watch('$route')
+    onRouteChanged() {
+      // call again the method if the route changes
+      this.fetchData();
+    }
+
     fetchData() {
-      eventFetch(`/actors/${this.name}`, this.$store)
-        .then(response => response.json())
-        .then((response) => {
-          this.group = response.data;
-          this.loading = false;
-          console.log(this.group);
-        });
-    },
-  },
-};
+      // FIXME: remove eventFetch
+      // eventFetch(`/actors/${this.name}`, this.$store)
+      //   .then(response => response.json())
+      //   .then((response) => {
+      //     this.group = response.data;
+      //     this.loading = false;
+      //     console.log(this.group);
+      //   });
+    }
+  };
 </script>
diff --git a/js/src/components/Group/GroupList.vue b/js/src/components/Group/GroupList.vue
index 7e009a2bd..c35a5b13a 100644
--- a/js/src/components/Group/GroupList.vue
+++ b/js/src/components/Group/GroupList.vue
@@ -22,11 +22,16 @@
           <v-card-title>
             <div>
               <p>{{ group.summary }}</p>
-              <p v-if="group.organizer">Organisé par <router-link :to="{name: 'Account', params: {'id': group.organizer.id}}">{{ group.organizer.username }}</router-link></p>
+              <p v-if="group.organizer">Organisé par
+                <router-link :to="{name: 'Account', params: {'id': group.organizer.id}}">{{ group.organizer.username }}</router-link>
+              </p>
             </div>
           </v-card-title>
           <v-card-actions>
-            <v-btn flat color="green" @click="joinGroup(group)"><v-icon v-if="group.locked">lock</v-icon>Join</v-btn>
+            <v-btn flat color="green" @click="joinGroup(group)">
+              <v-icon v-if="group.locked">lock</v-icon>
+              Join
+            </v-btn>
             <v-btn flat color="orange" @click="viewActor(group)">Explore</v-btn>
             <v-btn flat color="red" @click="deleteGroup(group)">Delete</v-btn>
           </v-card-actions>
@@ -37,48 +42,54 @@
   </v-container>
 </template>
 
-<script>
-export default {
-  name: 'GroupList',
-  data() {
-    return {
-      groups: [],
-      loading: true,
-    };
-  },
-  created() {
-    this.fetchData();
-  },
-  methods: {
-    username_with_domain(actor) {
+<script lang="ts">
+
+  import { Component, Vue } from 'vue-property-decorator';
+
+  @Component
+  export default class GroupList extends Vue {
+    groups = [];
+    loading = true;
+
+    created() {
+      this.fetchData();
+    }
+
+    usernameWithDomain(actor) {
       return actor.username + (actor.domain === null ? '' : `@${actor.domain}`);
-    },
+    }
+
     fetchData() {
-      eventFetch('/groups', this.$store)
-        .then(response => response.json())
-        .then((data) => {
-          console.log(data);
-          this.loading = false;
-          this.groups = data.data;
-        });
-    },
+      // FIXME: remove eventFetch
+      // eventFetch('/groups', this.$store)
+      //   .then(response => response.json())
+      //   .then((data) => {
+      //     console.log(data);
+      //     this.loading = false;
+      //     this.groups = data.data;
+      //   });
+    }
+
     deleteGroup(group) {
       const router = this.$router;
-      eventFetch(`/groups/${this.username_with_domain(group)}`, this.$store, { method: 'DELETE' })
-        .then(response => response.json())
-        .then(() => router.push('/groups'));
-    },
+      // FIXME: remove eventFetch
+      // eventFetch(`/groups/${this.usernameWithDomain(group)}`, this.$store, { method: 'DELETE' })
+      //   .then(response => response.json())
+      //   .then(() => router.push('/groups'));
+    }
+
     viewActor(actor) {
-      this.$router.push({ name: 'Group', params: { name: this.username_with_domain(actor) } });
-    },
+      this.$router.push({ name: 'Group', params: { name: this.usernameWithDomain(actor) } });
+    }
+
     joinGroup(group) {
       const router = this.$router;
-      eventFetch(`/groups/${this.username_with_domain(group)}/join`, this.$store, { method: 'POST' })
-        .then(response => response.json())
-        .then(() => router.push({ name: 'Group', params: { name: this.username_with_domain(group) } }));
-    },
-  },
-};
+      // FIXME: remove eventFetch
+      // eventFetch(`/groups/${this.usernameWithDomain(group)}/join`, this.$store, { method: 'POST' })
+      //   .then(response => response.json())
+      //   .then(() => router.push({ name: 'Group', params: { name: this.usernameWithDomain(group) } }));
+    }
+  };
 </script>
 
 <!-- Add "scoped" attribute to limit CSS to this component only -->
diff --git a/js/src/components/Home.vue b/js/src/components/Home.vue
index deb189d44..8f923d49e 100644
--- a/js/src/components/Home.vue
+++ b/js/src/components/Home.vue
@@ -1,128 +1,134 @@
 <template>
   <v-container>
-      <v-img
-        :gradient="gradient"
-        src="https://picsum.photos/1200/900"
-        dark
-        height="300"
-        v-if="!user"
-      >
-          <v-container fill-height>
-              <v-layout align-center>
-                  <v-flex text-xs-center>
-                      <h1 class="display-3">Find events you like</h1>
-                      <h2>Share it with Mobilizon</h2>
-                      <v-btn :to="{ name: 'Register' }"><translate>Register</translate></v-btn>
-                  </v-flex>
-              </v-layout>
-          </v-container>
-      </v-img>
-      <v-layout v-else>
-          <v-flex xs12 sm8 offset-sm2>
-            <v-layout row wrap>
-                <v-flex xs12 sm6>
-                  <h1><translate :translate-params="{username: actor.preferredUsername}">Welcome back %{username}</translate></h1>
-                </v-flex>
-                <v-flex xs12 sm6>
-                <v-layout align-center>
-                  <span class="events-nearby title">Events nearby </span><v-text-field
-                    solo
-                    append-icon="place"
-                    :value="ipLocation()"
-                  ></v-text-field>
-                </v-layout>
-              </v-flex>
+    <v-img
+      :gradient="gradient"
+      src="https://picsum.photos/1200/900"
+      dark
+      height="300"
+      v-if="!user"
+    >
+      <v-container fill-height>
+        <v-layout align-center>
+          <v-flex text-xs-center>
+            <h1 class="display-3">Find events you like</h1>
+            <h2>Share it with Mobilizon</h2>
+            <v-btn :to="{ name: 'Register' }">
+              <translate>Register</translate>
+            </v-btn>
+          </v-flex>
+        </v-layout>
+      </v-container>
+    </v-img>
+    <v-layout v-else>
+      <v-flex xs12 sm8 offset-sm2>
+        <v-layout row wrap>
+          <v-flex xs12 sm6>
+            <h1>
+              <translate :translate-params="{username: actor.preferredUsername}">Welcome back %{username}</translate>
+            </h1>
+          </v-flex>
+          <v-flex xs12 sm6>
+            <v-layout align-center>
+              <span class="events-nearby title">Events nearby </span>
+              <v-text-field
+                solo
+                append-icon="place"
+                :value="ipLocation()"
+              ></v-text-field>
             </v-layout>
-              <div v-if="$apollo.loading">
-                Still loading
-              </div>
-              <v-card v-if="events.length > 0">
-                <v-layout row wrap>
-                    <v-flex md4 v-for="event in events" :key="event.uuid">
-                        <v-card :to="{ name: 'Event', params:{ uuid: event.uuid } }">
-                            <v-img v-if="!event.image"
-                                          class="white--text"
-                                          height="200px"
-                                          src="https://picsum.photos/g/400/200/"
-                            >
-                                <v-container fill-height fluid>
-                                    <v-layout fill-height>
-                                        <v-flex xs12 align-end flexbox>
-                                            <span class="headline black--text">{{ event.title }}</span>
-                                        </v-flex>
-                                    </v-layout>
-                                </v-container>
-                            </v-img>
-                            <v-card-title primary-title>
-                                <div>
-                                    <span class="grey--text">{{ event.begins_on | formatDay }}</span><br>
-                                    <router-link :to="{name: 'Account', params: { name: event.organizerActor.preferredUsername } }">
-                                        <v-avatar size="25px">
-                                            <img class="img-circle elevation-7 mb-1"
-                                                :src="event.organizerActor.avatarUrl"
-                                            >
-                                        </v-avatar>
-                                    </router-link>
-                                    <span v-if="event.organizerActor">Organisé par {{ event.organizerActor.name ? event.organizerActor.name : event.organizerActor.preferredUsername }}</span>
-                                </div>
-                            </v-card-title>
-                        </v-card>
-                    </v-flex>
-                </v-layout>
-              </v-card>
-              <v-alert v-else :value="true" type="error">
-                No events found
-              </v-alert>
           </v-flex>
-      </v-layout>
+        </v-layout>
+        <div v-if="$apollo.loading">
+          Still loading
+        </div>
+        <v-card v-if="events.length > 0">
+          <v-layout row wrap>
+            <v-flex md4 v-for="event in events" :key="event.uuid">
+              <v-card :to="{ name: 'Event', params:{ uuid: event.uuid } }">
+                <v-img v-if="!event.image"
+                       class="white--text"
+                       height="200px"
+                       src="https://picsum.photos/g/400/200/"
+                >
+                  <v-container fill-height fluid>
+                    <v-layout fill-height>
+                      <v-flex xs12 align-end flexbox>
+                        <span class="headline black--text">{{ event.title }}</span>
+                      </v-flex>
+                    </v-layout>
+                  </v-container>
+                </v-img>
+                <v-card-title primary-title>
+                  <div>
+                    <span class="grey--text">{{ event.begins_on | formatDay }}</span><br>
+                    <router-link :to="{name: 'Account', params: { name: event.organizerActor.preferredUsername } }">
+                      <v-avatar size="25px">
+                        <img class="img-circle elevation-7 mb-1"
+                             :src="event.organizerActor.avatarUrl"
+                        >
+                      </v-avatar>
+                    </router-link>
+                    <span v-if="event.organizerActor">Organisé par {{ event.organizerActor.name ? event.organizerActor.name : event.organizerActor.preferredUsername }}</span>
+                  </div>
+                </v-card-title>
+              </v-card>
+            </v-flex>
+          </v-layout>
+        </v-card>
+        <v-alert v-else :value="true" type="error">
+          No events found
+        </v-alert>
+      </v-flex>
+    </v-layout>
   </v-container>
 </template>
 
-<script>
-import ngeohash from 'ngeohash';
-import {AUTH_USER_ACTOR, AUTH_USER_ID} from '@/constants';
-import { FETCH_EVENTS } from '@/graphql/event';
+<script lang="ts">
+  import ngeohash from 'ngeohash';
+  import { AUTH_USER_ACTOR, AUTH_USER_ID } from '@/constants';
+  import { FETCH_EVENTS } from '@/graphql/event';
+  import { Component, Vue } from 'vue-property-decorator';
 
-export default {
-  name: 'Home',
-  data() {
-    return {
-      gradient: 'to top right, rgba(63,81,181, .7), rgba(25,32,72, .7)',
-      searchTerm: null,
-      location_field: {
-        loading: false,
-        search: null,
+  @Component({
+    apollo: {
+      events: {
+        query: FETCH_EVENTS,
       },
-      events: [],
-      locations: [],
-      city: { name: null },
-      country: { name: null },
-      actor: JSON.parse(localStorage.getItem(AUTH_USER_ACTOR)),
-      user: localStorage.getItem(AUTH_USER_ID),
-    };
-  },
-  apollo: {
-    events: {
-      query: FETCH_EVENTS,
     },
-  },
-  computed: {
-    displayed_name() {
+  })
+  export default class Home extends Vue {
+    gradient = 'to top right, rgba(63,81,181, .7), rgba(25,32,72, .7)';
+    searchTerm = null;
+    location_field = {
+      loading: false,
+      search: null,
+    };
+    events = [];
+    locations = [];
+    city = { name: null };
+    country = { name: null };
+    // FIXME: correctly parse local storage
+    actor = JSON.parse(localStorage.getItem(AUTH_USER_ACTOR) || '{}');
+    user = localStorage.getItem(AUTH_USER_ID);
+
+    get displayed_name() {
       return this.actor.name === null ? this.actor.preferredUsername : this.actor.name;
-    },
-  },
-  methods: {
+    }
+
     fetchLocations() {
-      eventFetch('/locations', this.$store)
-        .then(response => (response.json()))
-        .then((response) => {
-          this.locations = response;
-        });
-    },
+      // FIXME: remove eventFetch
+      // eventFetch('/locations', this.$store)
+      //   .then(response => (response.json()))
+      //   .then((response) => {
+      //     this.locations = response;
+      //   });
+    }
+
     geoLocalize() {
       const router = this.$router;
-      if (sessionStorage.getItem('City')) {
-        router.push({ name: 'EventList', params: { location: localStorage.getItem('City') } });
+      const sessionCity = sessionStorage.getItem('City')
+      if (sessionCity) {
+        router.push({ name: 'EventList', params: { location: sessionCity } });
       } else {
         navigator.geolocation.getCurrentPosition((pos) => {
           const crd = pos.coords;
@@ -136,27 +142,29 @@ export default {
           maximumAge: 0,
         });
       }
-    },
+    }
+
     getAddressData(addressData) {
       const geohash = ngeohash.encode(addressData.latitude, addressData.longitude, 11);
       sessionStorage.setItem('City', geohash);
       this.$router.push({ name: 'EventList', params: { location: geohash } });
-    },
+    }
+
     viewEvent(event) {
       this.$router.push({ name: 'Event', params: { uuid: event.uuid } });
-    },
+    }
+
     ipLocation() {
       return this.city.name ? this.city.name : this.country.name;
-    },
-  },
-};
+    }
+  }
 </script>
 
 <!-- Add "scoped" attribute to limit CSS to this component only -->
 <style scoped>
   .search-autocomplete {
     border: 1px solid #dbdbdb;
-    color: rgba(0,0,0,.87);
+    color: rgba(0, 0, 0, .87);
   }
 
   .events-nearby {
diff --git a/js/src/components/Location.vue b/js/src/components/Location.vue
index cbd47afdd..82a08fe5c 100644
--- a/js/src/components/Location.vue
+++ b/js/src/components/Location.vue
@@ -24,29 +24,28 @@
   </div>
 </template>
 
-<script>
-
-export default {
-  data() {
-    return {
-      description: 'Paris, France',
-      center: { lat: 48.85, lng: 2.35 },
-      markers: [],
-    };
-  },
-  props: ['address'],
-  methods: {
+<script lang="ts">
+  import { Component, Prop, Vue } from 'vue-property-decorator';
+
+  @Component
+  export default class Location extends Vue {
+    @Prop(String) address!: string;
+
+    description = 'Paris, France';
+    center = { lat: 48.85, lng: 2.35 };
+    markers: any[] = [];
+
     setPlace(place) {
       this.center = {
         lat: place.geometry.location.lat(),
         lng: place.geometry.location.lng(),
       };
-      this.markers = [{
+      this.markers = [ {
         position: { lat: this.center.lat, lng: this.center.lng },
-      }];
+      } ];
+
       this.$emit('input', place.formatted_address);
-    },
-  },
-};
+    }
+  };
 
 </script>
diff --git a/js/src/components/NavBar.vue b/js/src/components/NavBar.vue
index 6c0e7e702..ba3727451 100644
--- a/js/src/components/NavBar.vue
+++ b/js/src/components/NavBar.vue
@@ -96,75 +96,74 @@
 </style>
 
 <script lang="ts">
-  import { Component, Prop, Vue, Watch } from 'vue-property-decorator';
-  import { AUTH_USER_ACTOR, AUTH_USER_ID } from '@/constants';
-  import { SEARCH } from '@/graphql/search';
+import { Component, Prop, Vue, Watch } from 'vue-property-decorator';
+import { AUTH_USER_ACTOR, AUTH_USER_ID } from '@/constants';
+import { SEARCH } from '@/graphql/search';
 
-  @Component({
-    apollo: {
-      search: {
-        query: SEARCH,
-        variables() {
-          return {
-            searchText: this.searchText,
-          };
-        },
-        skip() {
-          return !this.searchText;
-        },
+@Component({
+  apollo: {
+    search: {
+      query: SEARCH,
+      variables() {
+        return {
+          searchText: this.searchText,
+        };
       },
-    }
-  })
-  export default class NavBar extends Vue {
-    @Prop({ required: true, type: Function }) toggleDrawer!: Function;
-
-    notificationMenu = false;
-    notifications = [
-      { header: 'Coucou' },
-      { title: 'T\'as une notification', subtitle: 'Et elle est cool' },
-    ];
-    model = null;
-    search: any[] = [];
-    searchText: string | null = null;
-    searchSelect = null;
-    actor: string | null = localStorage.getItem(AUTH_USER_ACTOR);
-    user: string | null = localStorage.getItem(AUTH_USER_ID);
+      skip() {
+        return !this.searchText;
+      },
+    },
+  },
+})
+export default class NavBar extends Vue {
+  @Prop({ required: true, type: Function }) toggleDrawer!: Function;
 
-    get items() {
-      return this.search.map(searchEntry => {
-        switch (searchEntry.__typename) {
-          case 'Actor':
-            searchEntry.label = searchEntry.preferredUsername + (searchEntry.domain === null ? '' : `@${searchEntry.domain}`);
-            break;
-          case 'Event':
-            searchEntry.label = searchEntry.title;
-            break;
-        }
-        return searchEntry;
-      });
-    }
+  notificationMenu = false;
+  notifications = [
+    { header: 'Coucou' },
+    { title: 'T\'as une notification', subtitle: 'Et elle est cool' },
+  ];
+  model = null;
+  search: any[] = [];
+  searchText: string | null = null;
+  searchSelect = null;
+  actor = localStorage.getItem(AUTH_USER_ACTOR);
+  user = localStorage.getItem(AUTH_USER_ID);
 
-    @Watch('model')
-    onModelChanged(val) {
-      switch (val.__typename) {
-        case 'Event':
-          this.$router.push({ name: 'Event', params: { uuid: val.uuid } });
-          break;
+  get items() {
+    return this.search.map(searchEntry => {
+      switch (searchEntry.__typename) {
         case 'Actor':
-          this.$router.push({ name: 'Account', params: { name: this.username_with_domain(val) } });
+          searchEntry.label = searchEntry.preferredUsername + (searchEntry.domain === null ? '' : `@${searchEntry.domain}`);
+          break;
+        case 'Event':
+          searchEntry.label = searchEntry.title;
           break;
       }
-    }
+      return searchEntry;
+    });
+  }
 
-    username_with_domain(actor) {
-      return actor.preferredUsername + (actor.domain === null ? '' : `@${actor.domain}`);
+  @Watch('model')
+  onModelChanged(val) {
+    switch (val.__typename) {
+      case 'Event':
+        this.$router.push({ name: 'Event', params: { uuid: val.uuid } });
+        break;
+      case 'Actor':
+        this.$router.push({ name: 'Account', params: { name: this.username_with_domain(val) } });
+        break;
     }
+  }
 
-    enter() {
-      console.log('enter');
-      this.$apollo.queries[ 'search' ].refetch();
-    }
+  username_with_domain(actor) {
+    return actor.preferredUsername + (actor.domain === null ? '' : `@${actor.domain}`);
+  }
 
+  enter() {
+    console.log('enter');
+    this.$apollo.queries['search'].refetch();
   }
 
+}
 </script>
diff --git a/js/src/graphql/event.ts b/js/src/graphql/event.ts
index 689ce2e33..4de9b9d6d 100644
--- a/js/src/graphql/event.ts
+++ b/js/src/graphql/event.ts
@@ -90,11 +90,11 @@ export const CREATE_EVENT = gql`
         $addressType: AddressType!,
     ) {
         createEvent(
-            title: $title, 
-            description: $description, 
-            beginsOn: $beginsOn, 
-            organizerActorId: $organizerActorId, 
-            categoryId: $categoryId, 
+            title: $title,
+            description: $description,
+            beginsOn: $beginsOn,
+            organizerActorId: $organizerActorId,
+            categoryId: $categoryId,
             addressType: $addressType) {
                 uuid,
                 title,
diff --git a/js/src/graphql/search.ts b/js/src/graphql/search.ts
index 5182a98ca..e57c084d5 100644
--- a/js/src/graphql/search.ts
+++ b/js/src/graphql/search.ts
@@ -16,4 +16,4 @@ query SearchEvents($searchText: String!) {
     }
   }
 }
-`;
\ No newline at end of file
+`;
diff --git a/js/src/registerServiceWorker.ts b/js/src/registerServiceWorker.ts
index fbf9078f9..844b93bb9 100644
--- a/js/src/registerServiceWorker.ts
+++ b/js/src/registerServiceWorker.ts
@@ -5,8 +5,7 @@ import { register } from 'register-service-worker';
 if (process.env.NODE_ENV === 'production') {
   register(`${process.env.BASE_URL}service-worker.js`, {
     ready() {
-      console.log('App is being served from cache by a service worker.\n' +
-        'For more details, visit https://goo.gl/AFskqB');
+      console.log('App is being served from cache by a service worker.\nFor more details, visit https://goo.gl/AFskqB');
     },
     cached() {
       console.log('Content has been cached for offline use.');
diff --git a/js/src/shims-tsx.d.ts b/js/src/shims-tsx.d.ts
index c656c68b8..3b88b5829 100644
--- a/js/src/shims-tsx.d.ts
+++ b/js/src/shims-tsx.d.ts
@@ -1,4 +1,4 @@
-import Vue, { VNode } from 'vue'
+import Vue, { VNode } from 'vue';
 
 declare global {
   namespace JSX {
@@ -7,7 +7,7 @@ declare global {
     // tslint:disable no-empty-interface
     interface ElementClass extends Vue {}
     interface IntrinsicElements {
-      [elem: string]: any
+      [elem: string]: any;
     }
   }
 }
diff --git a/js/src/shims-vue.d.ts b/js/src/shims-vue.d.ts
index d9f24faa4..8f6f41026 100644
--- a/js/src/shims-vue.d.ts
+++ b/js/src/shims-vue.d.ts
@@ -1,4 +1,4 @@
 declare module '*.vue' {
-  import Vue from 'vue'
-  export default Vue
+  import Vue from 'vue';
+  export default Vue;
 }
diff --git a/js/tslint.json b/js/tslint.json
index 6bd52c80b..cb6227b2b 100644
--- a/js/tslint.json
+++ b/js/tslint.json
@@ -2,6 +2,7 @@
   "extends": "tslint-config-airbnb",
   "rules": {
     "max-line-length": [ true, 140 ],
-    "import-name": false
+    "import-name": false,
+    "ter-arrow-parens": false
   }
 }
-- 
GitLab