Skip to content
Snippets Groups Projects
Commit f3daaebc authored by Trygve Jørgensen's avatar Trygve Jørgensen
Browse files

Merge remote-tracking branch...

Merge remote-tracking branch 'refs/remotes/origin/feat/implement-genereated-challenges-modal' into fix/30-router-checks

# Conflicts:
#	src/stores/accountStore.ts
#	src/views/ConfigAccountNumberView.vue
parents 89d41a6d 9e2c929c
No related branches found
No related tags found
3 merge requests!66Final merge,!44Configuration validation,!4Pipeline fix
<template>
<div
v-if="generatedChallenges.length > 0"
class="fixed inset-0 bg-gray-300 bg-opacity-75 flex justify-center items-center"
>
<div class="relative bg-white pt-10 p-6 rounded-lg shadow-xl" style="width: 40rem">
<button
@click="closeModal"
class="absolute top-0 right-0 m-2 text-gray-600 hover:text-gray-800"
>
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-6 w-6"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M6 18L18 6M6 6l12 12"
/>
</svg>
</button>
<div class="text-center font-bold text-3xl mb-4 mt-2">
Personlig tilpassede spareutfordringer:
</div>
<div class="grid grid-cols-7 gap-4 p-3 border-b-2">
<span class="font-bold col-span-2">Tittel</span>
<span class="font-bold col-span-1">Målsum</span>
<span class="font-bold col-span-2">Frist</span>
<span class="col-span-2"></span>
</div>
<div class="space-y-2">
<div
v-for="(challenge, index) in generatedChallenges"
:key="challenge.id"
:class="{ 'bg-gray-100': index % 2 === 0 }"
class="grid grid-cols-7 gap-4 items-center border p-3 rounded"
>
<span class="break-words col-span-2 font-bold">{{ challenge.title }}</span>
<span class="col-span-1 font-bold">{{ challenge.target }}</span>
<span class="col-span-2 font-bold">{{ challenge.due }}</span>
<div class="flex items-center justify-end space-x-2 col-span-2">
<button
@click="declineChallenge(challenge.id)"
class="bg-gray-500 hover:bg-gray-700 text-white font-bold py-1 px-4"
>
Skip
</button>
<button
@click="acceptChallenge(challenge.id)"
class="text-white font-bold py-1 px-4"
>
Godta
</button>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import authInterceptor from '@/services/authInterceptor'
import { useChallengeStore } from '@/stores/challengeStore'
const generatedChallenges = ref([])
const fetchGeneratedChallenges = async () => {
try {
const response = await authInterceptor.get('/challenges/active')
if (response.status === 200 && response.data.content) {
console.log('Active challenges:', response.data.content)
generatedChallenges.value = response.data.content.map((challenge) => ({
id: challenge.id,
title: challenge.title,
target: challenge.target.toString(),
due: challenge.due.substring(0, 10)
}))
} else {
console.error('No challenges found for the user.')
generatedChallenges.value = []
}
} catch (error) {
console.error('Error fetching challenges:', error)
generatedChallenges.value = []
}
}
onMounted(() => {
fetchGeneratedChallenges()
})
const removeChallenge = (id) => {
const index = generatedChallenges.value.findIndex((challenge) => challenge.id === id)
if (index !== -1) {
generatedChallenges.value.splice(index, 1)
generatedChallenges.value = [...generatedChallenges.value]
}
if (generatedChallenges.value.length === 0) {
closeModal()
}
}
function acceptChallenge(id) {
console.log('Accepted challenge:', id)
const acceptedChallenge = generatedChallenges.value.find((challenge) => challenge.id === id)
if (acceptedChallenge) {
useChallengeStore.editUserChallenge(acceptedChallenge)
removeChallenge(id)
}
}
const declineChallenge = async (id) => {
try {
const response = authInterceptor.delete(`/challenges/${id}`)
if (response.status === 200) {
console.log('Challenge declined and removed:', id)
removeChallenge(id)
} else {
console.error('Failed to decline challenge:', response.data)
}
} catch (error) {
console.error('Error declining challenge:', error)
}
}
const closeModal = () => {
generatedChallenges.value = []
}
</script>
import { defineStore } from 'pinia'
import { ref } from 'vue'
import authInterceptor from '@/services/authInterceptor'
import { 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'
}
}
}
})
import { defineStore } from 'pinia'
import { ref } from 'vue'
import { defineStore } from 'pinia'
import authInterceptor from '@/services/authInterceptor'
import { AxiosError } from 'axios'
export const useUserConfigStore = defineStore('userConfig', {
state: () => ({
role: 'USER',
experience: 'VERY_HIGH',
motivation: 'VERY_HIGH',
challengeTypeConfigs: [] as {
export const useUserConfigStore = defineStore('userConfig', () => {
const role = ref('USER')
const experience = ref('')
const motivation = ref('')
const challengeTypeConfigs = ref(
[] as {
type: string
specificAmount: number
generalAmount: number
}[],
errorMessage: ref<string>('')
}),
actions: {
setExperience(value: string) {
this.experience = value
},
setMotivation(value: string) {
this.motivation = value
},
addChallengeTypeConfig(type: string, specificAmount: number, generalAmount: number) {
this.challengeTypeConfigs.push({ type, specificAmount, generalAmount })
},
postUserConfig() {
const payload = {
experience: this.experience,
motivation: this.motivation,
challengeTypeConfigs: Array.from(this.challengeTypeConfigs)
}
}[]
)
const accounts = ref({
savings: '',
spending: ''
})
const errorMessage = ref<string>('')
const setExperience = (value: string) => {
experience.value = value
}
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)
})
const setMotivation = (value: string) => {
motivation.value = value
}
const addChallengeTypeConfig = (
type: string,
specificAmount: number,
generalAmount: number
) => {
challengeTypeConfigs.value.push({ type, specificAmount, generalAmount })
}
const postAccount = async (
accountType: 'SAVING' | 'SPENDING',
accNumber: string,
balance: number
) => {
const payload = {
accountType,
accNumber,
balance
}
await authInterceptor
.post('/accounts', payload)
.then((response) => {
console.log('Success:', response.data)
})
.catch((error) => {
const axiosError = error as AxiosError
errorMessage.value =
(axiosError.response?.data as string) ||
'An error occurred while posting account'
console.error('Error posting account:', errorMessage.value)
})
}
const postUserConfig = () => {
const payload = {
experience: experience.value,
motivation: motivation.value,
challengeTypeConfigs: Array.from(challengeTypeConfigs.value)
}
authInterceptor
.post('/config/challenge', payload)
.then((response) => {
console.log('Success:', response.data)
})
.catch((error) => {
const axiosError = error as AxiosError
errorMessage.value =
(axiosError.response?.data as string) ||
'An error occurred while updating configuration'
console.error('Error updating configuration:', errorMessage.value)
})
}
return {
role,
experience,
motivation,
challengeTypeConfigs,
accounts,
errorMessage,
setExperience,
setMotivation,
addChallengeTypeConfig,
postAccount,
postUserConfig
}
})
......@@ -46,13 +46,13 @@
</template>
<script setup lang="ts">
import { computed, ref } from 'vue'
import { useAccountStore } from '@/stores/accountStore'
import { ref, computed } from 'vue'
import { useUserConfigStore } from '@/stores/userConfigStore'
import ContinueButtonComponent from '@/components/ContinueButtonComponent.vue'
import router from '@/router'
const MAX_DIGITS = 11
const accountStore = useAccountStore()
const userConfig = useUserConfigStore()
const spendingAccount = ref('')
const savingsAccount = ref('')
......@@ -68,9 +68,8 @@ 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 userConfig.postAccount('SAVING', savingAccountNumber, 0)
await userConfig.postAccount('SPENDING', spendingAccountNumber, 0)
await router.push({ name: 'home' })
}
......
......@@ -17,6 +17,7 @@
</div>
<savings-path :challenges="challenges" :goal="goal"></savings-path>
</div>
<GeneratedChallengesModal v-if="showModal" />
</template>
<script setup lang="ts">
......@@ -28,6 +29,9 @@ import type { Goal } from '@/types/goal'
import { useGoalStore } from '@/stores/goalStore'
import { useChallengeStore } from '@/stores/challengeStore'
import SavingsPath from '@/components/SavingsPath.vue'
import GeneratedChallengesModal from '@/components/GeneratedChallengesModal.vue'
const showModal = ref(true)
const goalStore = useGoalStore()
const challengeStore = useChallengeStore()
......
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