diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 89d533e50ec3edaf1b3916b80bebdb35ddf9eae8..64226ef4d8cfb17bd806c1510b3063d28b96f959 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -16,30 +16,41 @@
 stages:          # List of stages for jobs, and their order of execution
   - build
   - test
-  - deploy
 
-build-job:       # This job runs in the build stage, which runs first.
+image: node:16
+
+cache:
+  paths:
+    - node_modules/
+
+install_dependencies_job:       # This job runs in the build stage, which runs first.
   stage: build
-  script:
-    - echo "Compiling the code..."
-    - echo "Compile complete."
 
-unit-test-job:   # This job runs in the test stage.
-  stage: test    # It only starts when the job in the build stage completes successfully.
   script:
-    - echo "Running unit tests... This will take about 60 seconds."
-    - sleep 60
-    - echo "Code coverage is 90%"
+    - echo "Installing dependencies..."
+    - npm install
+    - echo "Dependencies installed."
+  artifacts:
+    paths:
+      - node_modules/
+
 
 lint-test-job:   # This job also runs in the test stage.
   stage: test    # It can run at the same time as unit-test-job (in parallel).
   script:
-    - echo "Linting code... This will take about 10 seconds."
-    - sleep 10
-    - echo "No lint issues found."
+    - echo "Linting the code..."
+    - npm run lint
+    - echo "Code-linting complete."
+  artifacts:
+    paths:
+      - node_modules/
 
-deploy-job:      # This job runs in the deploy stage.
-  stage: deploy  # It only runs when *both* jobs in the test stage complete successfully.
+unit-test-job:   # This job runs in the test stage.
+  stage: test    # It only starts when the job in the build stage completes successfully.
   script:
-    - echo "Deploying application..."
-    - echo "Application successfully deployed."
+    - echo "Running unit tests..."
+    - npm run test:unit -- --coverage
+    - echo "Unit tests complete."
+  artifacts:
+    paths:
+      - node_modules/
diff --git a/package-lock.json b/package-lock.json
index b00e266c5d8bf882654b8e90bb2188ae5fe3869b..52e8f82391ad66170f4ffe771040b0cdab18290f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -14,13 +14,14 @@
         "axios": "^0.26.1",
         "core-js": "^3.8.3",
         "cssom": "^0.5.0",
+        "jwt-decode": "^3.1.2",
         "roboto-fontface": "*",
         "vue": "^3.2.13",
         "vue-router": "^4.0.3",
         "vuelidate": "^0.7.7",
         "vuex": "^4.0.0",
         "vuex-persistedstate": "^4.1.0",
-        "webfontloader": "^1.0.0"
+        "webfontloader": "^1.6.28"
       },
       "devDependencies": {
         "@babel/core": "^7.12.16",
@@ -11057,6 +11058,11 @@
         "graceful-fs": "^4.1.6"
       }
     },
+    "node_modules/jwt-decode": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-3.1.2.tgz",
+      "integrity": "sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A=="
+    },
     "node_modules/kind-of": {
       "version": "3.2.2",
       "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
@@ -24450,6 +24456,11 @@
         "universalify": "^2.0.0"
       }
     },
+    "jwt-decode": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-3.1.2.tgz",
+      "integrity": "sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A=="
+    },
     "kind-of": {
       "version": "3.2.2",
       "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
diff --git a/package.json b/package.json
index cce55e6cd844586f7c09af08b82704f174640fc8..1a2ddb7600d61ce70a4bf73e48bccf3bfb399e50 100644
--- a/package.json
+++ b/package.json
@@ -15,13 +15,14 @@
     "axios": "^0.26.1",
     "core-js": "^3.8.3",
     "cssom": "^0.5.0",
+    "jwt-decode": "^3.1.2",
     "roboto-fontface": "*",
     "vue": "^3.2.13",
     "vue-router": "^4.0.3",
     "vuelidate": "^0.7.7",
     "vuex": "^4.0.0",
     "vuex-persistedstate": "^4.1.0",
-    "webfontloader": "^1.0.0"
+    "webfontloader": "^1.6.28"
   },
   "devDependencies": {
     "@babel/core": "^7.12.16",
diff --git a/src/assets/defaultUserProfileImage.jpg b/src/assets/defaultUserProfileImage.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..18c8cd70555d36af38f2bf164b0a22740b14cf6b
Binary files /dev/null and b/src/assets/defaultUserProfileImage.jpg differ
diff --git a/src/assets/removeIcon.png b/src/assets/removeIcon.png
new file mode 100644
index 0000000000000000000000000000000000000000..3c66b2a75ff7d81fd33622bf13a903f838f114ee
Binary files /dev/null and b/src/assets/removeIcon.png differ
diff --git a/src/components/CreateNewGroup.vue b/src/components/CreateNewGroup.vue
new file mode 100644
index 0000000000000000000000000000000000000000..ceb5303249f9797e27e06fea8c189f9ab99a095f
--- /dev/null
+++ b/src/components/CreateNewGroup.vue
@@ -0,0 +1,306 @@
+<template>
+  <div class="m-6">
+    <!-- Component heading -->
+    <div class="flex justify-center mt-6">
+      <p class="text-4xl">Opprett Gruppe</p>
+    </div>
+
+    <!-- Radio boxes -->
+    <div class="mt-6">
+      <label
+        class="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-300"
+        id="radioBoxLabel"
+        >Synlighet</label
+      >
+      <div class="form-check">
+        <input
+          class="form-check-input appearance-none rounded-full h-4 w-4 border border-gray-300 bg-white checked:bg-blue-600 checked:border-blue-600 focus:outline-none transition duration-200 mt-1 align-top bg-no-repeat bg-center bg-contain float-left mr-2 cursor-pointer"
+          type="radio"
+          name="flexRadioDefault"
+          id="flexRadioOpen"
+          value="Ã…pen"
+          @change="checkRadioButton($event)"
+          checked
+        />
+        <label
+          class="form-check-label inline-block text-gray-800"
+          for="flexRadioOpen"
+          id="radioBoxOpenLabel"
+        >
+          Ã…pen
+        </label>
+      </div>
+      <div class="form-check">
+        <input
+          class="form-check-input appearance-none rounded-full h-4 w-4 border border-gray-300 bg-white checked:bg-blue-600 checked:border-blue-600 focus:outline-none transition duration-200 mt-1 align-top bg-no-repeat bg-center bg-contain float-left mr-2 cursor-pointer"
+          type="radio"
+          name="flexRadioDefault"
+          id="flexRadioPrivate"
+          value="Privat"
+          @change="checkRadioButton($event)"
+        />
+        <label
+          class="form-check-label inline-block text-gray-800"
+          for="flexRadioPrivate"
+          id="radioBoxPrivateLabel"
+        >
+          Privat
+        </label>
+      </div>
+    </div>
+
+    <!-- Title -->
+    <div class="mt-6" :class="{ error: v$.group.name.$errors.length }">
+      <label
+        class="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-300"
+        id="titleLabel"
+        >Gruppenavn</label
+      >
+      <input
+        type="text"
+        id="title"
+        class="bg-gray-200 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
+        v-model="v$.group.name.$model"
+        required
+      />
+
+      <!-- error message for title-->
+      <div
+        class="text-red"
+        v-for="(error, index) of v$.group.name.$errors"
+        :key="index"
+      >
+        <div class="text-red-600 text-sm">
+          {{ error.$message }}
+        </div>
+      </div>
+    </div>
+
+    <!-- Select category -->
+    <div class="mt-6">
+      <label
+        class="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-400"
+        id="selectCategoryLabel"
+        >Kategori</label
+      >
+      <select
+        v-model="v$.group.select.$model"
+        id="categories"
+        class="bg-gray-200 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
+      >
+        <option class="text-gray-400" value="" disabled selected>
+          Select a category
+        </option>
+        <option
+          v-for="category in group.categories"
+          :key="category"
+          class="text-gray-900 text-sm"
+        >
+          {{ category }}
+        </option>
+      </select>
+
+      <!-- error message for select box -->
+      <div
+        class="text-red"
+        v-for="(error, index) of v$.group.select.$errors"
+        :key="index"
+      >
+        <div class="text-red-600 text-sm">
+          {{ error.$message }}
+        </div>
+      </div>
+    </div>
+
+    <!-- Description -->
+    <div class="mt-6" :class="{ error: v$.group.description.$errors.length }">
+      <label
+        class="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-400"
+        id="descriptionLabel"
+        >Beskrivelse</label
+      >
+      <textarea
+        id="description"
+        rows="4"
+        v-model="v$.group.description.$model"
+        class="block p-2.5 w-full text-sm text-gray-900 bg-gray-200 rounded-lg border border-gray-300 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
+        required
+      ></textarea>
+
+      <!-- error message for description -->
+      <div
+        class="text-red"
+        v-for="(error, index) of v$.group.description.$errors"
+        :key="index"
+      >
+        <div class="text-red-600 text-sm">
+          {{ error.$message }}
+        </div>
+      </div>
+    </div>
+
+    <!-- Images -->
+    <div class="mt-6">
+      <label
+        class="block mb-2 text-xl font-medium text-gray-900 dark:text-gray-400"
+        id="imageLabel"
+      >
+        Bilde
+      </label>
+
+      <input
+        type="file"
+        ref="file"
+        style="display: none"
+        @change="addImage"
+        multiple
+        accept="image/png, image/jpeg"
+      />
+
+      <!-- Button for adding an image -->
+      <div class="inline-flex rounded-md shadow-sm">
+        <button
+          @click="$refs.file.click()"
+          class="text-black bg-gray-200 hover:bg-grey-800 focus:ring-4 focus:outline-none focus:ring-grey-300 font-medium rounded-lg text-sm sm:w-auto px-5 py-2.5 text-center dark:bg-grey-600 dark:hover:bg-grey-700 dark:focus:ring-grey-800 disabled:opacity-50 cursor-not-allowed"
+          :disabled="imageAdded"
+        >
+          Velg bilde
+        </button>
+
+        <!-- Button for removing an image -->
+        <button
+          class="w-1/12 ml-5 text-white bg-white-500 font-medium rounded-lg text-sm"
+          v-show="imageAdded"
+          @click="removeImage"
+        >
+          <img src="../assets/removeIcon.png" alt="Remove icon image" />
+        </button>
+      </div>
+
+      <!-- Div box for showing all chosen images -->
+      <div v-for="image in group.images" :key="image" class="m-2">
+        <img :src="image" class="w-1/2 inline" alt="Bilde av gjenstanden" />
+      </div>
+    </div>
+
+    <!-- Save item button -->
+    <div class="flex justify-center mt-10">
+      <button
+        @click="saveClicked"
+        class="content-center text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm sm:w-auto px-5 py-2.5 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800"
+        id="saveButton"
+      >
+        Lagre
+      </button>
+    </div>
+  </div>
+</template>
+
+<script>
+import useVuelidate from "@vuelidate/core";
+import { required, helpers, maxLength } from "@vuelidate/validators";
+
+export default {
+  name: "CreateNewGroup.vue",
+
+  setup() {
+    return { v$: useVuelidate() };
+  },
+
+  validations() {
+    return {
+      group: {
+        name: {
+          required: helpers.withMessage(
+            () => "Navnt kan ikke være tom",
+            required
+          ),
+          max: helpers.withMessage(
+            () => `Navnet kan være på max 50 tegn`,
+            maxLength(50)
+          ),
+        },
+        description: {
+          required: helpers.withMessage(
+            () => "Beskrivelsen kan ikke være tom",
+            required
+          ),
+          max: helpers.withMessage(
+            () => `Beskrivelsen kan inneholde max 200 tegn`,
+            maxLength(200)
+          ),
+        },
+        select: {
+          required: helpers.withMessage(() => `Velg en kategori`, required),
+        },
+      },
+    };
+  },
+  data() {
+    return {
+      group: {
+        name: "",
+        select: null,
+        description: "",
+        images: [],
+        categories: ["Borettslag", "Idrettsklubb", "Fritidsklubb"],
+        radio: null,
+      },
+      imageThere: false,
+    };
+  },
+  computed: {
+    imageAdded: function () {
+      if (this.imageThere) {
+        return true;
+      } else {
+        return false;
+      }
+    },
+  },
+  methods: {
+    removeImage: function () {
+      this.group.images.pop();
+      this.imageThere = false;
+      console.log("Bilder nå: " + this.group.images.length);
+    },
+    checkRadioButton: function (event) {
+      this.group.radio = event.target.value;
+      console.log(this.group.radio);
+    },
+    checkValidation: function () {
+      console.log("sjekker validering");
+
+      this.v$.group.$touch();
+      if (this.v$.group.$invalid) {
+        console.log("Invalid, avslutter...");
+        return false;
+      }
+
+      console.log("validert!");
+      return true;
+    },
+
+    async saveClicked() {
+      console.log("Attempting to save item");
+
+      if (this.checkValidation()) {
+        console.log("validert, videre...");
+        console.log("Navn: " + this.group.name);
+        console.log("Synlighet: " + this.group.radio);
+        console.log("Kategori: " + this.group.select);
+        console.log("Beskrivelse: " + this.group.description);
+        console.log("bilder: " + this.group.images);
+      }
+    },
+
+    addImage: function (event) {
+      console.log(event.target.files);
+      this.group.images.push(URL.createObjectURL(event.target.files[0]));
+      console.log("antall bilder: " + this.group.images.length);
+      this.imageThere = true;
+      console.log("image: " + this.imageThere);
+    },
+  },
+};
+</script>
diff --git a/src/components/HelloWorld.vue b/src/components/HelloWorld.vue
index 84bd14e103584c8f13019ee33fe36e9a3f1e3ba3..ecc5bf651ccbb96fd5cc76ffaa9a204ea7d99310 100644
--- a/src/components/HelloWorld.vue
+++ b/src/components/HelloWorld.vue
@@ -1,78 +1,11 @@
 <template>
-  <v-container>
-    <v-row class="text-center">
-      <v-col cols="12">
-        <v-img
-          :src="require('../assets/logo.svg')"
-          class="my-3"
-          contain
-          height="200"
-        />
-      </v-col>
+  <div class="flex justify-center">
+    <router-link to="/" class="m-6">Logg inn</router-link>
 
-      <v-col class="mb-4">
-        <h1 class="display-2 font-weight-bold mb-3">
-          Welcome to the Vuetify 3 Beta
-        </h1>
+    <router-link to="/register" class="m-6">Registrer deg</router-link>
 
-        <p class="subheading font-weight-regular">
-          For help and collaboration with other Vuetify developers,
-          <br />please join our online
-          <a href="https://community.vuetifyjs.com" target="_blank"
-            >Discord Community</a
-          >
-        </p>
-      </v-col>
-
-      <v-col class="mb-5" cols="12">
-        <h2 class="headline font-weight-bold mb-5">What's next?</h2>
-
-        <v-row justify="center">
-          <a
-            v-for="(next, i) in whatsNext"
-            :key="i"
-            :href="next.href"
-            class="subheading mx-3"
-            target="_blank"
-          >
-            {{ next.text }}
-          </a>
-        </v-row>
-      </v-col>
-
-      <v-col class="mb-5" cols="12">
-        <h2 class="headline font-weight-bold mb-5">Important Links</h2>
-
-        <v-row justify="center">
-          <a
-            v-for="(link, i) in importantLinks"
-            :key="i"
-            :href="link.href"
-            class="subheading mx-3"
-            target="_blank"
-          >
-            {{ link.text }}
-          </a>
-        </v-row>
-      </v-col>
-
-      <v-col class="mb-5" cols="12">
-        <h2 class="headline font-weight-bold mb-5">Ecosystem</h2>
-
-        <v-row justify="center">
-          <a
-            v-for="(eco, i) in ecosystem"
-            :key="i"
-            :href="eco.href"
-            class="subheading mx-3"
-            target="_blank"
-          >
-            {{ eco.text }}
-          </a>
-        </v-row>
-      </v-col>
-    </v-row>
-  </v-container>
+    <router-link to="/about" class="m-6">Om BoCo</router-link>
+  </div>
 </template>
 
 <script>
@@ -80,52 +13,7 @@ export default {
   name: "HelloWorld",
 
   data: () => ({
-    ecosystem: [
-      {
-        text: "vuetify-loader",
-        href: "https://github.com/vuetifyjs/vuetify-loader",
-      },
-      {
-        text: "github",
-        href: "https://github.com/vuetifyjs/vuetify",
-      },
-      {
-        text: "awesome-vuetify",
-        href: "https://github.com/vuetifyjs/awesome-vuetify",
-      },
-    ],
-    importantLinks: [
-      {
-        text: "Chat",
-        href: "https://community.vuetifyjs.com",
-      },
-      {
-        text: "Made with Vuetify",
-        href: "https://madewithvuejs.com/vuetify",
-      },
-      {
-        text: "Twitter",
-        href: "https://twitter.com/vuetifyjs",
-      },
-      {
-        text: "Articles",
-        href: "https://medium.com/vuetify",
-      },
-    ],
-    whatsNext: [
-      {
-        text: "Explore components",
-        href: "https://vuetifyjs.com",
-      },
-      {
-        text: "Roadmap",
-        href: "https://vuetifyjs.com/introduction/roadmap/",
-      },
-      {
-        text: "Frequently Asked Questions",
-        href: "https://vuetifyjs.com/getting-started/frequently-asked-questions",
-      },
-    ],
-  }),
-};
+
+}),
+}
 </script>
diff --git a/src/components/LoginForm.vue b/src/components/LoginForm.vue
index 82ba58cdfb84ebc5cf9fa14c76374d5661705cec..a986f45f67e18ee7834405e64a493da89cedc72e 100644
--- a/src/components/LoginForm.vue
+++ b/src/components/LoginForm.vue
@@ -52,6 +52,7 @@
         class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
         v-model="v$.user.password.$model"
         required
+        @keyup.enter="loginClicked"
       />
       <!-- error message -->
       <div
@@ -83,14 +84,14 @@
       >
         Logg inn
       </button>
-      <div class="flex justify-center align-items: flex-end; mb-6 mt-6">
+      <div class="align-items: flex-end; mb-6 mt-6">
         <div class="ml-3 text-sm">
           <router-link to="register" class="text-blue-600"
             >Ny bruker</router-link
           >
         </div>
       </div>
-      <div class="flex flex-row min-h-screen justify-center items-center">
+      <div class="flex justify-center">
         <label>{{ message }}</label>
       </div>
     </div>
@@ -101,6 +102,7 @@
 import useVuelidate from "@vuelidate/core";
 import { required, email, helpers } from "@vuelidate/validators";
 import { doLogin } from "@/utils/apiutil";
+import { parseUserFromToken } from "@/utils/token-utils";
 
 export default {
   name: "LoginForm.vue",
@@ -153,12 +155,21 @@ export default {
 
       const loginResponse = await doLogin(loginRequest);
 
-      if (loginResponse.data === "Login failed") {
+      if (loginResponse.isLoggedIn === false) {
         this.message = "Feil e-post/passord";
         this.$store.commit("logout");
-      } else {
-        this.$store.commit("saveToken", loginResponse);
       }
+      else if (loginResponse.isLoggedIn === true) {
+        this.$store.commit("saveToken", loginResponse.token);
+        await this.$router.push("/endre");
+      }
+      else {
+        console.log("Something went wrong");
+      }
+
+      let user = parseUserFromToken();
+      let id = user.account_id;
+      this.$router.push("/profile/" + id);
     },
 
     validate() {
diff --git a/src/components/UserProfileComponents/LargeProfileCard.vue b/src/components/UserProfileComponents/LargeProfileCard.vue
new file mode 100644
index 0000000000000000000000000000000000000000..b27bd17390af37ac6aca878e982ee7b1e46e2c83
--- /dev/null
+++ b/src/components/UserProfileComponents/LargeProfileCard.vue
@@ -0,0 +1,144 @@
+<template>
+  <div
+    class="max-w-sm bg-white rounded-lg border border-gray-200 shadow-md dark:bg-gray-800 dark:border-gray-700"
+  >
+    <div v-show="isCurrentUser" class="flex justify-end px-4 pt-4">
+      <button
+        id="dropdownDefault"
+        data-dropdown-toggle="dropdown"
+        @click="dropdown = !dropdown"
+        class="hidden sm:inline-block text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700 focus:outline-none focus:ring-4 focus:ring-gray-200 dark:focus:ring-gray-700 rounded-lg text-sm p-1.5"
+        type="button"
+      >
+        <svg
+          class="w-6 h-6"
+          fill="currentColor"
+          viewBox="0 0 20 20"
+          xmlns="http://www.w3.org/2000/svg"
+        >
+          <path
+            d="M10 6a2 2 0 110-4 2 2 0 010 4zM10 12a2 2 0 110-4 2 2 0 010 4zM10 18a2 2 0 110-4 2 2 0 010 4z"
+          ></path>
+        </svg>
+      </button>
+
+      <div
+        id="dropdown"
+        v-show="dropdown"
+        zindex="2"
+        class="z-10 w-44 text-base list-none bg-white rounded divide-y divide-gray-100 shadow dark:bg-gray-700"
+      >
+        <ul class="py-1" aria-labelledby="dropdownDefault">
+          <li>
+            <router-link
+              to=""
+              class="block py-2 px-4 text-sm text-gray-700 hover:bg-gray-100 dark:hover:bg-gray-600 dark:text-gray-200 dark:hover:text-white"
+              >Mine gjenstander</router-link
+            >
+          </li>
+          <li>
+            <router-link
+              to=""
+              class="block py-2 px-4 text-sm text-gray-700 hover:bg-gray-100 dark:hover:bg-gray-600 dark:text-gray-200 dark:hover:text-white"
+              >Mine grupper
+            </router-link>
+          </li>
+          <li>
+            <router-link
+              to=""
+              class="block py-2 px-4 text-sm text-gray-700 hover:bg-gray-100 dark:hover:bg-gray-600 dark:text-gray-200 dark:hover:text-white"
+              >Leiehistorikk</router-link
+            >
+          </li>
+          <li>
+            <router-link
+              to="/newPassword"
+              class="block py-2 px-4 text-sm text-gray-700 hover:bg-gray-100 dark:hover:bg-gray-600 dark:text-gray-200 dark:hover:text-white"
+              >Endre passord</router-link
+            >
+          </li>
+          <li>
+            <router-link
+              to=""
+              class="block py-2 px-4 text-sm text-red-600 hover:bg-gray-100 dark:hover:bg-gray-600 dark:text-gray-200 dark:hover:text-white"
+              >Slett bruker</router-link
+            >
+          </li>
+        </ul>
+      </div>
+    </div>
+    <div class="flex flex-col items-center pb-10">
+      <img
+        class="mb-3 w-24 h-24 rounded-full shadow-lg"
+        src="../../assets/defaultUserProfileImage.jpg"
+        alt="Profile picture"
+      />
+      <h5 class="mb-1 text-xl font-medium text-gray-900 dark:text-white">
+        {{ user.first_name }} {{ user.last_name }}
+      </h5>
+      <div>
+        <rating-component :rating="renterRating" :ratingType="'Leietaker'" />
+        <rating-component :rating="ownerRating" :ratingType="'Utleier'" />
+      </div>
+
+      <div v-show="!isCurrentUser" class="flex mt-4 space-x-3 lg:mt-6">
+        <a
+          href="#"
+          class="inline-flex items-center py-2 px-4 text-sm font-medium text-center text-gray-900 bg-white rounded-lg border border-gray-300 hover:bg-gray-100 focus:ring-4 focus:outline-none focus:ring-gray-200 dark:bg-gray-800 dark:text-white dark:border-gray-600 dark:hover:bg-gray-700 dark:hover:border-gray-700 dark:focus:ring-gray-700"
+          >Ã…pne chat</a
+        >
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import RatingComponent from "@/components/UserProfileComponents/RatingComponent.vue";
+import { parseUserFromToken } from "@/utils/token-utils";
+import { getUser /* getRenterRating, getOwnerRating */ } from "@/utils/apiutil";
+import router from "@/router";
+
+export default {
+  name: "LargeProfileCard",
+  data() {
+    return {
+      user: {},
+      currentUser: {},
+      id: -1,
+      isCurrentUser: false,
+      renterRating: 0, //getRenterRating(this.userID),
+      ownerRating: 0, //getOwnerRating(this.userID),
+      dropdown: false,
+    };
+  },
+  components: {
+    RatingComponent,
+  },
+  methods: {
+    async getUser() {
+      this.currentUser = parseUserFromToken();
+      this.id = router.currentRoute.value.params.id;
+      if (this.id == this.currentUser.account_id) {
+        this.isCurrentUser = true;
+        this.user = this.currentUser;
+        return;
+      }
+      let getuser = await getUser(this.id);
+      this.user = {
+        account_id: getuser.userID,
+        first_name: getuser.firstName,
+        last_name: getuser.lastName,
+      };
+    },
+    getProfilePicture() {
+      /* if (this.user.picture != "") {
+        return this.user.picture;
+      } */
+      return "../assets/defaultUserProfileImage.jpg";
+    },
+  },
+  beforeMount() {
+    this.getUser();
+  },
+};
+</script>
diff --git a/src/components/UserProfileComponents/RatingComponent.vue b/src/components/UserProfileComponents/RatingComponent.vue
new file mode 100644
index 0000000000000000000000000000000000000000..f79ff22085c106a75f32e0b8294c7ef3c02e9801
--- /dev/null
+++ b/src/components/UserProfileComponents/RatingComponent.vue
@@ -0,0 +1,49 @@
+<template>
+  <ul class="flex justify-center">
+    <li>
+      <p class="ml-2 text-sm font-medium text-gray-500 dark:text-gray-400">
+        {{ ratingType }}:&nbsp;
+      </p>
+    </li>
+    <li v-for="i in 5" :key="i">
+      <svg
+        :class="getFill(i)"
+        fill="currentColor"
+        viewBox="0 0 20 20"
+        xmlns="http://www.w3.org/2000/svg"
+      >
+        <path
+          d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z"
+        ></path>
+      </svg>
+    </li>
+    <li>
+      <p class="ml-2 text-sm font-medium text-gray-500 dark:text-gray-400">
+        {{ compRating }} out of 5
+      </p>
+    </li>
+  </ul>
+</template>
+
+<script>
+export default {
+  name: "RatingComponent",
+  data() {
+    return {
+      compRating: this.rating + 0,
+    };
+  },
+  props: {
+    rating: Number,
+    ratingType: String,
+  },
+  methods: {
+    getFill(i) {
+      if (i <= this.rating) {
+        return "w-5 h-5 text-yellow-400";
+      }
+      return "w-5 h-5 text-gray-300 dark:text-gray-500";
+    },
+  },
+};
+</script>
diff --git a/src/components/UserProfileComponents/UserListItemCard.vue b/src/components/UserProfileComponents/UserListItemCard.vue
new file mode 100644
index 0000000000000000000000000000000000000000..4da140e21610587c8e88953ed9b17572c9bef98a
--- /dev/null
+++ b/src/components/UserProfileComponents/UserListItemCard.vue
@@ -0,0 +1,40 @@
+<template>
+  <div
+    class="select-none cursor-pointer hover:bg-gray-50 flex flex-1 items-center p-4"
+  >
+    <div class="flex flex-col w-10 h-10 justify-center items-center mr-4">
+      <router-link to="">
+        <img alt="profil" :src="getProfilePicture" />
+      </router-link>
+    </div>
+    <div class="flex-1 pl-1">
+      <div class="font-medium dark:text-white">
+        {{ user.first_name }} {{ user.last_name }}
+      </div>
+    </div>
+    <div class="flex flex-row justify-center">
+      <button class="w-10 text-right flex justify-end">Ã…pne chat</button>
+      <button v-if="admin" class="w-10 text-right flex justify-end">
+        Fjern bruker
+      </button>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  name: "UserListItem",
+  props: {
+    user: Object,
+    admin: Boolean,
+  },
+  methods: {
+    getProfilePicture() {
+      if (this.user.picture != "") {
+        return this.user.picture;
+      }
+      return "../assets/defaultUserProfileImage.jpg";
+    },
+  },
+};
+</script>
diff --git a/src/router/index.js b/src/router/index.js
index ee37f5728a0bd49fe00465b655978648728c19ac..ab5636466a09778a04cb08e526eb7a9961af942a 100644
--- a/src/router/index.js
+++ b/src/router/index.js
@@ -1,3 +1,4 @@
+import store from "@/store";
 import { createRouter, createWebHistory } from "vue-router";
 import HomeView from "../views/HomeView.vue";
 import LoginView from "../views/LoginView.vue";
@@ -5,18 +6,22 @@ import NewPasswordView from "../views/NewPasswordView";
 
 const routes = [
   {
-    path: "/endre", //Endre før push
+    path: "/", //Endre før push
     name: "home",
     component: HomeView,
   },
   {
     path: "/about",
     name: "about",
-    // route level code-splitting
-    // this generates a separate chunk (about.[hash].js) for this route
-    // which is lazy-loaded when the route is visited.
-    component: () =>
-      import(/* webpackChunkName: "about" */ "../views/AboutView.vue"),
+    component: () => import("../views/AboutView.vue"),
+  },
+  {
+    path: "/profile/:id",
+    name: "profile",
+    component: () => import("../views/ProfileView.vue"),
+    beforeEnter: () => {
+      if (store.state.user.token == null) router.push("login");
+    },
   },
   {
     path: "/register",
@@ -28,7 +33,7 @@ const routes = [
       import(/* webpackChunkName: "register" */ "../views/RegisterView.vue"),
   },
   {
-    path: "/",
+    path: "/login",
     name: "login",
     component: LoginView,
   },
@@ -38,9 +43,15 @@ const routes = [
     component: NewPasswordView,
   },
   {
+
     path: "/searchItemList",
     name: "searchItemList",
     component: () => import("../views/SearchItemListView.vue"),
+
+    path: "/createNewGroup",
+    name: "createNewGroup",
+    component: () => import("../views/CreateNewGroupView.vue"),
+
   },
 ];
 
diff --git a/src/utils/apiutil.js b/src/utils/apiutil.js
index a438e020751965e604551955bafcece3294f2e7a..887d222831d94469e22f88bdef28c13672fb02db 100644
--- a/src/utils/apiutil.js
+++ b/src/utils/apiutil.js
@@ -1,16 +1,20 @@
 import axios from "axios";
+import { tokenHeader } from "./token-utils";
 
 const API_URL = process.env.VUE_APP_BASEURL;
 
 export function doLogin(loginRequest) {
+  const auth = {isLoggedIn: false, token: ""};
   return axios
     .post(API_URL + "login/authentication", loginRequest)
     .then((response) => {
-      return response.data;
+      auth.isLoggedIn = true;
+      auth.token = response.data;
+      return auth;
     })
     .catch((error) => {
       console.log(error.response);
-      return error.response;
+      return auth;
     });
 }
 
@@ -28,3 +32,42 @@ export function registerUser(registerInfo) {
     })
     .catch((err) => console.log(err));
 }
+
+export async function getUser(userid) {
+  return axios
+    .get(API_URL + "users/" + userid + "/profile", {
+      headers: tokenHeader(),
+    })
+    .then((response) => {
+      return response.data;
+    })
+    .catch((error) => {
+      console.error(error);
+    });
+}
+
+export function getRenterRating(userid) {
+  return axios
+    .get(API_URL + "users/" + userid + "", {
+      headers: tokenHeader(),
+    })
+    .then((response) => {
+      return response.data;
+    })
+    .catch((error) => {
+      console.error(error);
+    });
+}
+
+export function getOwnerRating(userid) {
+  return axios
+    .get(API_URL + "users/" + userid + "", {
+      headers: tokenHeader(),
+    })
+    .then((response) => {
+      return response.data;
+    })
+    .catch((error) => {
+      console.error(error);
+    });
+}
diff --git a/src/utils/token-utils.js b/src/utils/token-utils.js
new file mode 100644
index 0000000000000000000000000000000000000000..baf77654f57a548de5a335038ed9fed4628d80e2
--- /dev/null
+++ b/src/utils/token-utils.js
@@ -0,0 +1,12 @@
+import jwt_decode from "jwt-decode";
+import store from "@/store";
+
+export function tokenHeader() {
+  let token = store.state.user.token;
+  return { Authorization: token };
+}
+
+export function parseUserFromToken() {
+  let token = store.state.user.token;
+  return jwt_decode(token);
+}
diff --git a/src/views/CreateNewGroupView.vue b/src/views/CreateNewGroupView.vue
new file mode 100644
index 0000000000000000000000000000000000000000..d3b69d68220d7cd3a1cc3cd4fa4e3398feafe696
--- /dev/null
+++ b/src/views/CreateNewGroupView.vue
@@ -0,0 +1,15 @@
+<template>
+  <CreateNewGroup></CreateNewGroup>
+</template>
+
+<script>
+import CreateNewGroup from "@/components/CreateNewGroup";
+export default {
+  name: "CreateNewGroupView.vue",
+  components: {
+    CreateNewGroup,
+  },
+};
+</script>
+
+<style scoped></style>
diff --git a/src/views/ProfileView.vue b/src/views/ProfileView.vue
new file mode 100644
index 0000000000000000000000000000000000000000..cfd2cefd902017ad7b867b7cfe447cce5648cf07
--- /dev/null
+++ b/src/views/ProfileView.vue
@@ -0,0 +1,14 @@
+<!-- View for looking at different profile display methods -->
+<template>
+  <large-profile-card :isCurrentUser="true" />
+</template>
+
+<script>
+import LargeProfileCard from "@/components/UserProfileComponents/LargeProfileCard.vue";
+
+export default {
+  components: {
+    LargeProfileCard,
+  },
+};
+</script>
diff --git a/tests/unit/apiutil-login-mock.spec.js b/tests/unit/apiutil-login-mock.spec.js
index b1c99b4c45466356b8b75bb354c1c52253edc156..b6d573beb45aff0450d4d52334a8e69cce950128 100644
--- a/tests/unit/apiutil-login-mock.spec.js
+++ b/tests/unit/apiutil-login-mock.spec.js
@@ -4,42 +4,39 @@ import axios from "axios";
 jest.mock("axios");
 
 describe("testing mocking of apiutil.js", () => {
+
   it("check that login fails with wrong credentials - against mock", async () => {
-    // mock api response on POST call (once)
-    const expectedLoginResponse = { response: "Login failed" };
+
+    const loginRequest = {
+      email: "wrong@email.com",
+      password: "thisiswrong123"};
+
+    const expectedLoginResponse = { isLoggedIn: false, token: "" }
+
     axios.post.mockImplementation(() =>
       Promise.resolve({ data: expectedLoginResponse })
     );
 
-    // do the call
-    const loginRequest = {
-      email: "wrong@email.com",
-      password: "thisiswrong123",
-    };
     const loginResponse = await doLogin(loginRequest);
 
-    //  check response
-    //  note that even if wrong username and password are used, mock is configured to return Success
-    expect(loginResponse).toEqual(expectedLoginResponse);
+    expect(loginResponse.token.isLoggedIn).toEqual(expectedLoginResponse.isLoggedIn);
   });
+
   it("check that login succeeds when correct credentials - against mock", async () => {
-    // mock api response on POST call (once)
-    const apiResponse = {
-      response:
-        "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
-    };
-    const expectedLoginResponse = { response: "Login failed" };
-    axios.post.mockImplementation(() => Promise.resolve({ data: apiResponse }));
 
-    // do the call
     const loginRequest = {
       email: "correct@email.com",
-      password: "thisiscorrect123",
-    };
+      password: "thisiscorrect123"};
+
+    const apiResponse = {isLoggedIn: true, token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM" +
+          "0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"};
+
+    const expectedLoginResponse = {isLoggedIn: false, token: ""};
+
+    axios.post.mockImplementation(() => Promise.resolve({ data: apiResponse }));
+
     const loginResponse = await doLogin(loginRequest);
 
-    //  check response
-    //  note that even if wrong username and password are used, mock is configured to return Success
-    expect(loginResponse).not.toEqual(expectedLoginResponse);
+    expect(loginResponse.token.isLoggedIn).not.toEqual(expectedLoginResponse.isLoggedIn);
   });
 });
diff --git a/tests/unit/apiutil-user-mock.spec.js b/tests/unit/apiutil-user-mock.spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..73612f0f3579f5c04b99d2424a1a57fd62ff31bc
--- /dev/null
+++ b/tests/unit/apiutil-user-mock.spec.js
@@ -0,0 +1,28 @@
+import { getUser } from "@/utils/apiutil";
+import axios from "axios";
+
+jest.mock("axios");
+
+describe("testing mocking of apiutil.js", () => {
+  it("check that existing user returns correctly", async () => {
+    const expectedResponse = {
+      response:
+        "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhY2NvdW50X2lkIjoiNiIsImV4cCI6MTY1MTEzMDU2NywiZmlyc3RfbmFtZSI6IkFsaWRhIiwiZW1haWwiOiJhbGlkYUB0ZXN0Lm5vIn0.Cp3_qfLhA55j5yaa1WPG97LNtvAZssxo0ROP3VIrHVs",
+    };
+    axios.get.mockImplementation(() =>
+      Promise.resolve({ data: expectedResponse })
+    );
+
+    const userResponse = await getUser(1);
+    expect(userResponse).not.toEqual({ response: "User not found in DB" });
+  });
+  it("check that non-existing user returns 404", async () => {
+    const expectedResponse = { response: "User not found in DB" };
+    axios.get.mockImplementation(() =>
+      Promise.resolve({ data: expectedResponse })
+    );
+
+    const userResponse = await getUser(100000);
+    expect(userResponse).toEqual(expectedResponse);
+  });
+});
diff --git a/tests/unit/create-new-group.spec.js b/tests/unit/create-new-group.spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..2b269b082ece04c8d1cbfa9c8eb9233d0d4e00f8
--- /dev/null
+++ b/tests/unit/create-new-group.spec.js
@@ -0,0 +1,49 @@
+import { shallowMount } from "@vue/test-utils";
+import CreateNewGroup from "@/components/CreateNewGroup.vue";
+
+describe("CreateNewGroup elements rendering", () => {
+
+    it("renders all labels", () => {
+
+        const wrapper = shallowMount(CreateNewGroup);
+
+        expect(wrapper.find('#radioBoxLabel').text()).toMatch("Synlighet");
+        expect(wrapper.find('#radioBoxOpenLabel').text()).toMatch("Ã…pen");
+        expect(wrapper.find('#radioBoxPrivateLabel').text()).toMatch("Privat");
+        expect(wrapper.find('#titleLabel').text()).toMatch("Gruppenavn");
+        expect(wrapper.find('#selectCategoryLabel').text()).toMatch("Kategori");
+        expect(wrapper.find('#descriptionLabel').text()).toMatch("Beskrivelse");
+        expect(wrapper.find('#imageLabel').text()).toMatch("Bilde");
+
+    });
+
+    it("Tests setting values of input field", async() => {
+
+        const wrapper = shallowMount(CreateNewGroup);
+
+        const titleInput = wrapper.find('#title');
+        await titleInput.setValue("Fjellgata");
+        expect(titleInput.element.value).toBe("Fjellgata");
+
+        const selectedCategory = wrapper.find('#categories');
+        await selectedCategory.setValue("Borettslag");
+        expect(selectedCategory.element.value).toBe("Borettslag");
+
+        const descriptionInput = wrapper.find('#description');
+        await descriptionInput.setValue("Dette er et borettslag");
+        expect(descriptionInput.element.value).toBe("Dette er et borettslag");
+    });
+
+    it("Tests if radio box checks", async() => {
+
+        const wrapper = shallowMount(CreateNewGroup);
+
+        const radioInputOpen = wrapper.find('#flexRadioOpen');
+        await radioInputOpen.setChecked();
+        expect(radioInputOpen.element.checked).toBeTruthy();
+
+        const radioInputPrivate = wrapper.find('#flexRadioPrivate');
+        await radioInputPrivate.setChecked();
+        expect(radioInputPrivate.element.checked).toBeTruthy();
+    });
+});