diff --git a/cypress/downloads/downloads.htm b/cypress/downloads/downloads.htm
deleted file mode 100644
index 7df700b8dcd760fb4e36f087449ae1fb4162a59c..0000000000000000000000000000000000000000
Binary files a/cypress/downloads/downloads.htm and /dev/null differ
diff --git a/package-lock.json b/package-lock.json
index 2861813d1d2391c020a3df5974a5f8a63525c6c5..962f0ccbb36ff69aa08fc85c6ff4ba24d66f855f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -12,7 +12,8 @@
         "canvas-confetti": "^1.9.2",
         "pinia": "^2.1.7",
         "vue": "^3.4.21",
-        "vue-router": "^4.3.1"
+        "vue-router": "^4.3.1",
+        "vuedraggable": "^4.1.0"
       },
       "devDependencies": {
         "@rushstack/eslint-patch": "^1.8.0",
@@ -6157,6 +6158,11 @@
         "node": ">=8"
       }
     },
+    "node_modules/sortablejs": {
+      "version": "1.14.0",
+      "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.14.0.tgz",
+      "integrity": "sha512-pBXvQCs5/33fdN1/39pPL0NZF20LeRbLQ5jtnheIPN9JQAaufGjKdWduZn4U7wCtVuzKhmRkI0DFYHYRbB2H1w=="
+    },
     "node_modules/source-map-js": {
       "version": "1.2.0",
       "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz",
@@ -7246,6 +7252,17 @@
         "typescript": "*"
       }
     },
+    "node_modules/vuedraggable": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/vuedraggable/-/vuedraggable-4.1.0.tgz",
+      "integrity": "sha512-FU5HCWBmsf20GpP3eudURW3WdWTKIbEIQxh9/8GE806hydR9qZqRRxRE3RjqX7PkuLuMQG/A7n3cfj9rCEchww==",
+      "dependencies": {
+        "sortablejs": "1.14.0"
+      },
+      "peerDependencies": {
+        "vue": "^3.0.1"
+      }
+    },
     "node_modules/w3c-xmlserializer": {
       "version": "5.0.0",
       "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz",
diff --git a/package.json b/package.json
index 68388497c7d873b8f03e1128b07bb1c1c5a21ef9..be13c2a5d3e4a0451de2749309b751fd1a3e17b4 100644
--- a/package.json
+++ b/package.json
@@ -22,7 +22,8 @@
     "canvas-confetti": "^1.9.2",
     "pinia": "^2.1.7",
     "vue": "^3.4.21",
-    "vue-router": "^4.3.1"
+    "vue-router": "^4.3.1",
+    "vuedraggable": "^4.1.0"
   },
   "devDependencies": {
     "@rushstack/eslint-patch": "^1.8.0",
diff --git a/src/assets/base.css b/src/assets/base.css
index b70ce8c50bc1afbb76d12643070e61a8574ffeda..3474f444941f8de331422c1d5f2561ddd56854e7 100644
--- a/src/assets/base.css
+++ b/src/assets/base.css
@@ -6,6 +6,7 @@
     --grey: #cbcbcb;
     --light-grey: #f2f2f2;
     --green: #95e35d;
+    --red: #ef9691;
     --light-green: #b3f385;
 
     --bright: #f7da7c;
diff --git a/src/components/CardChallenge.vue b/src/components/CardChallenge.vue
new file mode 100644
index 0000000000000000000000000000000000000000..dfbf2de5414580d015471191ad17d1aae657504d
--- /dev/null
+++ b/src/components/CardChallenge.vue
@@ -0,0 +1,40 @@
+<script lang="ts" setup>
+import { computed, type PropType } from 'vue'
+import ProgressBar from '@/components/ProgressBar.vue'
+import router from '@/router'
+import type { Challenge } from '@/types/challenge'
+
+const props = defineProps({
+    challengeInstance: {
+        type: Object as PropType<Challenge>,
+        default: () => ({
+            id: 0,
+            title: 'Challenge title',
+            saved: 500,
+            target: 1000,
+            description: 'challenge Description',
+            due: '2021-12-31'
+        })
+    }
+})
+
+const challengeInstance = props.challengeInstance
+
+const editChallenge = () =>
+    router.push({ name: 'edit-challenge', params: { id: challengeInstance.id } })
+const displayDate = computed(() => challengeInstance.due?.slice(0, 16).split('T').join(' '))
+</script>
+
+<template>
+    <div
+        class="border-2 border-black rounded-xl p-4 flex flex-col items-center gap-2 cursor-pointer"
+        @click="editChallenge"
+    >
+        <h2 class="m-0">{{ challengeInstance.title }}</h2>
+        <p>{{ challengeInstance.saved }}kr / {{ challengeInstance.target }}kr</p>
+        <ProgressBar :completion="challengeInstance.completion" />
+        <p>{{ displayDate }}</p>
+    </div>
+</template>
+
+<style scoped></style>
diff --git a/src/components/CardGoal.vue b/src/components/CardGoal.vue
new file mode 100644
index 0000000000000000000000000000000000000000..09d1241b1d2a613733865f5fefee1fb1c0f8cc1d
--- /dev/null
+++ b/src/components/CardGoal.vue
@@ -0,0 +1,42 @@
+<script lang="ts" setup>
+import type { Goal } from '@/types/goal'
+import { computed, type PropType, reactive } from 'vue'
+import ProgressBar from '@/components/ProgressBar.vue'
+import router from '@/router'
+
+const props = defineProps({
+    goalInstance: {
+        type: Object as PropType<Goal>,
+        default: () => ({
+            id: 0,
+            title: 'Goal Title',
+            saved: 500,
+            target: 1000,
+            completion: 50,
+            description: 'Goal Description',
+            priority: 0,
+            createdOn: '2021-01-01',
+            due: '2021-12-31'
+        })
+    }
+})
+
+const goalInstance = reactive(props.goalInstance)
+
+const editGoal = () => router.push({ name: 'edit-goal', params: { id: goalInstance.id } })
+const displayDate = computed(() => goalInstance.due?.slice(0, 16).split('T').join(' '))
+</script>
+
+<template>
+    <div
+        class="border-2 border-black rounded-xl p-4 flex flex-col items-center gap-2 cursor-pointer"
+        @click="editGoal"
+    >
+        <h2 class="m-0">{{ goalInstance.title }}</h2>
+        <p>{{ goalInstance.saved }}kr / {{ goalInstance.target }}kr</p>
+        <ProgressBar :completion="goalInstance.completion" />
+        <p>{{ displayDate }}</p>
+    </div>
+</template>
+
+<style scoped></style>
diff --git a/src/components/ContinueButtonComponent.vue b/src/components/ContinueButtonComponent.vue
index 8905468de41b630dc9223c5256a1ab73402d1955..54a0825553b94e865ab72579d7e64c5400fed7fa 100644
--- a/src/components/ContinueButtonComponent.vue
+++ b/src/components/ContinueButtonComponent.vue
@@ -10,7 +10,7 @@
 </template>
 
 <script setup lang="ts">
-import { defineProps, defineEmits } from 'vue'
+import { defineEmits, defineProps } from 'vue'
 
 const props = defineProps({
     disabled: Boolean
diff --git a/src/components/NavBarComponent.vue b/src/components/NavBarComponent.vue
index b58aa12b56956f2fb2419265d48ce75bb0a37466..bef306e26ec0414256225e1eecc6b91d0673ea23 100644
--- a/src/components/NavBarComponent.vue
+++ b/src/components/NavBarComponent.vue
@@ -68,7 +68,7 @@
 
 <script setup lang="ts">
 import { RouterLink, RouterView, useRoute, useRouter } from 'vue-router'
-import { computed, ref, onMounted } from 'vue'
+import { computed, onMounted, ref } from 'vue'
 
 const route = useRoute()
 const router = useRouter()
diff --git a/src/components/PageControl.vue b/src/components/PageControl.vue
new file mode 100644
index 0000000000000000000000000000000000000000..91edf78e6385d200e3a62621fb02201e2b337888
--- /dev/null
+++ b/src/components/PageControl.vue
@@ -0,0 +1,30 @@
+<script lang="ts" setup>
+defineProps({
+    currentPage: {
+        type: Number,
+        default: 1
+    },
+    totalPages: {
+        type: Number,
+        default: 1
+    },
+    onPageChange: {
+        type: Function,
+        default: () => {}
+    }
+})
+</script>
+
+<template>
+    <div class="flex justify-center gap-4">
+        <button :disabled="currentPage === 1" @click="onPageChange(currentPage - 1)">
+            Previous
+        </button>
+        <p>{{ currentPage }} / {{ totalPages }}</p>
+        <button :disabled="currentPage === totalPages" @click="onPageChange(currentPage + 1)">
+            Next
+        </button>
+    </div>
+</template>
+
+<style scoped></style>
diff --git a/src/components/ProgressBar.vue b/src/components/ProgressBar.vue
new file mode 100644
index 0000000000000000000000000000000000000000..770c75b9bbd250fa7c55ae5d4b1cdbd11ea3312b
--- /dev/null
+++ b/src/components/ProgressBar.vue
@@ -0,0 +1,13 @@
+<script lang="ts" setup>
+defineProps({
+    completion: Number
+})
+</script>
+
+<template>
+    <div class="w-full bg-gray-200 rounded-full overflow-hidden">
+        <div :style="{ width: completion + '%' }" class="bg-green-500 h-2 rounded-full"></div>
+    </div>
+</template>
+
+<style scoped></style>
diff --git a/src/components/__tests__/ContinueButtonTest.spec.ts b/src/components/__tests__/ContinueButtonTest.spec.ts
index 9440d848118e74c4646c9dd43863800756f89cf4..cc9285c7abb1bfbd9fec3b5b308865068fec964c 100644
--- a/src/components/__tests__/ContinueButtonTest.spec.ts
+++ b/src/components/__tests__/ContinueButtonTest.spec.ts
@@ -1,4 +1,4 @@
-import { describe, it, expect } from 'vitest'
+import { describe, expect, it } from 'vitest'
 import { mount } from '@vue/test-utils'
 import ContinueButtonComponent from '@/components/ContinueButtonComponent.vue'
 
diff --git a/src/components/__tests__/ModalTest.spec.ts b/src/components/__tests__/ModalTest.spec.ts
index 80f93265e199899e02ff5dbb1e6824cfaa5112ae..146c89975aec3406a97d723e5619614ff38e2cb2 100644
--- a/src/components/__tests__/ModalTest.spec.ts
+++ b/src/components/__tests__/ModalTest.spec.ts
@@ -1,4 +1,4 @@
-import { describe, it, expect, beforeEach } from 'vitest'
+import { beforeEach, describe, expect, it } from 'vitest'
 import { shallowMount } from '@vue/test-utils'
 import ModalComponent from '@/components/ModalComponent.vue'
 
diff --git a/src/router/index.ts b/src/router/index.ts
index 9a6758c7dcc2a73e732cb6d0173a96ea5f09e4ac..93d137b48bbadd87371d503ce071839867dc827e 100644
--- a/src/router/index.ts
+++ b/src/router/index.ts
@@ -1,5 +1,4 @@
 import { createRouter, createWebHistory } from 'vue-router'
-import HomeView from '../views/HomeView.vue'
 
 const router = createRouter({
     history: createWebHistory(import.meta.env.BASE_URL),
@@ -12,7 +11,7 @@ const router = createRouter({
         {
             path: '/hjem',
             name: 'home',
-            component: HomeView
+            component: () => import('@/views/HomeView.vue')
         },
         {
             path: '/logginn',
@@ -37,17 +36,32 @@ const router = createRouter({
         {
             path: '/sparemaal',
             name: 'goals',
-            component: () => import('@/views/GoalView.vue')
+            component: () => import('@/views/UserGoalsView.vue')
+        },
+        {
+            path: '/sparemaal/ny',
+            name: 'new-goal',
+            component: () => import('@/views/ManageGoalView.vue')
+        },
+        {
+            path: '/sparemaal/:id',
+            name: 'edit-goal',
+            component: () => import('@/views/ManageGoalView.vue')
         },
         {
             path: '/spareutfordringer',
             name: 'challenges',
-            component: () => import('@/views/ChallengeView.vue')
+            component: () => import('@/views/UserChallengesView.vue')
         },
         {
-            path: '/:pathMatch(.*)*',
-            name: 'not-found',
-            component: () => import('@/views/NotFoundView.vue')
+            path: '/spareutfordringer/ny',
+            name: 'new-challenge',
+            component: () => import('@/views/ManageChallengeView.vue')
+        },
+        {
+            path: '/spareutfordringer/:id',
+            name: 'edit-challenge',
+            component: () => import('@/views/ManageChallengeView.vue')
         },
         {
             path: '/konfigurasjonSteg1',
@@ -78,6 +92,11 @@ const router = createRouter({
             path: '/forsteSpareutfordring',
             name: 'firstSavingChallengde',
             component: () => import('@/views/FirstSavingChallengeView.vue')
+        },
+        {
+            path: '/:pathMatch(.*)*',
+            name: 'not-found',
+            component: () => import('@/views/NotFoundView.vue')
         }
     ],
     scrollBehavior(to, from, savedPosition) {
diff --git a/src/services/authInterceptor.ts b/src/services/authInterceptor.ts
index 087f27d9bea3f2d2cd6b0dbe83e756f63204a386..f2fd74ff880df5b430d9dadca83cf38b973c3449 100644
--- a/src/services/authInterceptor.ts
+++ b/src/services/authInterceptor.ts
@@ -3,7 +3,10 @@ import axios, { AxiosError } from 'axios'
 import router from '@/router'
 
 const authInterceptor = axios.create({
-    baseURL: 'http://localhost:8080'
+    baseURL: 'http://localhost:8080',
+    headers: {
+        'Content-Type': 'application/json'
+    }
 })
 
 authInterceptor.interceptors.request.use(
@@ -29,7 +32,11 @@ authInterceptor.interceptors.response.use(
             originalRequest._retry = true
             const refreshToken = localStorage.getItem('refreshToken')
             axios
-                .post('/auth/renewToken', { refreshToken })
+                .post('/auth/renewToken', null, {
+                    headers: {
+                        Authorization: `Bearer ${refreshToken}`
+                    }
+                })
                 .then((response) => {
                     sessionStorage.setItem('accessToken', response.data.accessToken)
                     authInterceptor.defaults.headers['Authorization'] =
diff --git a/src/types/challenge.ts b/src/types/challenge.ts
index cfdc5c5d3bf7430a0182bb55dfdc7711f424365c..ef9acee9b95f6d9ba0b28103b6981461bb1d8b83 100644
--- a/src/types/challenge.ts
+++ b/src/types/challenge.ts
@@ -1,15 +1,15 @@
 // Assuming the use of classes from 'class-transformer' for date handling or plain TypeScript
 
 export interface Challenge {
-    id: number
+    id?: number
     title: string
+    perPurchase: number
     saved: number // BigDecimal in Java, but TypeScript uses number for floating points
     target: number
-    perPurchase: number
     description: string
-    createdOn: Date // Mapping ZonedDateTime to Date
-    dueDate?: Date // Mapping ZonedDateTime to Date, optional since Temporal annotation not always implies required
-    type?: string // Not specified as @NotNull, so it's optional
+    due: string // Mapping ZonedDateTime to Date, optional since Temporal annotation not always implies required
+    createdOn?: string // Mapping ZonedDateTime to Date
+    type: string // Not specified as @NotNull, so it's optional
     completion?: number // Assuming BigDecimal maps to number, optional due to @Transient
-    completedOn?: Date // Adding the new variable as optional
+    completedOn?: string // Adding the new variable as optional
 }
diff --git a/src/types/goal.ts b/src/types/goal.ts
index 910d9f4359473f9b92a32dd7098a686e2784e5d6..3ad1769cd1112d5b29c4769fce17997b28a05fa8 100644
--- a/src/types/goal.ts
+++ b/src/types/goal.ts
@@ -1,44 +1,12 @@
 export interface Goal {
-    /** The unique identifier for the Goal, must not be null. */
-    id: number
-
-    /**
-     * The title of the Goal, must not be null, empty, or only whitespace.
-     */
+    id: number | null | undefined
     title: string
-
-    /**
-     * The amount saved towards the Goal so far. Must not be null and must be zero or positive.
-     */
     saved: number
-
-    /**
-     * The target amount to achieve for the Goal. Must not be null and must be positive.
-     */
     target: number
-
-    /**
-     * Completion percentage of the Goal. Must not be null and must be zero or positive.
-     */
     completion: number
-
-    /**
-     * A description of the Goal, must not be null, empty, or only whitespace.
-     */
     description: string
-
-    /**
-     * The priority of the Goal, must not be null and must be zero or positive.
-     */
     priority: number
-
-    /**
-     * The date and time when the Goal was created. Must be a date in the past.
-     */
-    createdOn: Date
-
-    /**
-     * The date and time by which the Goal is due. Must be a date in the future.
-     */
-    due: Date
+    createdOn: string | null | undefined
+    due: string
+    completedOn: string | null | undefined
 }
diff --git a/src/views/ConfigSpendingItemsAmountView.vue b/src/views/ConfigSpendingItemsAmountView.vue
index 8146317360f08c970b08df5a793addea913dfb10..46d8a5b02c6ef5f7fb6858d47257badf050e68e2 100644
--- a/src/views/ConfigSpendingItemsAmountView.vue
+++ b/src/views/ConfigSpendingItemsAmountView.vue
@@ -62,7 +62,7 @@
 </template>
 
 <script setup lang="ts">
-import { computed, ref } from 'vue'
+import { ref } from 'vue'
 import ContinueButtonComponent from '@/components/ContinueButtonComponent.vue'
 import router from '@/router'
 
diff --git a/src/views/ConfigSpendingItemsTotalAmountView.vue b/src/views/ConfigSpendingItemsTotalAmountView.vue
index 0f8d51b5d4cb80e1253864260d588c050c82ae4f..e872697fbe7dc8b9739bd3c2d81ac4778668d42e 100644
--- a/src/views/ConfigSpendingItemsTotalAmountView.vue
+++ b/src/views/ConfigSpendingItemsTotalAmountView.vue
@@ -62,7 +62,7 @@
 </template>
 
 <script setup lang="ts">
-import { computed, ref } from 'vue'
+import { ref } from 'vue'
 import ContinueButtonComponent from '@/components/ContinueButtonComponent.vue'
 import router from '@/router'
 
diff --git a/src/views/GoalView.vue b/src/views/GoalView.vue
deleted file mode 100644
index 8539abf5c1c859e60bd8e9fe1c8abcc77426b875..0000000000000000000000000000000000000000
--- a/src/views/GoalView.vue
+++ /dev/null
@@ -1,7 +0,0 @@
-<script lang="ts" setup></script>
-
-<template>
-    <h1>Sparemål</h1>
-</template>
-
-<style scoped></style>
diff --git a/src/views/ManageChallengeView.vue b/src/views/ManageChallengeView.vue
new file mode 100644
index 0000000000000000000000000000000000000000..b6c0c7ab2da794dea04f2e27e6eb865efe42e9a4
--- /dev/null
+++ b/src/views/ManageChallengeView.vue
@@ -0,0 +1,274 @@
+<script lang="ts" setup>
+import { useRouter } from 'vue-router'
+import { computed, onMounted, ref, watch } from 'vue'
+import ProgressBar from '@/components/ProgressBar.vue'
+import authInterceptor from '@/services/authInterceptor'
+import type { Challenge } from '@/types/challenge'
+
+const router = useRouter()
+
+const oneWeekFromNow = new Date()
+oneWeekFromNow.setDate(oneWeekFromNow.getDate() + 7)
+const minDate = oneWeekFromNow.toISOString().slice(0, 16)
+
+const thirtyDaysFromNow = new Date()
+thirtyDaysFromNow.setDate(thirtyDaysFromNow.getDate() + 30)
+const maxDate = thirtyDaysFromNow.toISOString().slice(0, 16)
+
+const challengeInstance = ref<Challenge>({
+    title: '',
+    perPurchase: 20,
+    saved: 0,
+    target: 100,
+    description: '',
+    due: minDate + ':00.000Z',
+    type: ''
+})
+
+const isAmountSaved = ref(false)
+const timesSaved = ref(challengeInstance.value.saved / challengeInstance.value.perPurchase)
+
+watch(
+    () => timesSaved.value,
+    (newVal) => {
+        challengeInstance.value.saved = newVal * challengeInstance.value.perPurchase
+        challengeInstance.value.saved = parseFloat(challengeInstance.value.saved.toFixed(2))
+    }
+)
+
+watch(
+    () => challengeInstance.value.saved,
+    (newVal) => {
+        challengeInstance.value.saved = Math.max(
+            0,
+            Math.min(challengeInstance.value.target, newVal)
+        )
+        challengeInstance.value.saved = parseFloat(challengeInstance.value.saved.toFixed(2))
+        timesSaved.value = challengeInstance.value.saved / challengeInstance.value.perPurchase
+        timesSaved.value = parseFloat(timesSaved.value.toFixed(2))
+    }
+)
+
+watch(
+    () => challengeInstance.value.perPurchase,
+    (newVal) => {
+        challengeInstance.value.perPurchase = Math.max(
+            1,
+            Math.min(challengeInstance.value.target, newVal)
+        )
+        challengeInstance.value.perPurchase = parseFloat(
+            challengeInstance.value.perPurchase.toFixed(2)
+        )
+        timesSaved.value = challengeInstance.value.saved / challengeInstance.value.perPurchase
+        timesSaved.value = parseFloat(timesSaved.value.toFixed(2))
+    }
+)
+
+watch(
+    () => challengeInstance.value.target,
+    (newVal) => {
+        challengeInstance.value.target = Math.max(
+            Math.max(challengeInstance.value.saved, 1),
+            newVal
+        )
+    }
+)
+
+const selectedDate = ref(minDate)
+watch(
+    () => selectedDate.value,
+    (newVal) => {
+        if (newVal) {
+            selectedDate.value = newVal < minDate ? minDate : newVal
+            challengeInstance.value.due = selectedDate.value + ':00.000Z'
+        }
+    }
+)
+
+const isEdit = computed(() => router.currentRoute.value.name === 'edit-challenge')
+const pageTitle = computed(() => (isEdit.value ? 'Rediger utfordring' : 'Ny utfordring'))
+const submitButton = computed(() => (isEdit.value ? 'Oppdater' : 'Opprett'))
+const completion = computed(
+    () => (challengeInstance.value.saved / challengeInstance.value.target) * 100
+)
+
+const isInputValid = computed(() => {
+    return (
+        challengeInstance.value.title !== '' &&
+        challengeInstance.value.target > 0 &&
+        challengeInstance.value.due !== ''
+    )
+})
+
+const submitAction = () => {
+    if (!isInputValid.value) {
+        return () => alert('Fyll ut alle feltene')
+    }
+
+    if (isEdit.value) {
+        updateChallenge()
+    } else {
+        createChallenge()
+    }
+}
+
+onMounted(async () => {
+    if (isEdit.value) {
+        const challengeId = router.currentRoute.value.params.id
+        if (!challengeId) return router.push({ name: 'challenges' })
+
+        await authInterceptor(`/users/me/challenges/${challengeId}`)
+            .then((response) => {
+                challengeInstance.value = response.data
+                selectedDate.value = response.data.due.slice(0, 16)
+            })
+            .catch((error) => {
+                console.error(error)
+                router.push({ name: 'challenges' })
+            })
+    }
+})
+
+const createChallenge = () => {
+    authInterceptor
+        .post('/users/me/challenges', challengeInstance.value, {})
+        .then(() => {
+            return router.push({ name: 'challenges' })
+        })
+        .catch((error) => {
+            console.error(error)
+        })
+}
+
+const updateChallenge = () => {
+    authInterceptor
+        .put(`/users/me/challenges/${challengeInstance.value.id}`, challengeInstance.value)
+        .then(() => {
+            router.push({ name: 'challenges' })
+        })
+        .catch((error) => {
+            console.error(error)
+        })
+}
+
+const deleteChallenge = () => {
+    authInterceptor
+        .delete(`/users/me/challenges/${challengeInstance.value.id}`)
+        .then(() => {
+            router.push({ name: 'challenges' })
+        })
+        .catch((error) => {
+            console.error(error)
+        })
+}
+</script>
+
+<template>
+    <div class="flex flex-col justify-center items-center">
+        <h1 class="font-bold" v-text="pageTitle" />
+        <div class="flex flex-col gap-5 items-center justify-center">
+            <div class="flex flex-col">
+                <p class="mx-4">Tittel*</p>
+                <input
+                    v-model="challengeInstance.title"
+                    placeholder="Skriv en tittel"
+                    type="text"
+                />
+            </div>
+
+            <div class="flex flex-col">
+                <p class="mx-4">Type</p>
+                <input v-model="challengeInstance.type" placeholder="Skriv en type" type="text" />
+            </div>
+
+            <div class="flex flex-col">
+                <p class="mx-4">Beskrivelse</p>
+                <textarea
+                    v-model="challengeInstance.description"
+                    class="w-80 h-20 no-rezise"
+                    placeholder="Beskriv sparemålet"
+                />
+            </div>
+
+            <div class="flex flex-col sm:flex-row gap-3">
+                <div class="flex flex-col">
+                    <p class="mx-4">Pris per sparing</p>
+                    <input
+                        v-model="challengeInstance.perPurchase"
+                        class="w-40 text-right"
+                        placeholder="Kr spart per sparing"
+                        type="number"
+                    />
+                </div>
+
+                <div class="flex flex-col">
+                    <div class="flex flex-row justify-between mx-4">
+                        <p>{{ isAmountSaved ? 'Kroner spart' : 'Antall sparinger' }}</p>
+                        <button class="p-0 bg-transparent" @click="isAmountSaved = !isAmountSaved">
+                            🔄️
+                        </button>
+                    </div>
+                    <input
+                        v-if="isAmountSaved"
+                        v-model="challengeInstance.saved"
+                        class="w-40 text-right"
+                        min="0"
+                        placeholder="Sparebeløp"
+                        type="number"
+                    />
+                    <input
+                        v-else
+                        v-model="timesSaved"
+                        class="w-40 text-right"
+                        placeholder="Kr spart per sparing"
+                        type="number"
+                    />
+                </div>
+
+                <div class="flex flex-col">
+                    <p class="mx-4">Av målbeløp...*</p>
+                    <input
+                        v-model="challengeInstance.target"
+                        class="w-40 text-right"
+                        placeholder="Målbeløp"
+                        type="number"
+                    />
+                </div>
+            </div>
+            <ProgressBar :completion="completion" />
+
+            <div class="flex flex-col">
+                <p class="mx-4">Forfallsdato*</p>
+                <input
+                    v-model="selectedDate"
+                    :max="maxDate"
+                    :min="minDate"
+                    placeholder="Forfallsdato"
+                    type="datetime-local"
+                />
+            </div>
+
+            <div class="flex flex-row justify-between w-full">
+                <button :disabled="!isInputValid" @click="submitAction" v-text="submitButton" />
+                <button
+                    v-if="isEdit"
+                    class="ml-2 bg-button-danger"
+                    @click="deleteChallenge"
+                    v-text="'Slett'"
+                />
+                <button
+                    v-else
+                    class="ml-2 bg-button-other"
+                    @click="router.push({ name: 'challenges' })"
+                    v-text="'Avbryt'"
+                />
+            </div>
+        </div>
+    </div>
+</template>
+
+<style scoped>
+.no-rezise {
+    resize: none;
+}
+</style>
diff --git a/src/views/ManageGoalView.vue b/src/views/ManageGoalView.vue
new file mode 100644
index 0000000000000000000000000000000000000000..360cf137ae3a52657f0d319f5c1b5c88168a56b1
--- /dev/null
+++ b/src/views/ManageGoalView.vue
@@ -0,0 +1,213 @@
+<script lang="ts" setup>
+import { useRouter } from 'vue-router'
+import { computed, onMounted, ref, watch } from 'vue'
+import type { Goal } from '@/types/goal'
+import ProgressBar from '@/components/ProgressBar.vue'
+import authInterceptor from '@/services/authInterceptor'
+
+const router = useRouter()
+
+const oneWeekFromNow = new Date()
+oneWeekFromNow.setDate(oneWeekFromNow.getDate() + 7)
+const minDate = oneWeekFromNow.toISOString().slice(0, 16)
+
+const thirtyDaysFromNow = new Date()
+thirtyDaysFromNow.setDate(thirtyDaysFromNow.getDate() + 30)
+const maxDate = thirtyDaysFromNow.toISOString().slice(0, 16)
+
+const goalInstance = ref<Goal>({
+    id: 0,
+    title: '',
+    saved: 50,
+    target: 100,
+    completion: 0,
+    description: '',
+    priority: 0,
+    createdOn: undefined,
+    due: minDate + ':00.000Z',
+    completedOn: null
+})
+
+watch(
+    () => goalInstance.value.saved,
+    (newVal) => {
+        goalInstance.value.saved = Math.max(0, Math.min(goalInstance.value.target, newVal))
+    }
+)
+
+watch(
+    () => goalInstance.value.target,
+    (newVal) => {
+        goalInstance.value.target = Math.max(Math.max(goalInstance.value.saved, 1), newVal)
+    }
+)
+
+const selectedDate = ref(minDate)
+watch(
+    () => selectedDate.value,
+    (newVal) => {
+        if (newVal) {
+            selectedDate.value = newVal < minDate ? minDate : newVal
+            goalInstance.value.due = selectedDate.value + ':00.000Z'
+        }
+        console.log(selectedDate.value)
+    }
+)
+
+const isEdit = computed(() => router.currentRoute.value.name === 'edit-goal')
+const pageTitle = computed(() => (isEdit.value ? 'Rediger sparemål' : 'Nytt sparemål'))
+const submitButton = computed(() => (isEdit.value ? 'Oppdater' : 'Opprett'))
+const completion = computed(() => (goalInstance.value.saved / goalInstance.value.target) * 100)
+
+const isInputValid = computed(() => {
+    return (
+        goalInstance.value.title !== '' &&
+        goalInstance.value.target > 0 &&
+        goalInstance.value.due !== ''
+    )
+})
+
+const submitAction = () => {
+    if (
+        goalInstance.value.title === '' ||
+        goalInstance.value.target < 1 ||
+        goalInstance.value.due === ''
+    ) {
+        return () => alert('Fyll ut alle feltene')
+    }
+
+    if (isEdit.value) {
+        updateGoal()
+    } else {
+        createGoal()
+    }
+}
+
+onMounted(async () => {
+    if (isEdit.value) {
+        const goalId = router.currentRoute.value.params.id
+        if (!goalId) return router.push({ name: 'goals' })
+
+        await authInterceptor(`/users/me/goals/${goalId}`)
+            .then((response) => {
+                goalInstance.value = response.data
+                selectedDate.value = response.data.due.slice(0, 16)
+            })
+            .catch((error) => {
+                console.error(error)
+                router.push({ name: 'goals' })
+            })
+    }
+})
+
+const createGoal = () => {
+    authInterceptor
+        .post('/users/me/goals', goalInstance.value, {})
+        .then(() => {
+            return router.push({ name: 'goals' })
+        })
+        .catch((error) => {
+            console.error(error)
+        })
+}
+
+const updateGoal = () => {
+    authInterceptor
+        .put(`/users/me/goals/${goalInstance.value.id}`, goalInstance.value)
+        .then(() => {
+            router.push({ name: 'goals' })
+        })
+        .catch((error) => {
+            console.error(error)
+        })
+}
+
+const deleteGoal = () => {
+    authInterceptor
+        .delete(`/users/me/goals/${goalInstance.value.id}`)
+        .then(() => {
+            router.push({ name: 'goals' })
+        })
+        .catch((error) => {
+            console.error(error)
+        })
+}
+</script>
+
+<template>
+    <div class="flex flex-col justify-center items-center">
+        <h1 class="font-bold" v-text="pageTitle" />
+        <div class="flex flex-col gap-5 items-center justify-center">
+            <div class="flex flex-col">
+                <p class="mx-4">Tittel*</p>
+                <input v-model="goalInstance.title" placeholder="Skriv en tittel" type="text" />
+            </div>
+
+            <div class="flex flex-col">
+                <p class="mx-4">Beskrivelse</p>
+                <textarea
+                    v-model="goalInstance.description"
+                    class="w-80 h-20 no-rezise"
+                    placeholder="Beskriv sparemålet"
+                />
+            </div>
+
+            <div class="flex flex-col sm:flex-row gap-3">
+                <div class="flex flex-col">
+                    <p class="mx-4">Kroner spart...</p>
+                    <input
+                        v-model="goalInstance.saved"
+                        class="w-40 text-right"
+                        min="0"
+                        placeholder="Sparebeløp"
+                        type="number"
+                    />
+                </div>
+
+                <div class="flex flex-col">
+                    <p class="mx-4">Av målbeløp...*</p>
+                    <input
+                        v-model="goalInstance.target"
+                        class="w-40 text-right"
+                        placeholder="Målbeløp"
+                        type="number"
+                    />
+                </div>
+            </div>
+            <ProgressBar :completion="completion" />
+
+            <div class="flex flex-col">
+                <p class="mx-4">Forfallsdato*</p>
+                <input
+                    v-model="selectedDate"
+                    :max="maxDate"
+                    :min="minDate"
+                    placeholder="Forfallsdato"
+                    type="datetime-local"
+                />
+            </div>
+
+            <div class="flex flex-row justify-between w-full">
+                <button :disabled="!isInputValid" @click="submitAction" v-text="submitButton" />
+                <button
+                    v-if="isEdit"
+                    class="ml-2 bg-button-danger"
+                    @click="deleteGoal"
+                    v-text="'Slett'"
+                />
+                <button
+                    v-else
+                    class="ml-2 bg-button-other"
+                    @click="router.push({ name: 'goals' })"
+                    v-text="'Avbryt'"
+                />
+            </div>
+        </div>
+    </div>
+</template>
+
+<style scoped>
+.no-rezise {
+    resize: none;
+}
+</style>
diff --git a/src/views/UserChallengesView.vue b/src/views/UserChallengesView.vue
new file mode 100644
index 0000000000000000000000000000000000000000..ffd2a3aac3a8d5e579653a4a3abf49847d271100
--- /dev/null
+++ b/src/views/UserChallengesView.vue
@@ -0,0 +1,56 @@
+<script lang="ts" setup>
+import { useRouter } from 'vue-router'
+import { onMounted, ref, watch } from 'vue'
+import authInterceptor from '@/services/authInterceptor'
+import draggable from 'vuedraggable'
+import type { Challenge } from '@/types/challenge'
+import CardChallenge from '@/components/CardChallenge.vue'
+
+const router = useRouter()
+
+const currentPage = ref(1)
+const totalPages = ref(1)
+
+const challenges = ref<Challenge[]>([])
+
+onMounted(async () => {
+    await authInterceptor('/users/me/challenges')
+        .then((response) => {
+            currentPage.value = response.data.currentPage
+            totalPages.value = response.data.totalPages
+            challenges.value = response.data.content
+        })
+        .catch((error) => {
+            console.error(error)
+        })
+})
+
+watch(challenges, (newChallenges) => {
+    console.log(newChallenges)
+})
+</script>
+
+<template>
+    <h1 class="font-bold text-center">Dine utfordringer</h1>
+    <div class="flex flex-col gap-5 items-center">
+        <draggable
+            v-model="challenges"
+            class="flex flex-col justify-center gap-10 sm:flex-row"
+            item-key="id"
+        >
+            <template #item="{ element, index }">
+                <CardChallenge :key="index" :challenge-instance="element" />
+            </template>
+        </draggable>
+        <div class="flex flex-row gap-5">
+            <button @click="router.push({ name: 'new-challenge' })">
+                Opprett en ny utfordring
+            </button>
+            <button @click="router.push({ name: 'edit-challenge', params: { id: 1 } })">
+                Rediger rekkefølge
+            </button>
+        </div>
+    </div>
+</template>
+
+<style scoped></style>
diff --git a/src/views/UserGoalsView.vue b/src/views/UserGoalsView.vue
new file mode 100644
index 0000000000000000000000000000000000000000..84f46d9c8d82f15055e0e121454df04941727b69
--- /dev/null
+++ b/src/views/UserGoalsView.vue
@@ -0,0 +1,55 @@
+<script lang="ts" setup>
+import CardGoal from '@/components/CardGoal.vue'
+
+import { useRouter } from 'vue-router'
+import { onMounted, ref, watch } from 'vue'
+import authInterceptor from '@/services/authInterceptor'
+import type { Goal } from '@/types/goal'
+import draggable from 'vuedraggable'
+
+const router = useRouter()
+
+const currentPage = ref(1)
+const totalPages = ref(1)
+
+const goals = ref<Goal[]>([])
+
+onMounted(async () => {
+    await authInterceptor('/users/me/goals')
+        .then((response) => {
+            currentPage.value = response.data.currentPage
+            totalPages.value = response.data.totalPages
+            goals.value = response.data.content
+        })
+        .catch((error) => {
+            console.error(error)
+        })
+})
+
+watch(goals, (newGoals) => {
+    console.log(newGoals)
+})
+</script>
+
+<template>
+    <h1 class="font-bold text-center">Dine sparemål</h1>
+    <div class="flex flex-col gap-5 items-center">
+        <draggable
+            v-model="goals"
+            class="flex flex-col justify-center gap-10 sm:flex-row"
+            item-key="id"
+        >
+            <template #item="{ element, index }">
+                <CardGoal :key="index" :goal-instance="element" />
+            </template>
+        </draggable>
+        <div class="flex flex-row gap-5">
+            <button @click="router.push({ name: 'new-goal' })">Opprett et nytt sparemål</button>
+            <button @click="router.push({ name: 'edit-goal', params: { id: 1 } })">
+                Rediger rekkefølge
+            </button>
+        </div>
+    </div>
+</template>
+
+<style scoped></style>
diff --git a/tailwind.config.js b/tailwind.config.js
index abe363ca68d682e6127eb145197317c59e9df11d..85dfc48accae773f41c89b06e72c48669c4df697 100644
--- a/tailwind.config.js
+++ b/tailwind.config.js
@@ -1,32 +1,34 @@
 /** @type {import('tailwindcss').Config} */
 export default {
-  content: [
-    "./index.html",
-    "./src/**/*.{vue,js,ts,jsx,tsx}",
-  ],
-  theme: {
-    extend: {
-      animation: {
-        clouds: 'clouds 20s linear infinite',
-        beach: 'beach 5s linear infinite',
-        flame: 'flame 0.3s linear infinite',
-      },
-      keyframes: {
-        clouds: {
-          '0%': { backgroundPosition: '0%' },
-          '100%': { backgroundPosition: '-100%' },
-        },
-        beach: {
-          '0%': { backgroundPosition: '0%' },
-          '100%': { backgroundPosition: '-100%' },
-        },
-        
-        flame: {
-          '0%, 100%': { transform: 'translateX(0%)' },
-          '50%': { transform: 'translateX(50%)' },
-        },
-      }
+    content: ['./index.html', './src/**/*.{vue,js,ts,jsx,tsx}'],
+    theme: {
+        extend: {
+            animation: {
+                clouds: 'clouds 20s linear infinite',
+                beach: 'beach 5s linear infinite',
+                flame: 'flame 0.3s linear infinite'
+            },
+            keyframes: {
+                clouds: {
+                    '0%': { backgroundPosition: '0%' },
+                    '100%': { backgroundPosition: '-100%' }
+                },
+                beach: {
+                    '0%': { backgroundPosition: '0%' },
+                    '100%': { backgroundPosition: '-100%' }
+                },
+
+                flame: {
+                    '0%, 100%': { transform: 'translateX(0%)' },
+                    '50%': { transform: 'translateX(50%)' }
+                }
+            },
+            colors: {
+                'button-disabled': 'var(--grey)',
+                'button-danger': 'var(--red)',
+                'button-other': 'var(--accent1)'
+            }
+        }
     },
-  },
-  plugins: [],
+    plugins: []
 }
\ No newline at end of file