Skip to content
Snippets Groups Projects
Commit dbbf4bb6 authored by Anders Høvik's avatar Anders Høvik
Browse files

Merge branch 'main' into feature/update-user-settings-layout

parents 33945c89 53d2191f
No related branches found
No related tags found
1 merge request!30Feature/update user settings layout
Showing
with 305 additions and 165 deletions
<script setup lang="ts"> <script setup lang="ts">
import { RouterView } from 'vue-router' import { RouterView } from 'vue-router'
// import ErrorBoundaryCatcher from '@/components/Exceptions/ErrorBoundaryCatcher.vue'; import ErrorBoundaryCatcher from '@/components/Exceptions/ErrorBoundaryCatcher.vue';
</script> </script>
<template> <template>
......
...@@ -28,10 +28,10 @@ ...@@ -28,10 +28,10 @@
<a class="nav-link text-white" href="#" @click="toStore"><img <a class="nav-link text-white" href="#" @click="toStore"><img
src="@/assets/icons/storefront.svg">Store</a> src="@/assets/icons/storefront.svg">Store</a>
</li> </li>
<li class="nav-item dropdown"> <li v-if="userStore.isLoggedIn" class="nav-item dropdown">
<a class="nav-link dropdown-toggle username-text text-white " href="#" role="button" <a class="nav-link dropdown-toggle username-text text-white " href="#" role="button"
data-bs-toggle="dropdown" aria-expanded="false"> data-bs-toggle="dropdown" aria-expanded="false">
<img src="@/assets/icons/person.svg">Username <img src="@/assets/icons/person.svg">{{ userStore.firstname }}
</a> </a>
<ul class="dropdown-menu dropdown-username-content"> <ul class="dropdown-menu dropdown-username-content">
<li><a class="dropdown-item text-white dropdown-username-link" href="#" <li><a class="dropdown-item text-white dropdown-username-link" href="#"
...@@ -41,9 +41,12 @@ ...@@ -41,9 +41,12 @@
<li><a class="dropdown-item text-white dropdown-username-link" href="#" <li><a class="dropdown-item text-white dropdown-username-link" href="#"
@click="toFeedback"><img src="@/assets/icons/feedback.svg">Feedback</a></li> @click="toFeedback"><img src="@/assets/icons/feedback.svg">Feedback</a></li>
<li><a class="dropdown-item text-white dropdown-username-link" href="#" <li><a class="dropdown-item text-white dropdown-username-link" href="#"
@click="toFeedback"><img src="@/assets/icons/logout.svg">Log out</a></li> @click="toLogout"><img src="@/assets/icons/logout.svg">Log out</a></li>
</ul> </ul>
</li> </li>
<li v-else class="nav-item">
<a class="nav-link text-white" href="#" @click="toLogout">Login</a>
</li>
</ul> </ul>
</div> </div>
</div> </div>
...@@ -51,9 +54,12 @@ ...@@ -51,9 +54,12 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { useRouter } from "vue-router"; import { useRouter } from "vue-router";
import { useUserInfoStore } from '@/stores/UserStore';
const router = useRouter(); const router = useRouter();
const userStore = useUserInfoStore();
function toHome() { function toHome() {
router.push('/') router.push('/')
} }
...@@ -83,8 +89,13 @@ function toFeedback() { ...@@ -83,8 +89,13 @@ function toFeedback() {
} }
function toUserProfile(){ function toUserProfile() {
router.push('/profile') router.push('/profile')
}
function toLogout() {
userStore.clearUserInfo();
router.push('/login')
} }
...@@ -127,6 +138,11 @@ function toUserProfile(){ ...@@ -127,6 +138,11 @@ function toUserProfile(){
.dropdown-menu { .dropdown-menu {
background-color: #0A58CA; background-color: #0A58CA;
right: -0.5rem;
}
.dropdown-menu[data-bs-popper] {
left: auto;
} }
.dropdown-username-link { .dropdown-username-link {
......
<script setup lang="ts"> <script setup lang="ts">
const emit = defineEmits(['challengeChangedEvent'])
const props = defineProps({ const props = defineProps({
id: { id: {
type: String, type: String,
...@@ -8,14 +9,24 @@ const props = defineProps({ ...@@ -8,14 +9,24 @@ const props = defineProps({
text: { text: {
type: String, type: String,
default: '' default: ''
},
modelValue: {
type: Boolean,
default: false
} }
}) })
const onChallengeChanged = (event: any) => {
const value = event.target.checked
const data = [props.text, value]
emit('challengeChangedEvent', data)
}
</script> </script>
<template> <template>
<span> <span>
<input type="checkbox" class="btn-check" :id="props.id" autocomplete="off"> <input @change="onChallengeChanged" 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> <label class="btn btn-outline-primary align-items-center justify-content-center" :for="props.id">{{ props.text }}</label>
</span> </span>
</template> </template>
......
...@@ -4,24 +4,48 @@ import { ref } from 'vue' ...@@ -4,24 +4,48 @@ import { ref } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
// Configuration variables
let bankId = ref('')
let commitment = ref('')
let experience = ref('')
let suitableChallenges = ref([])
let savingGoalTitle = ref('')
let sumToSpare = ref(0)
let dueDate = ref(null)
const router = useRouter() const router = useRouter()
// The configuration steps with path and order value. // 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 configurationSteps = {'/bank-id': 1, '/commitment': 2, '/experience': 3, '/suitable-challenges': 4, '/first-saving-goal': 5}
const length = Object.keys(configurationSteps).length const length = Object.keys(configurationSteps).length
let percentage = ref(1/length); let percentage = ref(1/length);
// Pushes to '/bank-id' RouterView and sets current path to this path. // Initially pushes to '/bank-id' RouterView and sets current path to this path.
router.push(Object.keys(configurationSteps)[0]) router.push(Object.keys(configurationSteps)[0])
let currentRoute = useRoute() let currentRoute = useRoute()
let currentPath = currentRoute.fullPath let currentPath = currentRoute.fullPath
// Sets the current path to a new path and updates progressbar
const onNewRouteEvent = (path) => { const onNewRouteEvent = (path) => {
currentPath = path currentPath = path
percentage.value = (1/length) * configurationSteps[path] percentage.value = (1/length) * configurationSteps[path]
} }
const onBankIdSelectedEvent = (value) => {
bankId.value = value
}
const onCommitmentSelectedEvent = (value) => {
commitment.value = value
}
const onExperienceSelectedEvent = (value) => {
experience.value = value
}
const onChallengesSelectedEvent = (value) => {
suitableChallenges.value = value
}
</script> </script>
<template> <template>
...@@ -30,7 +54,12 @@ const onNewRouteEvent = (path) => { ...@@ -30,7 +54,12 @@ const onNewRouteEvent = (path) => {
<ProgressBar id="progressbar" :percentage="percentage"/> <ProgressBar id="progressbar" :percentage="percentage"/>
</div> </div>
<div class="configuration-container"> <div class="configuration-container">
<RouterView @changeRouterEvent="onNewRouteEvent"/> <RouterView @changeRouterEvent="onNewRouteEvent"
@bankIdSelectedEvent="onBankIdSelectedEvent"
@commitmentSelectedEvent="onCommitmentSelectedEvent"
@experienceSelectedEvent="onExperienceSelectedEvent"
@challengesSelectedEvent="onChallengesSelectedEvent"
/>
</div> </div>
</div> </div>
</template> </template>
......
...@@ -4,8 +4,13 @@ import { useRouter } from 'vue-router' ...@@ -4,8 +4,13 @@ import { useRouter } from 'vue-router'
import { ref } from 'vue' import { ref } from 'vue'
const formRef = ref() const formRef = ref()
const bankIDRef = ref(false)
const minIdRef = ref(false)
const vippsRef = ref(false)
const router = useRouter(); const router = useRouter();
const emit = defineEmits(['changeRouterEvent']); const emit = defineEmits(['changeRouterEvent', 'bankIdSelectedEvent']);
emit('changeRouterEvent', '/bank-id'); emit('changeRouterEvent', '/bank-id');
const onClick = () => { const onClick = () => {
...@@ -17,6 +22,12 @@ const onClick = () => { ...@@ -17,6 +22,12 @@ const onClick = () => {
return; return;
} }
let choice = ''
if (bankIDRef.value.checked) choice = 'BankId på mobil'
else if (minIdRef.value.checked) choice = 'MinId'
else if (vippsRef.value.checked) choice = 'Vipps'
emit('bankIdSelectedEvent', choice)
router.push('/commitment') router.push('/commitment')
} }
...@@ -32,13 +43,13 @@ const onClick = () => { ...@@ -32,13 +43,13 @@ const onClick = () => {
<form class="btn-group-vertical" ref="formRef" @submit.prevent="onClick"> <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"> <input ref="bankIDRef" 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> <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"> <input ref="minIdRef" 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> <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"> <input ref="vippsRef" 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> <label class="btn btn-outline-primary d-flex align-items-center justify-content-center" for="btn-check3-outlined">Vipps</label>
</form> </form>
......
...@@ -4,8 +4,15 @@ import Button1 from '@/components/Buttons/Button1.vue' ...@@ -4,8 +4,15 @@ import Button1 from '@/components/Buttons/Button1.vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { ref } from 'vue' import { ref } from 'vue'
const router = useRouter(); const emit = defineEmits(['changeRouterEvent', 'commitmentSelectedEvent'])
emit('changeRouterEvent', '/commitment')
const formRef = ref() const formRef = ref()
const lowRef = ref('')
const mediumRef = ref('')
const highRef = ref('')
const router = useRouter();
const onClick = () => { const onClick = () => {
const radios = formRef.value.querySelectorAll('input[type="radio"]'); const radios = formRef.value.querySelectorAll('input[type="radio"]');
const checkedRadios = Array.from(radios).filter(radio => radio.checked); const checkedRadios = Array.from(radios).filter(radio => radio.checked);
...@@ -14,12 +21,16 @@ const onClick = () => { ...@@ -14,12 +21,16 @@ const onClick = () => {
alert('Please select an option.'); alert('Please select an option.');
return; return;
} }
let choice = '';
if (lowRef.value.checked) choice = 'Low'
else if (mediumRef.value.checked) choice = 'Medium'
else if (highRef.value.checked) choice = 'High'
emit('commitmentSelectedEvent', choice)
router.push('/experience') router.push('/experience')
} }
const emit = defineEmits(['changeRouterEvent'])
emit('changeRouterEvent', '/commitment')
</script> </script>
<template> <template>
...@@ -32,13 +43,13 @@ emit('changeRouterEvent', '/commitment') ...@@ -32,13 +43,13 @@ emit('changeRouterEvent', '/commitment')
<form class="btn-group-vertical" ref="formRef" @submit.prevent="onClick"> <form class="btn-group-vertical" ref="formRef" @submit.prevent="onClick">
<input type="radio" class="btn-check" name="commitment" id="btn-check-outlined" autocomplete="off"> <input ref="lowRef" 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> <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"> <input ref="mediumRef" 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> <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"> <input ref="highRef" 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> <label class="btn btn-outline-primary d-flex align-items-center justify-content-center" for="btn-check3-outlined">High</label>
</form> </form>
......
<script setup lang="ts"> <script setup lang="ts">
import Button1 from '@/components/Buttons/Button1.vue' import Button1 from '@/components/Buttons/Button1.vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { ref } from 'vue' import { ref } from 'vue'
const router = useRouter(); const router = useRouter();
const emit = defineEmits(['changeRouterEvent', 'experienceSelectedEvent'])
emit('changeRouterEvent', '/experience')
const formRef = ref() const formRef = ref()
const beginnerRef = ref('')
const someExperienceRef = ref('')
const expertRef = ref('')
const onClick = () => { const onClick = () => {
const radios = formRef.value.querySelectorAll('input[type="radio"]'); const radios = formRef.value.querySelectorAll('input[type="radio"]');
const checkedRadios = Array.from(radios).filter(radio => radio.checked); const checkedRadios = Array.from(radios).filter(radio => radio.checked);
...@@ -16,31 +21,34 @@ const onClick = () => { ...@@ -16,31 +21,34 @@ const onClick = () => {
return; return;
} }
let choice = ''
if (beginnerRef.value.checked) choice = 'Beginner'
else if (someExperienceRef.value.checked) choice = 'Some experience'
else if (expertRef.value.checked) choice = 'Expert'
emit('experienceSelectedEvent', choice)
router.push('/suitable-challenges') router.push('/suitable-challenges')
} }
const emit = defineEmits(['changeRouterEvent'])
emit('changeRouterEvent', '/experience')
</script> </script>
<template> <template>
<div class="container"> <div class="container">
<div> <div>
<h3 class="d-flex align-items-center justify-content-center"> <h3 class="d-flex align-items-center justify-content-center">
How much experice do you have with saving money? How much experience do you have with saving money?
</h3> </h3>
</div> </div>
<form class="btn-group-vertical" ref="formRef" @submit.prevent="onClick"> <form class="btn-group-vertical" ref="formRef" @submit.prevent="onClick">
<input type="radio" class="btn-check" name="experience" id="btn-check-outlined" autocomplete="off"> <input ref="beginnerRef" 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> <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"> <input ref="someExperienceRef" 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> <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"> <input ref="expertRef" 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> <label class="btn btn-outline-primary d-flex align-items-center justify-content-center" for="btn-check3-outlined">Expert</label>
</form> </form>
......
<script setup lang="ts"> <script setup lang="ts">
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
const router = useRouter();
const onClick = () => {
router.push('/first-saving-goal')
}
import ChallangeCheckBox from '@/components/Configuration/ChallangeCheckBox.vue' import ChallangeCheckBox from '@/components/Configuration/ChallangeCheckBox.vue'
import Button1 from '@/components/Buttons/Button1.vue' import Button1 from '@/components/Buttons/Button1.vue'
import { ref } from 'vue'
const emit = defineEmits(['changeRouterEvent']) const emit = defineEmits(['changeRouterEvent', 'challengesSelectedEvent'])
emit('changeRouterEvent', '/suitable-challenges') emit('changeRouterEvent', '/suitable-challenges')
const router = useRouter();
let chosenChallenges = ref([])
const challenges = ['Make packed lunch', 'Stop shopping', 'Drop coffee', const challenges = ['Make packed lunch', 'Stop shopping', 'Drop coffee',
'Quit subscription', 'Drop car', 'Short showers', 'Exercise outside', 'Make budget', 'Others' 'Quit subscription', 'Drop car', 'Short showers', 'Exercise outside', 'Make budget']
]
const onChangedChallengeEvent = (value) => {
// if challenge is checked then add it to the chosenChallenges variable
if (value[1]) {
chosenChallenges.value.push(value[0])
}
// if challenge is unchecked then remove it from the chosenChallenges variable
else {
console.log('Reached')
chosenChallenges.value = chosenChallenges.value.filter(item => item !== value[0]);
}
}
const onClick = () => {
emit('challengesSelectedEvent', chosenChallenges.value)
router.push('/first-saving-goal')
}
</script> </script>
...@@ -28,7 +40,9 @@ const challenges = ['Make packed lunch', 'Stop shopping', 'Drop coffee', ...@@ -28,7 +40,9 @@ const challenges = ['Make packed lunch', 'Stop shopping', 'Drop coffee',
</div> </div>
<div class="challenge-container"> <div class="challenge-container">
<ChallangeCheckBox v-for="(item, index) in challenges" :id="index" :text="item"/> <ChallangeCheckBox v-for="(item, index) in challenges" :id="index" :text="item"
@challengeChangedEvent="onChangedChallengeEvent"
/>
</div> </div>
<div class="confirm-button-container"> <div class="confirm-button-container">
......
<template> <template>
<error-box :error-message="errorStore.getFirstError" @update:errorMessage="errorStore.removeCurrentError" /> <error-box :error-message="errorStore.getFirstError" @update:errorMessage="errorStore.removeCurrentError" />
<slot /> <slot />
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { onErrorCaptured } from 'vue'; import { onErrorCaptured } from 'vue';
import { useErrorStore } from '@/stores/ErrorStore'; import { useErrorStore } from '@/stores/ErrorStore';
import ErrorBox from '@/components/Exceptions/ErrorBox.vue'; import ErrorBox from '@/components/Exceptions/ErrorBox.vue';
import handleUnknownError from '@/components/Exceptions/unkownErrorHandler'; import handleUnknownError from '@/components/Exceptions/unkownErrorHandler';
const errorStore = useErrorStore(); const errorStore = useErrorStore();
onErrorCaptured((err, _vm, _info): boolean => { onErrorCaptured((err, _vm, _info): boolean => {
const message = handleUnknownError(err.message); const message = handleUnknownError(err);
errorStore.addError(message); //If no openAPI axios error, use err.message errorStore.addError(message);
return false; return false;
}); });
</script> </script>
\ No newline at end of file \ No newline at end of file
<template> <template>
<div class="error-box" v-if="errorMessage" data-testid="error-box"> <div class="error-box" v-if="errorMessage" data-testid="error-box">
<span> <span>
<v-icon name="bi-exclamation-triangle" /> <v-icon name="bi-exclamation-triangle" />
<button @click="wrapText = !wrapText" class="error-message-button"> <button @click="wrapText = !wrapText" class="error-message-button">
<h4>{{ errorMessage }}</h4> <h4>{{ errorMessage }}</h4>
</button> </button>
<button class="error-remove-button" @click="$emit('update:errorMessage', '')" data-testid="hide-button"> <button class="error-remove-button" @click="$emit('update:errorMessage', '')" data-testid="hide-button">
<v-icon scale="2" name="bi-dash" /> <v-icon scale="2" name="bi-dash" />
</button> </button>
</span> </span>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import { OhVueIcon, addIcons } from 'oh-vue-icons'; import { OhVueIcon, addIcons } from 'oh-vue-icons';
import { BiDash, BiExclamationTriangle } from 'oh-vue-icons/icons'; import { BiDash, BiExclamationTriangle } from 'oh-vue-icons/icons';
addIcons(BiDash, BiExclamationTriangle); addIcons(BiDash, BiExclamationTriangle);
export default defineComponent({ export default defineComponent({
components: { components: {
'v-icon': OhVueIcon, 'v-icon': OhVueIcon,
},
props: {
errorMessage: {
type: String,
default: '',
}, },
props: { },
errorMessage: { data() {
type: String, return {
default: '', wrapText: false,
}, };
}, },
data() { });
return { </script>
wrapText: false,
}; <style scoped>
}, .error-box {
}); position: fixed;
</script> top: 2em;
left: 50%;
<style scoped> transform: translate(-50%, 0);
.error-box { width: min(100%, 700px);
position: fixed; background-color: var(--red-color);
top: 2em; padding: 7px;
left: 50%; border-radius: 5px;
transform: translate(-50%, 0); z-index: 1000;
width: min(100%, 700px); }
background-color: var(--red-color);
padding: 7px; .error-box span {
border-radius: 5px; display: flex;
z-index: 1000; justify-content: space-between;
} align-items: center;
}
.error-box span {
display: flex; .error-message-button {
justify-content: space-between; white-space: v-bind('wrapText ? "normal" : "nowrap"');
align-items: center; overflow: hidden;
} border: none;
}
.error-message-button {
white-space: v-bind('wrapText ? "normal" : "nowrap"'); .error-message-button h4 {
overflow: hidden; overflow: hidden;
border: none; text-overflow: ellipsis;
} }
.error-message-button h4 { .error-box * {
overflow: hidden; margin: 2px;
text-overflow: ellipsis; }
}
.error-box button {
.error-box * { background-color: transparent;
margin: 2px; box-shadow: none;
} color: black;
cursor: pointer;
.error-box button { }
background-color: transparent;
box-shadow: none; .error-remove-button {
color: black; color: rgb(79, 77, 77);
cursor: pointer; border: solid black 0.5px;
} padding: 0.3em;
}
.error-remove-button {
color: rgb(79, 77, 77); .error-box button:hover {
border: solid black 0.5px; color: rgb(40, 38, 38);
padding: 0.3em; }
} </style>
\ No newline at end of file
.error-box button:hover {
color: rgb(40, 38, 38);
}
</style>
\ No newline at end of file
...@@ -11,9 +11,9 @@ const handleUnknownError = (error: any): string => { ...@@ -11,9 +11,9 @@ const handleUnknownError = (error: any): string => {
if (error instanceof AxiosError) { if (error instanceof AxiosError) {
return error.code!!; return error.code!!;
} else if (error instanceof BackendApiError) { } else if (error instanceof BackendApiError) {
return error.body.detail ?? error.body; return error.body.message ?? error.body;
} }
return 'ContextErrorMessage'; return error;
}; };
export default handleUnknownError; export default handleUnknownError;
\ No newline at end of file
...@@ -29,6 +29,10 @@ const props = defineProps({ ...@@ -29,6 +29,10 @@ const props = defineProps({
min: { min: {
type: String, type: String,
required: false required: false
},
pattern: {
type: String,
default: null
} }
}); });
...@@ -47,6 +51,7 @@ const onInputEvent = (event: any) => { ...@@ -47,6 +51,7 @@ const onInputEvent = (event: any) => {
:placeholder="props.placeholder" :placeholder="props.placeholder"
:id="inputId" required :id="inputId" required
:min="min" :min="min"
:pattern="pattern"
/> />
<div v-if="props.isValid" class="invalid-feedback">Invalid {{ label }}</div> <div v-if="props.isValid" class="invalid-feedback">Invalid {{ label }}</div>
<div v-else class="valid-feedback">Valid {{ label }}</div> <div v-else class="valid-feedback">Valid {{ label }}</div>
......
...@@ -11,6 +11,7 @@ import { useErrorStore } from '@/stores/ErrorStore'; ...@@ -11,6 +11,7 @@ import { useErrorStore } from '@/stores/ErrorStore';
const emailRef = ref() const emailRef = ref()
const passwordRef = ref() const passwordRef = ref()
const formRef = ref() const formRef = ref()
let errorMsg = ref('');
const errorStore = useErrorStore(); const errorStore = useErrorStore();
const router = useRouter(); const router = useRouter();
...@@ -25,6 +26,7 @@ const handlePasswordInputEvent = (newValue: any) => { ...@@ -25,6 +26,7 @@ const handlePasswordInputEvent = (newValue: any) => {
} }
const handleSubmit = async () => { const handleSubmit = async () => {
formRef.value.classList.add("was-validated")
const loginUserPayload: LoginRequest = { const loginUserPayload: LoginRequest = {
email: emailRef.value, email: emailRef.value,
password: passwordRef.value password: passwordRef.value
...@@ -34,7 +36,7 @@ const handleSubmit = async () => { ...@@ -34,7 +36,7 @@ const handleSubmit = async () => {
let response = await AuthenticationService.login({ requestBody: loginUserPayload }); let response = await AuthenticationService.login({ requestBody: loginUserPayload });
if (response.token == null || response.token == undefined) { if (response.token == null || response.token == undefined) {
//errorBoxMsg.value = 'A valid token could not be created'; errorMsg.value = 'A valid token could not be created';
return; return;
} }
...@@ -49,7 +51,7 @@ const handleSubmit = async () => { ...@@ -49,7 +51,7 @@ const handleSubmit = async () => {
}); });
router.push({ name: 'home' }); router.push({ name: 'home' });
} catch (error: any) { } catch (error: any) {
console.log(error); errorMsg.value = handleUnknownError(error);
} }
} }
...@@ -57,11 +59,17 @@ const handleSubmit = async () => { ...@@ -57,11 +59,17 @@ const handleSubmit = async () => {
<template> <template>
<div class="container-fluid"> <div class="container-fluid">
<form ref="formRef" id="loginForm" @submit.prevent="handleSubmit"> <div class="container-fluid d-flex justify-content-center align-items-center flex-column mt-5">
<img src="@/assets/Sparesti-logo.png" style="width: 300px">
<h1>Sparesti.no</h1>
</div>
<form ref="formRef" id="loginForm" @submit.prevent="handleSubmit" novalidate>
<BaseInput :model-value="emailRef" @input-change-event="handleEmailInputEvent" id="emailInput" input-id="email" <BaseInput :model-value="emailRef" @input-change-event="handleEmailInputEvent" id="emailInput" input-id="email"
type="email" label="Email" placeholder="Enter your email" /> type="email" label="Email" placeholder="Enter your email" />
<BaseInput :model-value="passwordRef" @input-change-event="handlePasswordInputEvent" id="passwordInput" <BaseInput pattern="(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{4,16}" :model-value="passwordRef"
input-id="password" type="password" label="Password" placeholder="Enter password" /> @input-change-event="handlePasswordInputEvent" id="passwordInput" input-id="password" type="password"
label="Password" placeholder="Enter password" />
<p class="text-danger">{{ errorMsg }}</p>
<button1 id="confirmButton" type="submit" @click="handleSubmit" button-text="Login"></button1> <button1 id="confirmButton" type="submit" @click="handleSubmit" button-text="Login"></button1>
</form> </form>
</div> </div>
...@@ -83,4 +91,8 @@ const handleSubmit = async () => { ...@@ -83,4 +91,8 @@ const handleSubmit = async () => {
#confirmButton { #confirmButton {
margin: 1rem 0; margin: 1rem 0;
} }
h1 {
font-size: 4rem;
}
</style> </style>
\ No newline at end of file
...@@ -14,7 +14,6 @@ const formRef = ref() ...@@ -14,7 +14,6 @@ const formRef = ref()
const handleFirstNameInputEvent = (newValue: any) => { const handleFirstNameInputEvent = (newValue: any) => {
firstNameRef.value = newValue firstNameRef.value = newValue
console.log(firstNameRef.value)
} }
const handleSurnameInputEvent = (newValue: any) => { const handleSurnameInputEvent = (newValue: any) => {
...@@ -34,9 +33,15 @@ const handleConfirmPasswordInputEvent = (newValue: any) => { ...@@ -34,9 +33,15 @@ const handleConfirmPasswordInputEvent = (newValue: any) => {
} }
const handleSubmit = () => { const handleSubmit = () => {
const form = formRef.value;
// Check if the form is valid
if (form.checkValidity()) {
// Form is valid, submit the form or perform other actions
console.log('Form is valid');
}
formRef.value.classList.add("was-validated") formRef.value.classList.add("was-validated")
alert("Expected to be transferred to initial configuration") // Todo remove this line
router.push("/configuration")
} }
</script> </script>
......
...@@ -12,15 +12,15 @@ const routes = [ ...@@ -12,15 +12,15 @@ const routes = [
path: '/', path: '/',
name: 'base', name: 'base',
component: () => import('@/views/BasePageView.vue'), component: () => import('@/views/BasePageView.vue'),
meta: { requiresAuth: true },
children: [ children: [
{ {
path: '', path: '',
name: 'home', name: 'home',
component: () => import('../views/HomeView.vue'), component: () => import('../views/SavingGoalView/RoadmapView.vue'),
meta: { requiresAuth: true },
}, },
{ {
path: '/news', path: 'news',
name: 'news', name: 'news',
component: () => import('@/views/NewsView.vue'), component: () => import('@/views/NewsView.vue'),
}, },
...@@ -59,11 +59,16 @@ const routes = [ ...@@ -59,11 +59,16 @@ const routes = [
name: 'shop', name: 'shop',
component: () => import('@/views/ShopView.vue'), component: () => import('@/views/ShopView.vue'),
}, },
{
path: '/profile',
name: 'profile',
component: UserProfileView
},
{ {
path: 'admin', path: 'admin',
name: 'admin', name: 'admin',
component: () => import('@/views/TestView.vue'), component: () => import('@/views/TestView.vue'),
meta: { requiresAdmin: true } meta: { requiresAdmin: true },
}, },
{ {
path: 'unauthorized', path: 'unauthorized',
...@@ -136,9 +141,13 @@ const router = createRouter({ ...@@ -136,9 +141,13 @@ const router = createRouter({
router.beforeEach((to, from, next) => { router.beforeEach((to, from, next) => {
const requiresAuth = to.matched.some(record => record.meta.requiresAuth); const requiresAuth = to.matched.some(record => record.meta.requiresAuth);
const requiresAdmin = to.matched.some(record => record.meta.requiresAdmin); const requiresAdmin = to.matched.some(record => record.meta.requiresAdmin);
const userRole = useUserInfoStore().role; let user = useUserInfoStore();
const userRole = user.role;
const isAuthenticated = user.isLoggedIn;
if (requiresAdmin && userRole !== 'admin') { if (requiresAuth && !isAuthenticated) {
next({ name: 'login', query: { redirect: to.fullPath } });
} else if (requiresAdmin && userRole !== 'admin') {
next({ name: 'unauthorized' }); next({ name: 'unauthorized' });
} else { } else {
next(); next();
......
...@@ -33,6 +33,7 @@ export type UserStoreInfo = { ...@@ -33,6 +33,7 @@ export type UserStoreInfo = {
email?: string; email?: string;
firstname?: string; firstname?: string;
lastname?: string; lastname?: string;
password?: string;
accessToken?: string; accessToken?: string;
role?: string; role?: string;
}; };
...@@ -42,10 +43,17 @@ export const useUserInfoStore = defineStore('UserInfoStore', { ...@@ -42,10 +43,17 @@ export const useUserInfoStore = defineStore('UserInfoStore', {
email: '', email: '',
firstname: '', firstname: '',
lastname: '', lastname: '',
password: '',
accessToken: '', accessToken: '',
role: '', role: '',
}), }),
actions: { actions: {
setPassword(password: string) {
this.password = password
},
resetPassword() {
this.password = ''
},
setUserInfo(userinfo: UserStoreInfo) { setUserInfo(userinfo: UserStoreInfo) {
userinfo.email && (this.$state.email = userinfo.email); userinfo.email && (this.$state.email = userinfo.email);
userinfo.firstname && (this.$state.firstname = userinfo.firstname); userinfo.firstname && (this.$state.firstname = userinfo.firstname);
...@@ -64,6 +72,9 @@ export const useUserInfoStore = defineStore('UserInfoStore', { ...@@ -64,6 +72,9 @@ export const useUserInfoStore = defineStore('UserInfoStore', {
}, },
}, },
getters: { getters: {
getPassword(): string {
return this.password
},
isLoggedIn(): boolean { isLoggedIn(): boolean {
return this.accessToken !== ''; return this.accessToken !== '';
}, },
......
...@@ -5,9 +5,7 @@ import Login from '@/components/Login/Login.vue' ...@@ -5,9 +5,7 @@ import Login from '@/components/Login/Login.vue'
</script> </script>
<template> <template>
<Menu/>
<Login/> <Login/>
<Footer/>
</template> </template>
<style scoped> <style scoped>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment