From 14c9a4440fdaa2f9fd04d775313c4ae6ddf21ca1 Mon Sep 17 00:00:00 2001 From: Jens Christian Aanestad <jenscaa@stud.ntnu.no> Date: Thu, 18 Apr 2024 14:21:48 +0200 Subject: [PATCH] feat/Created configuration steps for sign up --- src/App.vue | 2 +- .../Configuration/ChallangeCheckBox.vue | 27 ++++++ .../Configuration/Configuration.vue | 47 ++++++++++ .../ConfigurationSteps/BankId.vue | 66 ++++++++++++++ .../ConfigurationSteps/Commitment.vue | 62 +++++++++++++ .../ConfigurationSteps/Experience.vue | 63 ++++++++++++++ .../ConfigurationSteps/FirstSavingGoal.vue | 87 +++++++++++++++++++ .../ConfigurationSteps/SuitableChallenges.vue | 55 ++++++++++++ src/components/Configuration/ProgressBar.vue | 34 ++++++++ src/components/InputFields/BaseInput.vue | 8 +- src/components/Login/LoginForm.vue | 1 - src/components/SignUp/SignUpForm.vue | 3 + src/router/index.ts | 32 +++++++ src/views/BasePageView.vue | 2 +- src/views/ConfigurationView.vue | 11 +++ 15 files changed, 496 insertions(+), 4 deletions(-) create mode 100644 src/components/Configuration/ChallangeCheckBox.vue create mode 100644 src/components/Configuration/Configuration.vue create mode 100644 src/components/Configuration/ConfigurationSteps/BankId.vue create mode 100644 src/components/Configuration/ConfigurationSteps/Commitment.vue create mode 100644 src/components/Configuration/ConfigurationSteps/Experience.vue create mode 100644 src/components/Configuration/ConfigurationSteps/FirstSavingGoal.vue create mode 100644 src/components/Configuration/ConfigurationSteps/SuitableChallenges.vue create mode 100644 src/components/Configuration/ProgressBar.vue create mode 100644 src/views/ConfigurationView.vue diff --git a/src/App.vue b/src/App.vue index 12ad03a..dc693f4 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,6 +1,6 @@ <script setup lang="ts"> import { RouterView } from 'vue-router' -import ErrorBoundaryCatcher from '@/components/Exceptions/ErrorBoundaryCatcher.vue'; +// import ErrorBoundaryCatcher from '@/components/Exceptions/ErrorBoundaryCatcher.vue'; </script> <template> diff --git a/src/components/Configuration/ChallangeCheckBox.vue b/src/components/Configuration/ChallangeCheckBox.vue new file mode 100644 index 0000000..f607eaf --- /dev/null +++ b/src/components/Configuration/ChallangeCheckBox.vue @@ -0,0 +1,27 @@ +<script setup lang="ts"> + +const props = defineProps({ + id: { + type: String, + required: true + }, + text: { + type: String, + default: '' + } +}) + +</script> + +<template> + <span> + <input type="checkbox" class="btn-check" :id="props.id" autocomplete="off"> + <label class="btn btn-outline-primary align-items-center justify-content-center" :for="props.id">{{ props.text }}</label> + </span> +</template> + +<style scoped> +label { + margin: 5px +} +</style> \ No newline at end of file diff --git a/src/components/Configuration/Configuration.vue b/src/components/Configuration/Configuration.vue new file mode 100644 index 0000000..3b5cd1a --- /dev/null +++ b/src/components/Configuration/Configuration.vue @@ -0,0 +1,47 @@ +<script setup lang="ts"> +import ProgressBar from '@/components/Configuration/ProgressBar.vue' +import { ref } from 'vue' +import { useRouter } from 'vue-router' +import { useRoute } from 'vue-router' + + +const router = useRouter() + +// The configuration steps with path and order value. +const configurationSteps = {'/bank-id': 1, '/commitment': 2, '/experience': 3, '/suitable-challenges': 4, '/first-saving-goal': 5} +const length = Object.keys(configurationSteps).length +let percentage = ref(1/length); + +// Pushes to '/bank-id' RouterView and sets current path to this path. +router.push(Object.keys(configurationSteps)[0]) +let currentRoute = useRoute() +let currentPath = currentRoute.fullPath + +const onNewRouteEvent = (path) => { + currentPath = path + percentage.value = (1/length) * configurationSteps[path] +} + +</script> + +<template> + <div class="container"> + <div class="progress-bar-container"> + <ProgressBar id="progressbar" :percentage="percentage"/> + </div> + <div class="configuration-container"> + <RouterView @changeRouterEvent="onNewRouteEvent"/> + </div> + </div> +</template> + +<style scoped> +.container { + display: grid; + grid-template-rows: 0.5fr 2.3fr 0.2fr; +} + +#progressbar { + padding-top: 2rem; +} +</style> \ No newline at end of file diff --git a/src/components/Configuration/ConfigurationSteps/BankId.vue b/src/components/Configuration/ConfigurationSteps/BankId.vue new file mode 100644 index 0000000..5fe760d --- /dev/null +++ b/src/components/Configuration/ConfigurationSteps/BankId.vue @@ -0,0 +1,66 @@ +<script setup lang="ts"> +import Button1 from '@/components/Buttons/Button1.vue' +import { useRouter } from 'vue-router' +import { ref } from 'vue' + +const formRef = ref() +const router = useRouter(); +const emit = defineEmits(['changeRouterEvent']); +emit('changeRouterEvent', '/bank-id'); + +const onClick = () => { + const radios = formRef.value.querySelectorAll('input[type="radio"]'); + const checkedRadios = Array.from(radios).filter(radio => radio.checked); + + if (checkedRadios.length === 0) { + alert('Please select an option.'); + return; + } + + router.push('/commitment') +} + +</script> + +<template> + <div class="container"> + <div> + <h3 class="d-flex align-items-center justify-content-center"> + In order to provide best advice we need to connect to your bank account + </h3> + </div> + + <form class="btn-group-vertical" ref="formRef" @submit.prevent="onClick"> + + <input type="radio" class="btn-check" name="bank-id" id="btn-check-outlined" autocomplete="off"> + <label class="btn btn-outline-primary d-flex align-items-center justify-content-center" for="btn-check-outlined">BankID på mobil</label> + + <input type="radio" class="btn-check" name="bank-id" id="btn-check2-outlined" autocomplete="off"> + <label class="btn btn-outline-primary d-flex align-items-center justify-content-center" for="btn-check2-outlined">MinID</label> + + <input type="radio" class="btn-check" name="bank-id" id="btn-check3-outlined" autocomplete="off"> + <label class="btn btn-outline-primary d-flex align-items-center justify-content-center" for="btn-check3-outlined">Vipps</label> + + </form> + + <div class="confirm-button-container"> + <button1 id="confirmButton" + @click="onClick" + button-text="Continue"> + </button1> + </div> + + </div> +</template> + +<style scoped> +#confirmButton { + margin-bottom: 2rem; + width: 300px; +} + +.confirm-button-container { + display: flex; + justify-content: center; +} +</style> \ No newline at end of file diff --git a/src/components/Configuration/ConfigurationSteps/Commitment.vue b/src/components/Configuration/ConfigurationSteps/Commitment.vue new file mode 100644 index 0000000..7fb352e --- /dev/null +++ b/src/components/Configuration/ConfigurationSteps/Commitment.vue @@ -0,0 +1,62 @@ +<script setup lang="ts"> + +import Button1 from '@/components/Buttons/Button1.vue' +import { useRouter } from 'vue-router' +import { ref } from 'vue' + +const router = useRouter(); +const formRef = ref() +const onClick = () => { + const radios = formRef.value.querySelectorAll('input[type="radio"]'); + const checkedRadios = Array.from(radios).filter(radio => radio.checked); + + if (checkedRadios.length === 0) { + alert('Please select an option.'); + return; + } + router.push('/experience') +} + +const emit = defineEmits(['changeRouterEvent']) +emit('changeRouterEvent', '/commitment') + +</script> + +<template> + <div class="container"> + <div> + <h3 class="d-flex align-items-center justify-content-center"> + In which degree are you willing to make changes? + </h3> + </div> + + <form class="btn-group-vertical" ref="formRef" @submit.prevent="onClick"> + + <input type="radio" class="btn-check" name="commitment" id="btn-check-outlined" autocomplete="off"> + <label class="btn btn-outline-primary d-flex align-items-center justify-content-center" for="btn-check-outlined">Low</label> + + <input type="radio" class="btn-check" name="commitment" id="btn-check2-outlined" autocomplete="off"> + <label class="btn btn-outline-primary d-flex align-items-center justify-content-center" for="btn-check2-outlined">Medium</label> + + <input type="radio" class="btn-check" name="commitment" id="btn-check3-outlined" autocomplete="off"> + <label class="btn btn-outline-primary d-flex align-items-center justify-content-center" for="btn-check3-outlined">High</label> + + </form> + + <div class="confirm-button-container"> + <button1 id="confirmButton" @click="onClick" button-text="Continue"></button1> + </div> + </div> +</template> + +<style scoped> +#confirmButton { + margin-bottom: 2rem; + width: 300px; +} + +.confirm-button-container { + display: flex; + justify-content: center; +} +</style> \ No newline at end of file diff --git a/src/components/Configuration/ConfigurationSteps/Experience.vue b/src/components/Configuration/ConfigurationSteps/Experience.vue new file mode 100644 index 0000000..fe699a9 --- /dev/null +++ b/src/components/Configuration/ConfigurationSteps/Experience.vue @@ -0,0 +1,63 @@ +<script setup lang="ts"> + +import Button1 from '@/components/Buttons/Button1.vue' + +import { useRouter } from 'vue-router' +import { ref } from 'vue' + +const router = useRouter(); +const formRef = ref() +const onClick = () => { + const radios = formRef.value.querySelectorAll('input[type="radio"]'); + const checkedRadios = Array.from(radios).filter(radio => radio.checked); + + if (checkedRadios.length === 0) { + alert('Please select an option.'); + return; + } + + router.push('/suitable-challenges') +} + +const emit = defineEmits(['changeRouterEvent']) +emit('changeRouterEvent', '/experience') + +</script> + +<template> + <div class="container"> + <div> + <h3 class="d-flex align-items-center justify-content-center"> + How much experice do you have with saving money? + </h3> + </div> + + <form class="btn-group-vertical" ref="formRef" @submit.prevent="onClick"> + + <input type="radio" class="btn-check" name="experience" id="btn-check-outlined" autocomplete="off"> + <label class="btn btn-outline-primary d-flex align-items-center justify-content-center" for="btn-check-outlined">Beginner</label> + + <input type="radio" class="btn-check" name="experience" id="btn-check2-outlined" autocomplete="off"> + <label class="btn btn-outline-primary d-flex align-items-center justify-content-center" for="btn-check2-outlined">Some experience</label> + + <input type="radio" class="btn-check" name="experience" id="btn-check3-outlined" autocomplete="off"> + <label class="btn btn-outline-primary d-flex align-items-center justify-content-center" for="btn-check3-outlined">Expert</label> + </form> + + <div class="confirm-button-container"> + <button1 id="confirmButton" @click="onClick" button-text="Continue"></button1> + </div> +</div> +</template> + +<style scoped> +#confirmButton { + margin-bottom: 2rem; + width: 300px; +} + +.confirm-button-container { + display: flex; + justify-content: center; +} +</style> \ No newline at end of file diff --git a/src/components/Configuration/ConfigurationSteps/FirstSavingGoal.vue b/src/components/Configuration/ConfigurationSteps/FirstSavingGoal.vue new file mode 100644 index 0000000..50e010c --- /dev/null +++ b/src/components/Configuration/ConfigurationSteps/FirstSavingGoal.vue @@ -0,0 +1,87 @@ +<script setup lang="ts"> +import BaseInput from '@/components/InputFields/BaseInput.vue' +import { ref } from 'vue' +import Button1 from '@/components/Buttons/Button1.vue' + +import { useRouter } from 'vue-router' + +const router = useRouter(); + +const emit = defineEmits(['changeRouterEvent']) +emit('changeRouterEvent', '/first-saving-goal') + +const formRef = ref() +const titleRef = ref() +const sumRef = ref() +const dateRef = ref() + +const onClick = () => { + formRef.value.classList.add("was-validated") +} + +const getTodayDate = () => { + const today = new Date(); + const year = today.getFullYear(); + let month = today.getMonth() + 1; + let day = today.getDate(); + + // Ensure month and day are in double digits + month = month < 10 ? `0${month}` : month; + day = day < 10 ? `0${day}` : day; + + console.log(`${year}-${month}-${day}`) + + return `${year}-${month}-${day}`; +}; + +</script> + +<template> + <div class="container"> + <div> + <h3 class="d-flex align-items-center justify-content-center"> + Create your first saving goal + </h3> + </div> + + <form ref="formRef" id="loginForm" @submit.prevent="handleSubmit"> + <BaseInput :model-value="titleRef" + @input-change-event="handleEmailInputEvent" + id="titleInput" + input-id="title" + label="Title" + placeholder="Enter the title of the saving goal"/> + <BaseInput :model-value="sumRef" + @input-change-event="handlePasswordInputEvent" + id="sumToSaveInput" + input-id="sumToSpareInput" + type="number" + label="Sum to save" + min="0" + placeholder="Enter the sum you would like to spare"/> + <BaseInput :model-value="dateRef" + @input-change-event="handlePasswordInputEvent" + id="dueDateInput" + input-id="dueDate" + type="date" + :min="getTodayDate()" + label="Due date"/> + </form> + + <div class="confirm-button-container"> + <button1 id="confirmButton" @click="onClick" button-text="Continue"></button1> + </div> + </div> +</template> + +<style scoped> +#confirmButton { + margin-bottom: 2rem; + width: 300px; +} + +.confirm-button-container { + display: flex; + justify-content: center; +} +</style> \ No newline at end of file diff --git a/src/components/Configuration/ConfigurationSteps/SuitableChallenges.vue b/src/components/Configuration/ConfigurationSteps/SuitableChallenges.vue new file mode 100644 index 0000000..09e7fcd --- /dev/null +++ b/src/components/Configuration/ConfigurationSteps/SuitableChallenges.vue @@ -0,0 +1,55 @@ +<script setup lang="ts"> + +import { useRouter } from 'vue-router' + +const router = useRouter(); +const onClick = () => { + router.push('/first-saving-goal') +} + +import ChallangeCheckBox from '@/components/Configuration/ChallangeCheckBox.vue' +import Button1 from '@/components/Buttons/Button1.vue' + +const emit = defineEmits(['changeRouterEvent']) +emit('changeRouterEvent', '/suitable-challenges') + +const challenges = ['Make packed lunch', 'Stop shopping', 'Drop coffee', + 'Quit subscription', 'Drop car', 'Short showers', 'Exercise outside', 'Make budget', 'Others' +] + +</script> + +<template> + <div class="container"> + <div> + <h3 class="d-flex align-items-center justify-content-center"> + Which challenges are suitable for you? + </h3> + </div> + + <div class="challenge-container"> + <ChallangeCheckBox v-for="(item, index) in challenges" :id="index" :text="item"/> + </div> + + <div class="confirm-button-container"> + <button1 id="confirmButton" @click="onClick" button-text="Continue"></button1> + </div> + </div> +</template> + +<style scoped> +.challenge-container { + justify-self: center; + max-width: 500px; +} + +#confirmButton { + margin-bottom: 2rem; + width: 300px; +} + +.confirm-button-container { + display: flex; + justify-content: center; +} +</style> \ No newline at end of file diff --git a/src/components/Configuration/ProgressBar.vue b/src/components/Configuration/ProgressBar.vue new file mode 100644 index 0000000..4b014e3 --- /dev/null +++ b/src/components/Configuration/ProgressBar.vue @@ -0,0 +1,34 @@ +<script setup lang="ts"> +const props = defineProps({ + percentage: { + type: Number, + default: 0 + } +}) +</script> + +<template> + <div class="container"> + <div class="progress"> + <div id="configuration-progress" + class="progress-bar progress-bar-striped bg-info progress-bar-animated" + role="progressbar" + :aria-valuenow="props.percentage" + :style="{ width: Math.round(props.percentage*100) + '%' }" + aria-valuemin="0" + aria-valuemax="100"/> + </div> + <label class="row text-info font-bold display-5">{{ Math.round(props.percentage*100) + '%' }} Completed</label> + </div> +</template> + +<style scoped> +.progress { + height: 35px; + border-radius: 12px; +} + +.row { + justify-content: center; +} +</style> \ No newline at end of file diff --git a/src/components/InputFields/BaseInput.vue b/src/components/InputFields/BaseInput.vue index 516faeb..28f6f3e 100644 --- a/src/components/InputFields/BaseInput.vue +++ b/src/components/InputFields/BaseInput.vue @@ -25,6 +25,10 @@ const props = defineProps({ isValid: { type: Boolean, default: false + }, + min: { + type: String, + required: false } }); @@ -41,7 +45,9 @@ const onInputEvent = (event: any) => { :type="props.type" class="form-control" :placeholder="props.placeholder" - :id="inputId" required /> + :id="inputId" required + :min="min" + /> <div v-if="props.isValid" class="invalid-feedback">Invalid {{ label }}</div> <div v-else class="valid-feedback">Valid {{ label }}</div> </div> diff --git a/src/components/Login/LoginForm.vue b/src/components/Login/LoginForm.vue index 150711c..b5198d4 100644 --- a/src/components/Login/LoginForm.vue +++ b/src/components/Login/LoginForm.vue @@ -9,7 +9,6 @@ const formRef = ref() const handleEmailInputEvent = (newValue: any) => { emailRef.value = newValue - console.log(emailRef.value) } const handlePasswordInputEvent = (newValue: any) => { diff --git a/src/components/SignUp/SignUpForm.vue b/src/components/SignUp/SignUpForm.vue index 04a8547..9ef325b 100644 --- a/src/components/SignUp/SignUpForm.vue +++ b/src/components/SignUp/SignUpForm.vue @@ -2,7 +2,9 @@ import BaseInput from '@/components/InputFields/BaseInput.vue' import Button1 from '@/components/Buttons/Button1.vue' import { ref } from 'vue' +import { useRouter } from 'vue-router' +const router = useRouter(); const firstNameRef = ref('') const surnameRef = ref('') const emailRef = ref('') @@ -34,6 +36,7 @@ const handleConfirmPasswordInputEvent = (newValue: any) => { const handleSubmit = () => { formRef.value.classList.add("was-validated") alert("Expected to be transferred to initial configuration") // Todo remove this line + router.push("/configuration") } </script> diff --git a/src/router/index.ts b/src/router/index.ts index 9cefc38..d8c0f7c 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -81,6 +81,38 @@ const routes = [ name: 'sign up', component: () => import('@/views/Authentication/SignUpView.vue'), }, + { + path: '/configuration', + name: 'configuration', + component: () => import('@/views/ConfigurationView.vue'), + children: [ + { + path: '/bank-id', + name: 'bankId', + component: () => import('@/components/Configuration/ConfigurationSteps/BankId.vue'), + }, + { + path: '/commitment', + name: 'commitment', + component: () => import('@/components/Configuration/ConfigurationSteps/Commitment.vue'), + }, + { + path: '/experience', + name: 'experience', + component: () => import('@/components/Configuration/ConfigurationSteps/Experience.vue'), + }, + { + path: '/suitable-challenges', + name: 'suitable challenges', + component: () => import('@/components/Configuration/ConfigurationSteps/SuitableChallenges.vue'), + }, + { + path: '/first-saving-goal', + name: 'first saving goal', + component: () => import('@/components/Configuration/ConfigurationSteps/FirstSavingGoal.vue'), + } + ] + }, { path: '/:pathMatch(.*)*', redirect: { name: 'not-found' }, diff --git a/src/views/BasePageView.vue b/src/views/BasePageView.vue index e22b163..e46d413 100644 --- a/src/views/BasePageView.vue +++ b/src/views/BasePageView.vue @@ -1,5 +1,5 @@ <script setup lang="ts"> -import { RouterLink, RouterView } from 'vue-router' +import { RouterView } from 'vue-router' import Footer from '@/components/BaseComponents/Footer.vue' import Menu from '@/components/BaseComponents/Menu.vue' </script> diff --git a/src/views/ConfigurationView.vue b/src/views/ConfigurationView.vue new file mode 100644 index 0000000..6732fee --- /dev/null +++ b/src/views/ConfigurationView.vue @@ -0,0 +1,11 @@ +<script setup lang="ts"> +import Configuration from '@/components/Configuration/Configuration.vue' +import Menu from '@/components/BaseComponents/Menu.vue' +import Footer from '@/components/BaseComponents/Footer.vue' +</script> + +<template> + <Menu/> + <Configuration/> + <Footer/> +</template> \ No newline at end of file -- GitLab