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

Merge remote-tracking branch 'origin/dev' into feat/46-manage-challenge-configuration

parents 6c66dc7c a80afdca
No related branches found
No related tags found
3 merge requests!66Final merge,!48Feat/46 manage challenge configuration,!4Pipeline fix
......@@ -25,20 +25,25 @@
import { defineProps, ref } from 'vue'
import { useRouter } from 'vue-router'
interface Props {
buttonText: string
type: 'goal' | 'challenge'
}
const props = defineProps({
buttonText: String,
type: String,
showModal: Boolean
})
const emit = defineEmits(['update:showModal'])
const router = useRouter()
const props = defineProps<Props>()
const btnText = ref(props.buttonText)
const routeToGoalOrChallenge = () => {
if (props.type === 'goal') {
router.push('/sparemaal')
} else {
} else if (props.type === 'challenge') {
router.push('/spareutfordringer')
} else if (props.type === 'generatedChallenge') {
emit('update:showModal', true)
}
}
</script>
<template>
<div
v-if="generatedChallenges.length > 0"
class="fixed inset-0 bg-gray-300 bg-opacity-75 flex justify-center items-center"
v-if="showModal"
class="fixed inset-0 flex items-center justify-center bg-black bg-opacity-50 z-50"
>
<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"
>
<div class="relative bg-white pt-10 p-4 rounded-lg shadow-xl" style="width: 40rem">
<button @click="closeModal" class="absolute top-0 right-0 m-2 text-white">
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-6 w-6"
......@@ -23,112 +20,133 @@
/>
</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"
<div v-if="generatedChallenges.length > 0">
<div class="text-center font-bold text-3xl mb-4 mt-2">
Personlig tilpassede spareutfordringer:
</div>
<div class="grid grid-cols-7 sm:grid-cols-11 gap-2 p-3 pb-1 border-b-2">
<span class="font-bold col-span-2 md:col-span-3 sm:text-lg pt-1 mb-0"
>Tittel</span
>
<span class="font-bold col-span-2 md:col-span-2 sm:text-lg pt-1 mb-0"
>Målsum</span
>
<span
class="font-bold col-span-2 md:col-span-1 sm:text-lg pt-1 pr-1 md:pr-3 mb-0"
>Frist</span
>
<span class="col-span-2"></span>
</div>
<div class="space-y-2">
<div
v-for="(challenge, index) in generatedChallenges"
:key="index"
:class="{ 'bg-gray-100': index % 2 === 0 }"
class="grid grid-cols-7 md:grid-cols-7 sm:grid-cols-2 lg:grid-cols-7 gap-4 items-center border p-3 rounded mt-[-8px]"
>
<span class="break-words col-span-2 md:col-span-1 lg:col-span-2 text-lg">{{
challenge.title
}}</span>
<span class="col-span-2 md:col-span-2 lg:col-span-1 text-lg">{{
challenge.target
}}</span>
<span class="col-span-2 md:col-span-1 lg:col-span-2 text-lg">{{
challenge.due
}}</span>
<div
class="col-span-7 sm:col-start-3 sm:col-span-2 md:col-span-2 lg:col-span-2 flex items-center justify-end space-x-2"
>
Godta
</button>
<span v-if="challenge.isAccepted" class="font-bold text-lg"
>Godtatt!</span
>
<button
@click="acceptChallenge(challenge)"
class="text-white font-bold py-1 px-4 mt-[-14px] sm:mt-0"
>
Godta
</button>
</div>
</div>
</div>
</div>
<div v-else class="text-center text-2xl font-bold mt-1">
Ingen nye spareutfordringer enda ... sjekk igjen senere!
</div>
</div>
</div>
</template>
<script setup>
import { onMounted, ref } from 'vue'
<script setup lang="ts">
import { ref, reactive, onMounted } from 'vue'
import authInterceptor from '@/services/authInterceptor'
import { useChallengeStore } from '@/stores/challengeStore'
import type { AxiosResponse } from 'axios'
interface Challenge {
title: string
target: number
due: string
dueFull: string
isAccepted: boolean
perPurchase?: number
description?: string
type?: string
}
const generatedChallenges = ref([])
const showModal = ref(true)
const generatedChallenges = reactive<Challenge[]>([])
const fetchGeneratedChallenges = async () => {
async function fetchGeneratedChallenges() {
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)
}))
const response: AxiosResponse = await authInterceptor.get('/challenges/generate')
if (response.status === 200) {
generatedChallenges.splice(
0,
generatedChallenges.length,
...response.data.map((ch: any) => ({
...ch,
due: new Date(ch.due).toISOString().split('T')[0],
dueFull: ch.due,
isAccepted: false
}))
)
} else {
console.error('No challenges found for the user.')
generatedChallenges.value = []
generatedChallenges.splice(0, generatedChallenges.length)
}
} catch (error) {
console.error('Error fetching challenges:', error)
generatedChallenges.value = []
}
}
onMounted(() => {
fetchGeneratedChallenges()
localStorage.setItem('lastModalShow', Date.now().toString())
})
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(challenge: Challenge) {
if (!challenge) {
console.error('No challenge data provided to acceptChallenge function.')
return
}
}
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 postData = {
title: challenge.title,
saved: 0,
target: challenge.target,
perPurchase: challenge.perPurchase,
description: challenge.description,
due: challenge.dueFull,
type: challenge.type
}
authInterceptor
.post('/challenges', postData)
.then((response: AxiosResponse) => {
challenge.isAccepted = true
})
.catch((error) => {
console.error('Failed to save challenge:', error)
})
}
const closeModal = () => {
generatedChallenges.value = []
showModal.value = false
}
</script>
......@@ -22,6 +22,13 @@
:buttonText="'Legg til spareutfordring'"
:type="'challenge'"
/>
<ButtonAddGoalOrChallenge
:buttonText="'Generer spareutfordring'"
:type="'generatedChallenge'"
:showModal="showModal"
@click="showModal = true"
@update:showModal="showModal = $event"
/>
</div>
</div>
<savings-path :challenges="challenges" :goal="goal"></savings-path>
......@@ -38,6 +45,7 @@
<img alt="Hjelp" class="w-1/12" src="@/assets/hjelp.png" />
</div>
</div>
<GeneratedChallengesModal v-show="showModal" @update:showModal="showModal = $event" />
</template>
<script setup lang="ts">
......@@ -50,8 +58,9 @@ import { useGoalStore } from '@/stores/goalStore'
import { useChallengeStore } from '@/stores/challengeStore'
import SavingsPath from '@/components/SavingsPath.vue'
import router from '@/router'
import GeneratedChallengesModal from '@/components/GeneratedChallengesModal.vue'
const showModal = ref(true)
const showModal = ref(false)
const goalStore = useGoalStore()
const challengeStore = useChallengeStore()
......@@ -70,10 +79,15 @@ onMounted(async () => {
challenges.value = challengeStore.challenges
goals.value = goalStore.goals
goal.value = goals.value[0]
console.log('Goals:', goals.value)
const lastModalShow = localStorage.getItem('lastModalShow')
if (!lastModalShow || Date.now() - Number(lastModalShow) >= 24 * 60 * 60 * 1000) {
showModal.value = true
}
firstLoggedInSpeech()
})
// Check if the user is logging in for the first time, and display the first login speech
const firstLoggedInSpeech = () => {
const isFirstLogin = router.currentRoute.value.query.firstLogin === 'true'
if (isFirstLogin) {
......@@ -87,6 +101,13 @@ const firstLoggedInSpeech = () => {
}
}
// Define your speech array
const speechArray = [
'Hei! Jeg er Sparemannen.',
'Jeg hjelper deg med å spare penger.',
'Klikk på meg for å høre mer.'
]
const openInteractiveSpare = () => {
// Check if there's new speech available before opening the modal.
if (newSpeechAvailable.value) {
......
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