diff --git a/src/App.vue b/src/App.vue index be78194d361500e3ee20a20eb0d46747b92c4a2a..6cd1e8f97ea32c80831eb29c2ff51bc86c2722ac 100644 --- a/src/App.vue +++ b/src/App.vue @@ -10,7 +10,8 @@ const showNavBar = computed(() => { route.path == '/' || route.path == '/registrer' || route.path == '/logginn' || - route.path == '/forgotPassword' + route.path == '/forgotPassword' || + route.path.startsWith('/konfigurasjon') ) }) </script> diff --git a/src/components/ModalEditAvatar.vue b/src/components/ModalEditAvatar.vue new file mode 100644 index 0000000000000000000000000000000000000000..5474b34e6b346b1206405db3681641638b9f74ff --- /dev/null +++ b/src/components/ModalEditAvatar.vue @@ -0,0 +1,65 @@ +<template> + <button @click="openModal" class="text-nowrap">Endre avatar</button> + <div + v-if="isModalOpen" + class="fixed inset-0 bg-black bg-opacity-50 flex justify-center items-center z-50" + > + <div class="bg-white p-6 rounded-lg shadow-lg max-w-[80vh] h-auto w-full text-center"> + <h2 class="title">Endre avatar</h2> + <div class="avatar-container flex flex-row justify-between items-center my-8"> + <button @click="cycleArray('prev')">◀</button> + <div class="flex flex-row items-center justify-around"> + <img :src="previousAvatar" alt="avatar" class="avatar h-16 w-16" /> + <div class="border-4 rounded-full border-green-600 p-8 mx-4"> + <img :src="currentAvatar" alt="avatar" class="avatar h-40 w-40" /> + </div> + <img :src="nextAvatar" alt="avatar" class="avatar h-16 w-16" /> + </div> + <button @click="cycleArray('next')">▶</button> + </div> + <button @click="saveAvatar" class="save-button">Lagre</button> + </div> + </div> +</template> + +<script setup lang="ts"> +import { ref } from 'vue' + +const isModalOpen = ref(false) +const avatars = [ + 'src/assets/coffee.png', + 'src/assets/head.png', + 'src/assets/nose.png', + 'src/assets/penger.png', + 'src/assets/pig.png' +] +let currentAvatarIndex = 0 + +const openModal = () => { + isModalOpen.value = !isModalOpen.value +} + +const nextAvatar = ref(avatars[(currentAvatarIndex + 1) % avatars.length]) +const currentAvatar = ref(avatars[currentAvatarIndex]) +const previousAvatar = ref(avatars[(currentAvatarIndex - 1 + avatars.length) % avatars.length]) + +const cycleArray = (direction: string) => { + if (direction === 'prev') { + currentAvatarIndex = (currentAvatarIndex - 1 + avatars.length) % avatars.length + console.log(currentAvatarIndex) + currentAvatar.value = avatars[currentAvatarIndex] + previousAvatar.value = avatars[(currentAvatarIndex - 1 + avatars.length) % avatars.length] + nextAvatar.value = avatars[(currentAvatarIndex + 1) % avatars.length] + } else { + currentAvatarIndex = (currentAvatarIndex + 1) % avatars.length + currentAvatar.value = avatars[currentAvatarIndex] + previousAvatar.value = avatars[(currentAvatarIndex - 1 + avatars.length) % avatars.length] + nextAvatar.value = avatars[(currentAvatarIndex + 1) % avatars.length] + } +} + +const saveAvatar = () => { + localStorage.setItem('avatar', currentAvatar.value) + isModalOpen.value = false +} +</script> diff --git a/src/router/index.ts b/src/router/index.ts index 4ae0e9a4990bd24573cd01d10768f46825ef4c14..c80ad8b511e430bf1f1a6bec2b83e82e929f4641 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -98,6 +98,11 @@ const router = createRouter({ name: 'configurations5', component: () => import('@/views/ConfigSpendingItemsTotalAmountView.vue') }, + { + path: '/konfigurasjonSteg6', + name: 'configurations6', + component: () => import('@/views/ConfigAccountNumberView.vue') + }, { path: '/forsteSparemaal', name: 'firstSavingGoal', diff --git a/src/stores/accountStore.ts b/src/stores/accountStore.ts new file mode 100644 index 0000000000000000000000000000000000000000..d0d7715190bedd53d676fa1193145768037f2774 --- /dev/null +++ b/src/stores/accountStore.ts @@ -0,0 +1,35 @@ +import { defineStore } from 'pinia' +import { ref } from 'vue' +import authInterceptor from '@/services/authInterceptor' +import axios, { AxiosError } from 'axios' + +export const useAccountStore = defineStore('account', { + state: () => ({ + errorMessage: ref<string>('') + }), + actions: { + async postAccount(accountType: 'SAVING' | 'SPENDING', accNumber: string, balance: number) { + const payload = { + accountType, + accNumber, + balance + } + + try { + const response = await authInterceptor.post('/accounts', payload) + console.log('Success:', response.data) + } catch (error) { + console.error('Error posting account:', error) + this.handleAxiosError(error) + } + }, + handleAxiosError(error: any) { + const axiosError = error as AxiosError + if (axiosError.response && axiosError.response.data) { + const errorData = axiosError.response.data as { message: string } + } else { + this.errorMessage = 'An unexpected error occurred' + } + } + } +}) diff --git a/src/stores/userConfigStore.ts b/src/stores/userConfigStore.ts index c2fa6032c4fdfaf6f3e130d465d9cb33dde3c256..3b5661531c91b403d1fb631dc6959fa513065285 100644 --- a/src/stores/userConfigStore.ts +++ b/src/stores/userConfigStore.ts @@ -1,6 +1,7 @@ import { defineStore } from 'pinia' +import { ref } from 'vue' import authInterceptor from '@/services/authInterceptor' -import axios from 'axios' +import axios, { AxiosError } from 'axios' export const useUserConfigStore = defineStore('userConfig', { state: () => ({ @@ -11,7 +12,8 @@ export const useUserConfigStore = defineStore('userConfig', { type: string specificAmount: number generalAmount: number - }[] + }[], + errorMessage: ref<string>('') }), actions: { setExperience(value: string) { @@ -23,23 +25,28 @@ export const useUserConfigStore = defineStore('userConfig', { addChallengeTypeConfig(type: string, specificAmount: number, generalAmount: number) { this.challengeTypeConfigs.push({ type, specificAmount, generalAmount }) }, - async postUserConfig() { + postUserConfig() { const payload = { experience: this.experience, motivation: this.motivation, challengeTypeConfigs: Array.from(this.challengeTypeConfigs) } - try { - const response = await authInterceptor.post('/config/challenge', payload) - console.log('Success:', response.data) - } catch (error: unknown) { - if (axios.isAxiosError(error)) { - console.error('Axios error:', error.response?.data || error.message) - } else { - console.error('An unexpected error occurred:', error) - } - } + authInterceptor + .post('/config/challenge', payload) + .then((response) => { + console.log('Success:', response.data) + }) + .catch((error) => { + const axiosError = error as AxiosError + if (axiosError.response && axiosError.response.data) { + const errorData = axiosError.response.data as { message: string } + this.errorMessage = errorData.message || 'An error occurred' + } else { + this.errorMessage = 'An unexpected error occurred' + } + console.error('Axios error:', this.errorMessage) + }) } } }) diff --git a/src/views/ConfigAccountNumberView.vue b/src/views/ConfigAccountNumberView.vue new file mode 100644 index 0000000000000000000000000000000000000000..ef650a7178ad5b1ec25644fbe3193b9822e83e91 --- /dev/null +++ b/src/views/ConfigAccountNumberView.vue @@ -0,0 +1,114 @@ +<template> + <div + class="flex flex-col items-center justify-center min-h-screen md:pt-10 pt-4 pb-24 text-center" + > + <h1 class="mb-8 lg:mb-12 text-4xl font-bold"> + Legg til kontonummer for sparekonto og brukskonto + </h1> + <div + class="flex flex-col items-center justify-center bg-white rounded-lg p-8 shadow-lg w-full md:w-[45%]" + > + <div class="w-full mb-4"> + <label for="savingsAccount" class="block text-lg font-bold mb-2">Sparekonto</label> + <input + id="savingsAccount" + v-model="savingsAccount" + @input="restrictToNumbers($event as InputEvent, 'savings')" + @focus="removeFormatting('savings')" + @blur="applyFormatting('savings')" + class="w-full h-11 px-3 rounded-md text-xl focus:outline-none transition-colors border-2 border-gray-300" + type="text" + placeholder="Skriv inn ditt kontonummer..." + /> + </div> + <div class="w-full mb-4"> + <label for="spendingAccount" class="block text-lg font-bold mb-2">Brukskonto</label> + <input + id="spendingAccount" + v-model="spendingAccount" + @input="restrictToNumbers($event as InputEvent, 'spending')" + @focus="removeFormatting('spending')" + @blur="applyFormatting('spending')" + class="w-full h-11 px-3 rounded-md text-xl focus:outline-none transition-colors border-2 border-gray-300" + type="text" + placeholder="Skriv inn ditt kontonummer..." + /> + </div> + </div> + <div class="absolute bottom-36 right-2"> + <ContinueButtonComponent + @click="onButtonClick" + :disabled="!isFormValid" + class="px-10 py-3 text-2xl font-bold mb-4 mr-2" + ></ContinueButtonComponent> + </div> + </div> +</template> + +<script setup lang="ts"> +import { ref, computed } from 'vue' +import { useAccountStore } from '@/stores/accountStore' +import ContinueButtonComponent from '@/components/ContinueButtonComponent.vue' +import router from '@/router' + +const MAX_DIGITS = 11 +const accountStore = useAccountStore() + +const spendingAccount = ref('') +const savingsAccount = ref('') + +const isFormValid = computed(() => { + return ( + spendingAccount.value.replace(/\./g, '').length === MAX_DIGITS && + savingsAccount.value.replace(/\./g, '').length === MAX_DIGITS + ) +}) + +async function onButtonClick() { + const savingAccountNumber = savingsAccount.value.replace(/\./g, '') + const spendingAccountNumber = spendingAccount.value.replace(/\./g, '') + + await accountStore.postAccount('SAVING', savingAccountNumber, 0) + + await accountStore.postAccount('SPENDING', spendingAccountNumber, 0) + + await router.push({ name: 'home' }) +} + +function restrictToNumbers(event: InputEvent, type: string) { + const inputValue = (event.target as HTMLInputElement)?.value + if (inputValue !== undefined) { + const sanitizedValue = inputValue.replace(/\D/g, '') + const truncatedValue = sanitizedValue.slice(0, MAX_DIGITS) + if (type === 'spending') { + spendingAccount.value = truncatedValue + } else { + savingsAccount.value = truncatedValue + } + } +} + +function applyFormatting(type: string) { + if (type === 'spending') { + spendingAccount.value = formatAccount(spendingAccount.value) + } else { + savingsAccount.value = formatAccount(savingsAccount.value) + } +} + +function removeFormatting(type: string) { + if (type === 'spending') { + spendingAccount.value = removeFormat(spendingAccount.value) + } else { + savingsAccount.value = removeFormat(savingsAccount.value) + } +} + +function formatAccount(value: string): string { + return value.replace(/\D/g, '').replace(/^(.{4})(.{2})(.*)$/, '$1.$2.$3') +} + +function removeFormat(value: string): string { + return value.replace(/\./g, '') +} +</script> diff --git a/src/views/ConfigFamiliarWithSavingsView.vue b/src/views/ConfigFamiliarWithSavingsView.vue index 3b3c8a4d1c6b6143ce1100e1e850a97a82e27988..ecbd66aa41acbc1aa29f2fd9405ec17cb0c00213 100644 --- a/src/views/ConfigFamiliarWithSavingsView.vue +++ b/src/views/ConfigFamiliarWithSavingsView.vue @@ -1,7 +1,9 @@ <template> <div class="flex flex-col items-center justify-center min-h-screen px-4 text-center"> - <h1 class="mb-16 text-4xl font-bold lg:mb-20">Hvor kjent er du med sparing fra før?</h1> - <div class="grid grid-cols-1 gap-14 mb-20 md:grid-cols-3"> + <h1 class="mb-8 text-2xl font-bold sm:mb-16 sm:text-4xl"> + Hvor kjent er du med sparing fra før? + </h1> + <div class="grid grid-cols-1 gap-8 mb-16 sm:gap-14 sm:mb-20 md:grid-cols-3"> <div :class="{ 'border-[var(--green)] border-4': selectedOption === 'litt', @@ -39,7 +41,7 @@ <ContinueButtonComponent :disabled="selectedOption === null" @click="onButtonClick" - class="px-10 py-3 text-2xl self-end mb-4 mt-0" + class="px-10 py-3 text-2xl self-end" ></ContinueButtonComponent> </div> </template> @@ -67,8 +69,6 @@ const selectOption = (option: string) => { case 'godt': experienceValue = 'VERY_HIGH' break - default: - experienceValue = 'VERY_LOW' } userConfigStore.setExperience(experienceValue) @@ -76,7 +76,7 @@ const selectOption = (option: string) => { const onButtonClick = () => { if (selectedOption.value) { - router.push('/konfigurasjonSteg3') + router.push({ name: 'configurations3' }) } else { console.error('No option selected') } diff --git a/src/views/ConfigHabitChangeView.vue b/src/views/ConfigHabitChangeView.vue index c686e26112e56a2d298e5e80d5cb59f0b8736f83..5ee730d582b413003907a7061f11affe2792a103 100644 --- a/src/views/ConfigHabitChangeView.vue +++ b/src/views/ConfigHabitChangeView.vue @@ -47,7 +47,7 @@ </template> <script setup lang="ts"> -import { ref } from 'vue' +import { onMounted, ref } from 'vue' import ContinueButtonComponent from '@/components/ContinueButtonComponent.vue' import router from '@/router' import { useUserConfigStore } from '@/stores/userConfigStore' @@ -69,8 +69,6 @@ const selectOption = (option: string) => { case 'store': motivationValue = 'VERY_HIGH' break - default: - motivationValue = 'VERY_LOW' } userConfigStore.setMotivation(motivationValue) @@ -78,7 +76,7 @@ const selectOption = (option: string) => { const onButtonClick = () => { if (selectedOption.value) { - router.push('/konfigurasjonSteg2') + router.push({ name: 'configurations2' }) } else { console.error('No option selected') } diff --git a/src/views/ConfigSpendingItemsAmountView.vue b/src/views/ConfigSpendingItemsAmountView.vue index 625bf5335a0e50a61c1db4cb2a388d954430a73e..aa77feb1371de00b5a70dfe7815dd86e77f939c5 100644 --- a/src/views/ConfigSpendingItemsAmountView.vue +++ b/src/views/ConfigSpendingItemsAmountView.vue @@ -1,59 +1,74 @@ <template> <div class="flex flex-col items-center justify-center min-h-screen px-4 text-center relative"> - <h1 class="mb-8 lg:mb-12 text-4xl font-bold">Hvor mye bruker du per kjøp på ...</h1> - <div class="grid grid-cols-1 md:grid-cols-2 gap-8 mb-6"> - <div class="flex flex-col items-center bg-white rounded-lg p-8 shadow-lg w-full"> + <h1 class="mb-8 text-2xl font-bold sm:mb-16 sm:text-4xl"> + Hvor mye bruker du per kjøp på ... + </h1> + <div class="w-full flex justify-center"> + <div :class="[showSecondBox ? 'md:grid md:grid-cols-2 md:gap-4 sm:gap-8 mb-6' : '']"> <div - v-for="(option, index) in options.slice(0, 6)" - :key="`option-${index}`" - class="w-full my-4" + v-if="showFirstBox" + class="flex flex-col items-center bg-white rounded-lg p-4 sm:p-8 shadow-lg" + :class="showSecondBox ? 'w-full' : 'w-full md:w-1/2 mx-auto'" + :style="{ minWidth: '400px', maxWidth: '400px' }" > - <div class="flex justify-between items-center"> - <p class="text-xl font-bold mr-4">{{ option.type }}</p> - <div class="flex items-center w-2/3"> - <input - v-model="amounts[index]" - @input="filterAmount(index, $event)" - class="h-11 px-3 rounded-md text-lg focus:outline-none border-2 w-full" - :class="{ - 'border-gray-300': !amounts[index], - 'border-[var(--green)]': amounts[index] - }" - /> - <p class="text-xl font-bold ml-2">kr</p> + <div + v-for="(option, index) in firstBoxOptions" + :key="`first-option-${index}`" + class="w-full my-4" + > + <div class="flex justify-between items-center"> + <p class="text-xl font-bold mr-4">{{ option.type }}</p> + <div class="flex items-center w-2/3"> + <input + v-model="amounts[index]" + @input="filterAmount(index, $event)" + class="h-11 px-3 rounded-md text-lg focus:outline-none border-2 w-full" + :class="{ + 'border-gray-300': !amounts[index], + 'border-[var(--green)]': amounts[index] + }" + /> + <p class="text-xl font-bold ml-2">kr</p> + </div> </div> </div> </div> - </div> - <div class="flex flex-col items-center bg-white rounded-lg p-8 shadow-lg w-full"> <div - v-for="(option, index) in options.slice(6, 12)" - :key="`option-${index}`" - class="w-full my-4" + v-if="showSecondBox" + class="flex flex-col items-center bg-white rounded-lg p-4 sm:p-8 shadow-lg" + :class="showSecondBox ? 'w-full' : 'w-full md:w-1/2 mx-auto'" + :style="{ minWidth: '400px', maxWidth: '400px' }" > - <div class="flex justify-between items-center"> - <p class="text-xl font-bold mr-4">{{ option.type }}</p> - <div class="flex items-center w-2/3"> - <input - v-model="amounts[index + 6]" - @input="filterAmount(index + 6, $event)" - class="h-11 px-3 rounded-md text-lg focus:outline-none border-2 w-full" - :class="{ - 'border-gray-300': !amounts[index + 6], - 'border-[var(--green)]': amounts[index + 6] - }" - /> - <p class="text-xl font-bold ml-2">kr</p> + <div + v-for="(option, index) in secondBoxOptions" + :key="`second-option-${index}`" + class="w-full my-4" + > + <div class="flex justify-between items-center"> + <p class="text-xl font-bold mr-4">{{ option.type }}</p> + <div class="flex items-center w-2/3"> + <input + v-model="amounts[index + firstBoxOptions.length]" + @input="filterAmount(index + firstBoxOptions.length, $event)" + class="h-11 px-3 rounded-md text-lg focus:outline-none border-2 w-full" + :class="{ + 'border-gray-300': !amounts[index + firstBoxOptions.length], + 'border-[var(--green)]': + amounts[index + firstBoxOptions.length] + }" + /> + <p class="text-xl font-bold ml-2">kr</p> + </div> </div> </div> </div> </div> </div> - <div class="absolute bottom-36 right-4"> + <div class="w-full text-right"> <ContinueButtonComponent @click="onButtonClick" :disabled="!isAllAmountsFilled" - class="px-10 py-3 text-2xl font-bold mb-4" + class="px-10 py-3 text-2xl font-bold mb-20 mt-10 sm:mb-12 sm:mt-10" ></ContinueButtonComponent> </div> </div> @@ -77,7 +92,7 @@ const onButtonClick = () => { userConfigStore.challengeTypeConfigs[index].specificAmount = parseFloat(amounts.value[index]) || 0 }) - router.push('/konfigurasjonSteg5') + router.push({ name: 'configurations5' }) } const filterAmount = (index: number, event: Event) => { @@ -86,4 +101,10 @@ const filterAmount = (index: number, event: Event) => { filteredValue = filteredValue.replace(/(,.*?),/g, '$1').replace(/,+/g, ',') amounts.value[index] = filteredValue } + +const firstBoxOptions = computed(() => options.value.slice(0, 6)) +const secondBoxOptions = computed(() => (options.value.length > 6 ? options.value.slice(6) : [])) + +const showFirstBox = computed(() => options.value.length > 0) +const showSecondBox = computed(() => options.value.length > 6) </script> diff --git a/src/views/ConfigSpendingItemsTotalAmountView.vue b/src/views/ConfigSpendingItemsTotalAmountView.vue index d24cb4f6c652780a935a810a34c0ef3b489a2588..098ba86faec729bcbc8450ad91268d7e65d82536 100644 --- a/src/views/ConfigSpendingItemsTotalAmountView.vue +++ b/src/views/ConfigSpendingItemsTotalAmountView.vue @@ -1,61 +1,74 @@ <template> - <div class="flex flex-col items-center justify-center min-h-screen px-4 text-center"> - <h1 class="mb-8 lg:mb-12 text-4xl font-bold">Hvor mye bruker du totalt per uke på ...</h1> - <div class="grid grid-cols-1 md:grid-cols-2 gap-8 mb-6"> - <div class="flex flex-col items-center bg-white rounded-lg p-8 shadow-lg w-full"> + <div class="flex flex-col items-center justify-center min-h-screen px-4 text-center relative"> + <h1 class="mb-8 text-2xl font-bold sm:mb-16 sm:text-4xl"> + Hvor mye bruker du per uke på ... + </h1> + <div class="w-full flex justify-center"> + <div :class="[showSecondBox ? 'md:grid md:grid-cols-2 md:gap-4 sm:gap-8 mb-6' : '']"> <div - v-for="(option, index) in options.slice(0, 6)" - :key="`option-${index}`" - class="w-full my-4" + v-if="showFirstBox" + class="flex flex-col items-center bg-white rounded-lg p-4 sm:p-8 shadow-lg" + :class="showSecondBox ? 'w-full' : 'w-full md:w-1/2 mx-auto'" + :style="{ minWidth: '400px', maxWidth: '400px' }" > - <div class="flex justify-between items-center"> - <p class="text-xl font-bold mr-4">{{ option.type }}</p> - <div class="flex items-center w-2/3"> - <input - type="text" - v-model="amounts[index]" - @input="($event) => filterAmount(index, $event)" - class="h-11 px-3 rounded-md text-lg focus:outline-none border-2 w-full" - :class="{ - 'border-gray-300': !amounts[index], - 'border-[var(--green)]': amounts[index] - }" - /> - <p class="text-xl font-bold ml-2">kr</p> + <div + v-for="(option, index) in firstBoxOptions" + :key="`first-option-${index}`" + class="w-full my-4" + > + <div class="flex justify-between items-center"> + <p class="text-xl font-bold mr-4">{{ option.type }}</p> + <div class="flex items-center w-2/3"> + <input + v-model="amounts[index]" + @input="filterAmount(index, $event)" + class="h-11 px-3 rounded-md text-lg focus:outline-none border-2 w-full" + :class="{ + 'border-gray-300': !amounts[index], + 'border-[var(--green)]': amounts[index] + }" + /> + <p class="text-xl font-bold ml-2">kr</p> + </div> </div> </div> </div> - </div> - <div class="flex flex-col items-center bg-white rounded-lg p-8 shadow-lg w-full"> <div - v-for="(option, index) in options.slice(6, 12)" - :key="`option-${index}`" - class="w-full my-4" + v-if="showSecondBox" + class="flex flex-col items-center bg-white rounded-lg p-4 sm:p-8 shadow-lg" + :class="showSecondBox ? 'w-full' : 'w-full md:w-1/2 mx-auto'" + :style="{ minWidth: '400px', maxWidth: '400px' }" > - <div class="flex justify-between items-center"> - <p class="text-xl font-bold mr-4">{{ option.type }}</p> - <div class="flex items-center w-2/3"> - <input - type="text" - v-model="amounts[index + 6]" - @input="($event) => filterAmount(index + 6, $event)" - class="h-11 px-3 rounded-md text-lg focus:outline-none border-2 w-full" - :class="{ - 'border-gray-300': !amounts[index + 6], - 'border-[var(--green)]': amounts[index + 6] - }" - /> - <p class="text-xl font-bold ml-2">kr</p> + <div + v-for="(option, index) in secondBoxOptions" + :key="`second-option-${index}`" + class="w-full my-4" + > + <div class="flex justify-between items-center"> + <p class="text-xl font-bold mr-4">{{ option.type }}</p> + <div class="flex items-center w-2/3"> + <input + v-model="amounts[index + firstBoxOptions.length]" + @input="filterAmount(index + firstBoxOptions.length, $event)" + class="h-11 px-3 rounded-md text-lg focus:outline-none border-2 w-full" + :class="{ + 'border-gray-300': !amounts[index + firstBoxOptions.length], + 'border-[var(--green)]': + amounts[index + firstBoxOptions.length] + }" + /> + <p class="text-xl font-bold ml-2">kr</p> + </div> </div> </div> </div> </div> </div> - <div class="absolute bottom-24 right-4"> + <div class="w-full text-right"> <ContinueButtonComponent @click="onButtonClick" :disabled="!isAllAmountsFilled" - class="px-10 py-3 text-2xl font-bold mb-4" + class="px-10 py-3 text-2xl font-bold mb-20 mt-10 sm:mb-12 sm:mt-10" ></ContinueButtonComponent> </div> </div> @@ -80,8 +93,8 @@ const onButtonClick = async () => { parseFloat(amounts.value[index]) || 0 }) - await userConfigStore.postUserConfig() - router.push('/hjem') + userConfigStore.postUserConfig() + await router.push({ name: 'configurations6' }) } const filterAmount = (index: number, event: Event) => { @@ -90,4 +103,10 @@ const filterAmount = (index: number, event: Event) => { filteredValue = filteredValue.replace(/(,.*?),/g, '$1').replace(/,+/g, ',') amounts.value[index] = filteredValue } + +const firstBoxOptions = computed(() => options.value.slice(0, 6)) +const secondBoxOptions = computed(() => (options.value.length > 6 ? options.value.slice(6) : [])) + +const showFirstBox = computed(() => options.value.length > 0) +const showSecondBox = computed(() => options.value.length > 6) </script> diff --git a/src/views/ConfigSpendingItemsView.vue b/src/views/ConfigSpendingItemsView.vue index 2226cb066b6cc1ed6a880672f6f3223bd33bbb5d..fbeda5f09011b4a3f8639d752c02eab917c32e14 100644 --- a/src/views/ConfigSpendingItemsView.vue +++ b/src/views/ConfigSpendingItemsView.vue @@ -1,9 +1,9 @@ <template> <div class="flex flex-col items-center justify-center min-h-screen text-center"> - <h1 class="mb-8 lg:mb-12 text-4xl font-bold">Hva bruker du mye penger på?</h1> + <h1 class="mb-8 text-2xl font-bold sm:mb-16 sm:text-4xl">Hva bruker du mye penger på?</h1> <div class="flex flex-wrap justify-center gap-8 mb-8"> <div - class="flex flex-col items-center justify-center bg-white rounded-lg p-8 shadow-lg w-full md:w-[45%]" + class="flex flex-col items-center justify-center bg-white rounded-lg sm:p-8 shadow-lg sm:w-full md:w-[45%]" > <div v-for="buttonText in [ @@ -32,7 +32,7 @@ </div> </div> <div - class="flex flex-col items-center justify-center bg-white rounded-lg p-8 shadow-lg w-full md:w-[45%]" + class="flex flex-col items-center justify-center bg-white rounded-lg sm:p-8 shadow-lg sm:w-full md:w-[45%]" > <div v-for="(option, index) in customOptions" @@ -41,18 +41,23 @@ > <input v-model="customOptions[index]" - class="w-full md:w-64 h-11 px-3 rounded-md text-xl focus:outline-none transition-colors border-2 border-gray-300" + :class="[ + 'w-full md:w-64 h-11 px-3 rounded-md text-xl focus:outline-none transition-colors border-2', + customOptions[index].trim() !== '' + ? 'border-[var(--green)]' + : 'border-gray-300' + ]" type="text" :placeholder="'Annet ' + ' ...'" /> </div> </div> </div> - <div class="w-full text-right mb-0 mt-0" style="position: relative; top: -92px; right: 8px"> + <div class="w-full text-right"> <ContinueButtonComponent @click="onButtonClick" :disabled="!isFormValid" - class="px-10 py-3 text-2xl font-bold mb-4 mr-2" + class="px-10 py-3 text-2xl font-bold mt-36 mr-4 sm:mb-12 sm:mt-10" ></ContinueButtonComponent> </div> </div> @@ -68,24 +73,27 @@ const userConfigStore = useUserConfigStore() const selectedOptions = ref<string[]>([]) const customOptions = ref(['', '', '', '', '', '']) -const toggleOption = (option: string, isCustom: boolean = false) => { - if (!isCustom) { - const index = selectedOptions.value.indexOf(option) - if (index === -1) { - selectedOptions.value.push(option) - } else { - selectedOptions.value.splice(index, 1) - } +const toggleOption = (option: string) => { + const index = selectedOptions.value.indexOf(option) + if (index === -1) { + selectedOptions.value.push(option) + } else { + selectedOptions.value.splice(index, 1) } } const isFormValid = computed(() => { const predefinedSelected = selectedOptions.value.length > 0 const customFilled = customOptions.value.some((option) => option.trim() !== '') - return predefinedSelected || (customFilled && predefinedSelected) + return predefinedSelected || customFilled }) const onButtonClick = () => { + if (!isFormValid.value) { + console.error('Form is not valid') + return + } + const predefinedChallengeTypes = selectedOptions.value.map((option) => ({ type: option, specificAmount: 0, @@ -101,7 +109,6 @@ const onButtonClick = () => { })) userConfigStore.challengeTypeConfigs = [...predefinedChallengeTypes, ...customChallengeTypes] - console.log('Selected Challenge Types:', userConfigStore.challengeTypeConfigs) - router.push('/konfigurasjonSteg4') + router.push({ name: 'configurations4' }) } </script> diff --git a/src/views/ResetPasswordView.vue b/src/views/ResetPasswordView.vue index 6eacf26404a862292a847529cf8486b8c33d9d0e..cce6427a6d0dbea6bd91a5dfb1905a439374ca56 100644 --- a/src/views/ResetPasswordView.vue +++ b/src/views/ResetPasswordView.vue @@ -91,16 +91,11 @@ const canResetPassword = computed(() => { const resetPassword = async () => { isModalOpen.value = true - const resetPasswordDTO = { - resetID: resetID.value, - userID: userID.value, - newPassword: newPassword.value - } try { await axios.post('http://localhost:8080/forgotPassword/resetPassword', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(resetPasswordDTO) + resetID: resetID.value, + userID: userID.value, + newPassword: newPassword.value }) } catch (error) { const err = error as Error