diff --git a/src/components/GeneratedChallengesModal.vue b/src/components/GeneratedChallengesModal.vue new file mode 100644 index 0000000000000000000000000000000000000000..0d5bd1a75fc3191ad75186178e66f42d169fe900 --- /dev/null +++ b/src/components/GeneratedChallengesModal.vue @@ -0,0 +1,134 @@ +<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> diff --git a/src/stores/accountStore.ts b/src/stores/accountStore.ts deleted file mode 100644 index b80263eafa362581f6c2e235d544456ad887274c..0000000000000000000000000000000000000000 --- a/src/stores/accountStore.ts +++ /dev/null @@ -1,35 +0,0 @@ -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' - } - } - } -}) diff --git a/src/stores/userConfigStore.ts b/src/stores/userConfigStore.ts index 6f1e0f1ccbe25f90f463aed9ef97ec9b63ac4319..c6c0ba84ff9725f568cdcad154af537ab3d29b80 100644 --- a/src/stores/userConfigStore.ts +++ b/src/stores/userConfigStore.ts @@ -1,52 +1,96 @@ -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 } }) diff --git a/src/views/ConfigAccountNumberView.vue b/src/views/ConfigAccountNumberView.vue index 652cf0d11ba68e92f47d8b6e14171faa565cf321..7841aa5e7c6f7c4e510a2c381e180cf3df700c33 100644 --- a/src/views/ConfigAccountNumberView.vue +++ b/src/views/ConfigAccountNumberView.vue @@ -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' }) } diff --git a/src/views/HomeView.vue b/src/views/HomeView.vue index f596bcaee5dac3d20b57091ba4c959c50580dc9e..b7c9f94852d11bf552a9b4a6d603accc819be7e8 100644 --- a/src/views/HomeView.vue +++ b/src/views/HomeView.vue @@ -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()