diff --git a/cypress/e2e/homeView.cy.ts b/cypress/e2e/homeView.cy.ts index c36a497515644ec32fde9cce49a6e3f78850b3ae..8ddc7675b8e2f8c39782ff114013650c1f9a9138 100644 --- a/cypress/e2e/homeView.cy.ts +++ b/cypress/e2e/homeView.cy.ts @@ -14,6 +14,13 @@ describe('Goals and Challenges Page Load', () => { ], }, }).as('fetchGoals'); + // Mock the POST request for renewing the token if it's not implemented in the backend + cy.intercept('POST', '/auth/renewToken', { + statusCode: 200, + body: { + accessToken: 'newlyRenewedAccessToken' + } + }).as('renewToken'); cy.intercept('GET', '/challenges', { statusCode: 200, @@ -24,6 +31,14 @@ describe('Goals and Challenges Page Load', () => { }, }).as('fetchChallenges'); + cy.intercept('GET', '/profile/streak', { + statusCode: 200, + body: { + content: [ + { streak: 1, startDate: "2026-04-29T12:10:38.308Z" }, + ], + }, + }).as('fetchChallenges'); // Visit the component that triggers these requests in `onMounted` cy.visit('/hjem'); }); @@ -31,6 +46,13 @@ describe('Goals and Challenges Page Load', () => { it('loads and displays goals and challenges after onMounted', () => { // Wait for API calls made during `onMounted` to complete cy.wait(['@fetchGoals', '@fetchChallenges']); + // Mock the POST request for renewing the token if it's not implemented in the backend + cy.intercept('POST', '/auth/renewToken', { + statusCode: 200, + body: { + accessToken: 'newlyRenewedAccessToken' + } + }).as('renewToken'); // Check console logs for any errors or warnings that might indicate issues cy.window().then((win) => { diff --git a/src/components/ButtonDisplayStreak.vue b/src/components/ButtonDisplayStreak.vue index d1b43156ed25e464e9dcd839ba359fae18a06df3..d8ee03d0d40f01911f62e6b95172b092fed73313 100644 --- a/src/components/ButtonDisplayStreak.vue +++ b/src/components/ButtonDisplayStreak.vue @@ -1,7 +1,11 @@ <template> <div class="flex flex-col items-center absolute"> <Span class="text-sm text-bold">STREAK</Span> - <button @mouseover="display" @mouseleave="hide" class="bg-transparent delay-200 cursor-pointer"> + <button + @mouseover="display" + @mouseleave="hide" + class="bg-transparent delay-200 cursor-pointer" + > <img src="@/assets/streak.png" alt="streak" class="mx-auto w-6 h-6 md:w-12 md:h-12" /> </button> @@ -9,10 +13,15 @@ v-if="displayStreakCard" class="w-[40vh] h-[20vh]md:w-96 md:h-64 group z-50 bg-opacity-50 overflow-hidden absolute left-0 top-14 md:top-20 flex flex-col justify-evenly text-wrap" > - - <div class=" flex flex-col justify-evenly w-full h-full py-2 px-4 md:py-0 bg-white rounded-2xl border-4 border-green-300" > - <span class=" text-xs md:text-2xl font-bold text-black" - >{{ currentStreak }}{{ currentStreak === 1 ? ' utfodring fullført' : ' utfodringer fullført' }} streak</span + <div + class="flex flex-col justify-evenly w-full h-full py-2 px-4 md:py-0 bg-white rounded-2xl border-4 border-green-300" + > + <span class="text-xs md:text-2xl font-bold text-black" + >{{ currentStreak + }}{{ + currentStreak === 1 ? ' utfodring fullført' : ' utfodringer fullført' + }} + streak</span > <p class="text-black text-xs md:text-1xl md:font-bold"> {{ @@ -21,7 +30,18 @@ : 'Du har ikke fullført en utfordring det siste. Fullfør en nÃ¥ for Ã¥ starte en streak!' }} </p> - <Countdown v-if="screenSize > 768 && currentStreak!>0" class="flex flex-row" countdownSize="1rem" labelSize=".5rem" mainColor="white" secondFlipColor="white" mainFlipBackgroundColor="#30ab0e" secondFlipBackgroundColor='#9af781' :labels="{ days: 'dager', hours: 'timer', minutes: 'min', seconds: 'sek', }" :deadlineISO="deadline"></Countdown> + <Countdown + v-if="screenSize > 768 && currentStreak! > 0" + class="flex flex-row" + countdownSize="1rem" + labelSize=".5rem" + mainColor="white" + secondFlipColor="white" + mainFlipBackgroundColor="#30ab0e" + secondFlipBackgroundColor="#9af781" + :labels="{ days: 'dager', hours: 'timer', minutes: 'min', seconds: 'sek' }" + :deadlineISO="deadline" + ></Countdown> <!-- Row component with horizontal padding and auto margins for centering --> <div class="flex flex-row justify-content-between items-center h-20 w-full mx-auto bg-black-400 gap-4" @@ -29,9 +49,9 @@ <div class="flex flex-1 overflow-x-auto"> <div v-for="index in 6" :key="index" class="min-w-max mx-auto"> <div class="flex flex-col items-center"> - <span class="text-black text-xs md:text-1xl font-bold" - >{{ currentStreak! - ((currentStreak! % 7) - index) }}</span - > + <span class="text-black text-xs md:text-1xl font-bold">{{ + currentStreak! - ((currentStreak! % 7) - index) + }}</span> <!-- Conditional rendering for streak images --> <img v-if="index - 1 < currentStreak! % 7" @@ -55,59 +75,54 @@ </template> <script setup lang="ts"> -import {onMounted, onUnmounted, ref, watch} from 'vue' -import {useUserStore} from "@/stores/userStore"; -import {Countdown} from 'vue3-flip-countdown' - - -const userStore = useUserStore(); -const currentStreak = ref<number>(); -const streakStart = ref<string>(); +import { onMounted, onUnmounted, ref, watch } from 'vue' +import { useUserStore } from '@/stores/userStore' +// @ts-ignore +import { Countdown } from 'vue3-flip-countdown' + +const userStore = useUserStore() +const currentStreak = ref<number>() +const streakStart = ref<string>() const deadline = ref<string>() onMounted(async () => { - await userStore.getUserStreak(); - if (userStore.streak){ - currentStreak.value = userStore.streak?.streak; - streakStart.value = userStore.streak?.streakStart; - deadline.value = userStore.streak?.streakStart; + await userStore.getUserStreak() + if (userStore.streak) { + currentStreak.value = userStore.streak?.streak + streakStart.value = userStore.streak?.streakStart + deadline.value = userStore.streak?.streakStart } console.log('Streak:', currentStreak.value) - window.addEventListener("resize", handleWindowSizeChange); - handleWindowSizeChange(); -}); - + window.addEventListener('resize', handleWindowSizeChange) + handleWindowSizeChange() +}) const screenSize = ref<number>(window.innerWidth) onUnmounted(() => { - window.removeEventListener("resize", handleWindowSizeChange); -}); + window.removeEventListener('resize', handleWindowSizeChange) +}) const handleWindowSizeChange = () => { - screenSize.value = window.innerWidth; -}; + screenSize.value = window.innerWidth +} watch( () => currentStreak.value, (newStreak, oldStreak) => { - if (newStreak !== oldStreak) { - currentStreak.value = newStreak; - console.log('Updated Steak:', currentStreak) - } + if (newStreak !== oldStreak) { + currentStreak.value = newStreak + console.log('Updated Steak:', currentStreak) + } }, { immediate: true } ) - - const displayStreakCard = ref(false) - -const display =() => { - displayStreakCard.value = true; +const display = () => { + displayStreakCard.value = true } -const hide =() => { - displayStreakCard.value = false; +const hide = () => { + displayStreakCard.value = false } - </script> diff --git a/src/components/DisplayInfoForChallengeOrGoal.vue b/src/components/DisplayInfoForChallengeOrGoal.vue index 56931219c7fd08a203dce6c8cb44a03614886541..b20221e248f67478e41e6df76fd9e8459645cc7d 100644 --- a/src/components/DisplayInfoForChallengeOrGoal.vue +++ b/src/components/DisplayInfoForChallengeOrGoal.vue @@ -1,53 +1,87 @@ <template> <button @click="display" class="bg-transparent relative p-0 hover:bg-transparent"> - <img src="@/assets/infoIcon.png" alt="i" class="max-h-4 max-w-4 ml-1" /> + <img src="@/assets/infoIcon.png" alt="i" class="max-h-4 max-w-4 ml-1" /> </button> - <div v-if="displayInfoCard" class="w-[40vh] h-[20vh]md:w-60 md:h-40 group z-50 bg-opacity-50 overflow-hidden absolute mt-8 md:mt-4 md:mr-0 flex flex-col justify-evenly text-wrap"> - <div class="flex flex-col justify-around w-3/4 md:w-full h-[80%] py-2 px-4 md:py-0 bg-white rounded-2xl border-4 border-green-300 overflow-auto"> - <p class="text-base md:text-lg text-wrap text-bold">{{ title.toUpperCase() }}</p> - <p class="text-xs md:text-sm text-wrap mb-2">Beskrivelse: {{ description }}</p> - <p v-if="completion!==100" class="text-xs md:text-sm text-nowrap text-green-800">Utløper om:</p> - <Countdown v-if="completion!==100 && screenSize > 763" class="flex flex-row" countdownSize="1.3rem" labelSize=".8rem" mainColor="white" secondFlipColor="white" mainFlipBackgroundColor="#30ab0e" secondFlipBackgroundColor='#9af781' :labels="{ days: 'dager', hours: 'timer', minutes: 'min', seconds: 'sek', }" :deadlineISO="deadline"></Countdown> - <Countdown v-else-if="completion!==100 && screenSize <= 763" class="flex flex-row" countdownSize="1.0rem" labelSize=".6rem" mainColor="white" secondFlipColor="white" mainFlipBackgroundColor="#30ab0e" secondFlipBackgroundColor='#9af781' :labels="{ days: 'dager', hours: 'timer', minutes: 'min', seconds: 'sek', }" :deadlineISO="deadline"></Countdown> - <p class="text-nowrap text-xs md:text.sm" v-else>Utfordring fullført.<br/> Totalt spart: {{amountSaved}}kr</p> - </div> + <div + v-if="displayInfoCard" + class="w-[40vh] h-[20vh]md:w-60 md:h-40 group z-50 bg-opacity-50 overflow-hidden absolute mt-8 md:mt-4 md:mr-0 flex flex-col justify-evenly text-wrap" + > + <div + class="flex flex-col justify-around w-3/4 md:w-full h-[80%] py-2 px-4 md:py-0 bg-white rounded-2xl border-4 border-green-300 overflow-auto" + > + <p class="text-base md:text-lg text-wrap text-bold">{{ title.toUpperCase() }}</p> + <p class="text-xs md:text-sm text-wrap mb-2">Beskrivelse: {{ description }}</p> + <p v-if="completion !== 100" class="text-xs md:text-sm text-nowrap text-green-800"> + Utløper om: + </p> + <Countdown + v-if="completion !== 100 && screenSize > 763" + class="flex flex-row" + countdownSize="1.3rem" + labelSize=".8rem" + mainColor="white" + secondFlipColor="white" + mainFlipBackgroundColor="#30ab0e" + secondFlipBackgroundColor="#9af781" + :labels="{ days: 'dager', hours: 'timer', minutes: 'min', seconds: 'sek' }" + :deadlineISO="deadline" + ></Countdown> + <Countdown + v-else-if="completion !== 100 && screenSize <= 763" + class="flex flex-row" + countdownSize="1.0rem" + labelSize=".6rem" + mainColor="white" + secondFlipColor="white" + mainFlipBackgroundColor="#30ab0e" + secondFlipBackgroundColor="#9af781" + :labels="{ days: 'dager', hours: 'timer', minutes: 'min', seconds: 'sek' }" + :deadlineISO="deadline" + ></Countdown> + <p class="text-nowrap text-xs md:text.sm" v-else> + Utfordring fullført.<br /> + Totalt spart: {{ amountSaved }}kr + </p> + </div> </div> </template> <script setup lang="ts"> -import type {Challenge} from "@/types/challenge"; -import type {Goal} from "@/types/goal"; -import {onUnmounted, ref} from "vue"; -import {Countdown} from 'vue3-flip-countdown' +import type { Challenge } from '@/types/challenge' +import type { Goal } from '@/types/goal' +import { onUnmounted, ref } from 'vue' +// @ts-ignore +import { Countdown } from 'vue3-flip-countdown' interface Props { - challenge: Challenge | null | undefined - goal: Goal | null | undefined - isChallenge: boolean + challenge: Challenge | null | undefined + goal: Goal | null | undefined + isChallenge: boolean } const props = defineProps<Props>() -const description = ref<string>(props.isChallenge ? props.challenge!.description : props.goal!.description) +const description = ref<string>( + props.isChallenge ? props.challenge!.description : props.goal!.description +) const title = ref<string>(props.isChallenge ? props.challenge!.title : props.goal!.title) const amountSaved = ref<number>(props.isChallenge ? props.challenge!.saved : props.goal!.saved) -const completion = ref<number>(props.isChallenge ? (props.challenge?.completion ?? 0) : (props.goal?.completion ?? 0)); +const completion = ref<number>( + props.isChallenge ? props.challenge?.completion ?? 0 : props.goal?.completion ?? 0 +) const deadline = ref<string>(props.isChallenge ? props.challenge!.due : props.goal!.due) const displayInfoCard = ref(false) - -const display =() => { - displayInfoCard.value = !displayInfoCard.value +const display = () => { + displayInfoCard.value = !displayInfoCard.value } const screenSize = ref<number>(window.innerWidth) onUnmounted(() => { - window.removeEventListener("resize", handleWindowSizeChange); -}); + window.removeEventListener('resize', handleWindowSizeChange) +}) const handleWindowSizeChange = () => { - screenSize.value = window.innerWidth; -}; - - + screenSize.value = window.innerWidth +} </script> diff --git a/src/components/ImgGifTemplate.vue b/src/components/ImgGifTemplate.vue index c4d06b434a0f6a87f09a6d84a6b63287a0ef5840..83144b10dfe1236438f8730e19a23cbbbd610c01 100644 --- a/src/components/ImgGifTemplate.vue +++ b/src/components/ImgGifTemplate.vue @@ -1,18 +1,18 @@ <template> - <img - v-if="index%6===modValue" - :src="url" - alt="could not load" - class="h-32 w-32 border-2 rounded-lg border-stale-400" - /> + <img + v-if="index % 6 === modValue" + :src="url" + alt="could not load" + class="h-32 w-32 border-2 rounded-lg border-stale-400" + /> </template> <script setup lang="ts"> interface Props { - url: string - index: number - modValue: number + url: string + index: number + modValue: number } -const props = defineProps<Props>() +defineProps<Props>() </script> diff --git a/src/components/NavBarComponent.vue b/src/components/NavBarComponent.vue index 397917f0e5facfbba83f520e6b6d1db07776c66f..702b5fafc282c519dd01d6ac3e19a9391138ec97 100644 --- a/src/components/NavBarComponent.vue +++ b/src/components/NavBarComponent.vue @@ -10,7 +10,7 @@ </router-link> <div class="flex flex-row justify-center"> - <ButtonDisplayStreak></ButtonDisplayStreak> + <ButtonDisplayStreak></ButtonDisplayStreak> </div> </div> <div v-if="!isHamburger" class="flex flex-row gap-10"> @@ -67,7 +67,7 @@ import { RouterLink } from 'vue-router' import { onMounted, ref } from 'vue' import { useUserStore } from '@/stores/userStore' import ModalComponent from '@/components/ModalComponent.vue' -import ButtonDisplayStreak from "@/components/ButtonDisplayStreak.vue"; +import ButtonDisplayStreak from '@/components/ButtonDisplayStreak.vue' const userStore = useUserStore() diff --git a/src/components/SavingsPath.vue b/src/components/SavingsPath.vue index f23676f1827c6d148efcd76194c30b0598e6c091..9bf0500ce61e30f6a9eef946f6bc92f62254b69a 100644 --- a/src/components/SavingsPath.vue +++ b/src/components/SavingsPath.vue @@ -12,7 +12,7 @@ <div class="h-1 w-4/6 mx-auto my-2 opacity-10"></div> <div ref="containerRef" - class="container relative pt-6 w-4/5 mx-auto md:w-4/5 no-scrollbar h-full max-h-[60vh] md:max-h-[60v] md:min-w-2/5 overflow-y-auto border-2 border-slate-300 rounded-lg bg-white shadow-lg" + class="container relative pt-6 w-4/5 mx-auto md:w-4/5 no-scrollbar h-full max-h-[60vh] md:max-h-[60v] md:min-w-2/5 overflow-y-auto border-2 border-slate-300 rounded-lg bg-white shadow-lg" > <div> <img src="@/assets/start.png" alt="Spare" class="md:w-1/6 md:h-auto h-20" /> @@ -31,84 +31,121 @@ class="flex flex-row w-4/5 gap-8" > <div class="right-auto just"> - <img-gif-template :index="index" :mod-value="1" url="src/assets/golfSpare.gif"></img-gif-template> - <img-gif-template :index="index" :mod-value="3" url="src/assets/sleepingSpare.gif"></img-gif-template> - <img-gif-template :index="index" :mod-value="5" url="src/assets/archerSpare.gif"></img-gif-template> + <img-gif-template + :index="index" + :mod-value="1" + url="src/assets/golfSpare.gif" + ></img-gif-template> + <img-gif-template + :index="index" + :mod-value="3" + url="src/assets/sleepingSpare.gif" + ></img-gif-template> + <img-gif-template + :index="index" + :mod-value="5" + url="src/assets/archerSpare.gif" + ></img-gif-template> </div> <!-- Challenge Icon and Details --> <card-template> - <div class="flex"> - <!-- Challenge Icon --> - <div class="flex flex-col items-center gap-4"> - <div class="flex flex-row flex-nowrap"> - <p class="text-center text-wrap text-xs md:text-lg" data-cy="challenge-title"> - {{ challenge.title }} - </p> - <display-info-for-challenge-or-goal :goal="goal" :challenge="challenge" :is-challenge="true"></display-info-for-challenge-or-goal> - </div> - <img - @click="editChallenge(challenge)" - :data-cy="'challenge-icon-' + challenge.id" - :src="getChallengeIcon(challenge)" - class="max-w-20 max-h-20 cursor-pointer" - :alt="challenge.title" - /> - <!-- Progress Bar, if the challenge is not complete --> - <div - v-if=" - challenge.completion != undefined && challenge.completion < 100 - " - class="flex-grow w-full mt-2" - > - <div class="flex flex-row ml-5 md:ml-10 justify-center"> - <div class="flex flex-col"> - <div - class="bg-gray-200 rounded-full h-2.5 dark:bg-gray-700" - > - <div - class="bg-green-600 h-2.5 rounded-full" - data-cy="challenge-progress" - :style="{ - width: - (challenge.saved / challenge.target) * 100 + - '%' - }" - ></div> - </div> - <div class="text-center text-xs md:text-base"> - {{ challenge.saved }}kr / {{ challenge.target }}kr - </div> - </div> - - <button - @click="incrementSaved(challenge)" - :data-cy="'increment-challenge' + challenge.id" - type="button" - class="inline-block mb-2 ml-2 h-7 w-8 rounded-full p-1 uppercase leading-normal transition duration-150 ease-in-out focus:bg-green-accent-300 focus:shadow-green-2 focus:outline-none focus:ring-0 active:bg-green-600 active:shadow-green-200 motion-reduce:transition-none dark:shadow-black/30 dark:hover:shadow-dark-strong dark:focus:shadow-dark-strong dark:active:shadow-dark-strong" - > - + - </button> - </div> - </div> - <span v-else class="text-center text-xs md:text-base">Ferdig: {{ challenge.saved }}</span> - </div> - <!-- Check Icon --> - <div - v-if="challenge.completion !== undefined && challenge.completion >= 100" - class="md:max-w-10 min-w-4 max-w-6 max-h-6 w-full h-auto md:max-h-10 min-h-4" - > - <img src="@/assets/completed.png" alt="" />ï¸ - </div> - <div v-else class="max-w-6 max-h-6"> - <img src="@/assets/pending.png" alt="" />ï¸ - </div> - </div> + <div class="flex"> + <!-- Challenge Icon --> + <div class="flex flex-col items-center gap-4"> + <div class="flex flex-row flex-nowrap"> + <p + class="text-center text-wrap text-xs md:text-lg" + data-cy="challenge-title" + > + {{ challenge.title }} + </p> + <display-info-for-challenge-or-goal + :goal="goal" + :challenge="challenge" + :is-challenge="true" + ></display-info-for-challenge-or-goal> + </div> + <img + @click="editChallenge(challenge)" + :data-cy="'challenge-icon-' + challenge.id" + :src="getChallengeIcon(challenge)" + class="max-w-20 max-h-20 cursor-pointer" + :alt="challenge.title" + /> + <!-- Progress Bar, if the challenge is not complete --> + <div + v-if=" + challenge.completion != undefined && + challenge.completion < 100 + " + class="flex-grow w-full mt-2" + > + <div class="flex flex-row ml-5 md:ml-10 justify-center"> + <div class="flex flex-col"> + <div + class="bg-gray-200 rounded-full h-2.5 dark:bg-gray-700" + > + <div + class="bg-green-600 h-2.5 rounded-full" + data-cy="challenge-progress" + :style="{ + width: + (challenge.saved / challenge.target) * + 100 + + '%' + }" + ></div> + </div> + <div class="text-center text-xs md:text-base"> + {{ challenge.saved }}kr / {{ challenge.target }}kr + </div> + </div> + + <button + @click="incrementSaved(challenge)" + :data-cy="'increment-challenge' + challenge.id" + type="button" + class="inline-block mb-2 ml-2 h-7 w-8 rounded-full p-1 uppercase leading-normal transition duration-150 ease-in-out focus:bg-green-accent-300 focus:shadow-green-2 focus:outline-none focus:ring-0 active:bg-green-600 active:shadow-green-200 motion-reduce:transition-none dark:shadow-black/30 dark:hover:shadow-dark-strong dark:focus:shadow-dark-strong dark:active:shadow-dark-strong" + > + + + </button> + </div> + </div> + <span v-else class="text-center text-xs md:text-base" + >Ferdig: {{ challenge.saved }}</span + > + </div> + <!-- Check Icon --> + <div + v-if=" + challenge.completion !== undefined && + challenge.completion >= 100 + " + class="md:max-w-10 min-w-4 max-w-6 max-h-6 w-full h-auto md:max-h-10 min-h-4" + > + <img src="@/assets/completed.png" alt="" />ï¸ + </div> + <div v-else class="max-w-6 max-h-6"> + <img src="@/assets/pending.png" alt="" />ï¸ + </div> + </div> </card-template> <div class=""> - <img-gif-template :index="index" :mod-value="0" url="src/assets/cowboySpare.gif"></img-gif-template> - <img-gif-template :index="index" :mod-value="2" url="src/assets/hotAirBalloonSpare.gif"></img-gif-template> - <img-gif-template :index="index" :mod-value="4" url="src/assets/farmerSpare.gif"></img-gif-template> - + <img-gif-template + :index="index" + :mod-value="0" + url="src/assets/cowboySpare.gif" + ></img-gif-template> + <img-gif-template + :index="index" + :mod-value="2" + url="src/assets/hotAirBalloonSpare.gif" + ></img-gif-template> + <img-gif-template + :index="index" + :mod-value="4" + url="src/assets/farmerSpare.gif" + ></img-gif-template> </div> </div> <!-- Piggy Steps, centered --> @@ -144,11 +181,16 @@ <!-- Goal --> <div v-if="goal" class="flex flex-row justify-around m-t-2 pt-6 w-full mx-auto"> <div class="grid grid-rows-2 grid-flow-col gap 4"> - <div class="row-span-3 cursor-pointer" @click="editGoal(goal)"> - <img :src="getGoalIcon(goal)" class="w-12 h-12 mx-auto" :alt="goal.title" /> - <div class="text-lg font-bold" data-cy="goal-title">{{ goal.title }}</div> - </div> - <display-info-for-challenge-or-goal class="col-span-2" :goal="goal" :challenge="null" :is-challenge="false"></display-info-for-challenge-or-goal> + <div class="row-span-3 cursor-pointer" @click="editGoal(goal)"> + <img :src="getGoalIcon(goal)" class="w-12 h-12 mx-auto" :alt="goal.title" /> + <div class="text-lg font-bold" data-cy="goal-title">{{ goal.title }}</div> + </div> + <display-info-for-challenge-or-goal + class="col-span-2" + :goal="goal" + :challenge="null" + :is-challenge="false" + ></display-info-for-challenge-or-goal> </div> <div class="flex flex-col items-end"> <div @click="goToEditGoal" class="cursor-pointer"> @@ -173,7 +215,7 @@ </template> <script setup lang="ts"> -import {nextTick, onMounted, onUnmounted, type Ref, ref, watch} from 'vue' +import { nextTick, onMounted, onUnmounted, type Ref, ref, watch } from 'vue' import anime from 'animejs' import type { Challenge } from '@/types/challenge' import type { Goal } from '@/types/goal' @@ -181,9 +223,9 @@ import confetti from 'canvas-confetti' import { useRouter } from 'vue-router' import { useGoalStore } from '@/stores/goalStore' import { useChallengeStore } from '@/stores/challengeStore' -import DisplayInfoForChallengeOrGoal from "@/components/DisplayInfoForChallengeOrGoal.vue"; -import CardTemplate from "@/views/CardTemplate.vue"; -import ImgGifTemplate from "@/components/ImgGifTemplate.vue"; +import DisplayInfoForChallengeOrGoal from '@/components/DisplayInfoForChallengeOrGoal.vue' +import CardTemplate from '@/views/CardTemplate.vue' +import ImgGifTemplate from '@/components/ImgGifTemplate.vue' const router = useRouter() const goalStore = useGoalStore() @@ -200,34 +242,35 @@ const goal = ref<Goal | null | undefined>(props.goal) onMounted(async () => { await goalStore.getUserGoals() - window.addEventListener("resize", handleWindowSizeChange); - handleWindowSizeChange(); - sortChallenges(); -}); + window.addEventListener('resize', handleWindowSizeChange) + handleWindowSizeChange() + sortChallenges() +}) const sortChallenges = () => { - challenges.value.sort((a, b) => { - // First, sort by completion status: non-completed (less than 100) before completed (100) - if (a.completion !== 100 && b.completion === 100) { - return 1; // 'a' is not completed and 'b' is completed, 'a' should come first - } else if (a.completion === 100 && b.completion !== 100) { - return -1; // 'a' is completed and 'b' is not, 'b' should come first - } else { - // If both are completed or both are not completed, sort by the due date - return new Date(a.due) - new Date(b.due); - } - }); + challenges.value.sort((a, b) => { + // First, sort by completion status: non-completed (less than 100) before completed (100) + if (a.completion !== 100 && b.completion === 100) { + return 1 // 'a' is not completed and 'b' is completed, 'a' should come first + } else if (a.completion === 100 && b.completion !== 100) { + return -1 // 'a' is completed and 'b' is not, 'b' should come first + } else { + // Explicitly convert dates to numbers for subtraction + const dateA = new Date(a.due).getTime() + const dateB = new Date(b.due).getTime() + return dateA - dateB + } + }) } - const screenSize = ref<number>(window.innerWidth) onUnmounted(() => { - window.removeEventListener("resize", handleWindowSizeChange); -}); + window.removeEventListener('resize', handleWindowSizeChange) +}) const handleWindowSizeChange = () => { - screenSize.value = window.innerWidth; -}; + screenSize.value = window.innerWidth +} // Utilizing watch to specifically monitor for changes in the props watch( @@ -246,7 +289,7 @@ watch( (newChallenges, oldChallenges) => { if (newChallenges !== oldChallenges) { challenges.value = newChallenges - sortChallenges(); + sortChallenges() console.log('Updated challenges:', challenges.value) } }, diff --git a/src/stores/challengeStore.ts b/src/stores/challengeStore.ts index 8f2b5603ae4423473434764106746ba8dc560876..a8417aa62bf51495a6a06b401145b0fe06f34a06 100644 --- a/src/stores/challengeStore.ts +++ b/src/stores/challengeStore.ts @@ -43,7 +43,10 @@ export const useChallengeStore = defineStore('challenge', () => { } const completeUserChallenge = async (challenge: Challenge) => { try { - const response = await authInterceptor.put(`/challenges/${challenge.id}/complete`, challenge) + const response = await authInterceptor.put( + `/challenges/${challenge.id}/complete`, + challenge + ) if (response.data) { // Update local challenge state to reflect changes const index = challenges.value.findIndex((c) => c.id === challenge.id) diff --git a/src/stores/userStore.ts b/src/stores/userStore.ts index e441008fea5b04d234fca42a9c253a21ce2b1590..a54494ddbd27e1539d5f97b71637995a4a7f8c32 100644 --- a/src/stores/userStore.ts +++ b/src/stores/userStore.ts @@ -4,8 +4,8 @@ import type { User } from '@/types/user' import router from '@/router' import type { AxiosError } from 'axios' import axios from 'axios' -import authInterceptor from "@/services/authInterceptor"; -import type {Streak} from "@/types/streak"; +import authInterceptor from '@/services/authInterceptor' +import type { Streak } from '@/types/streak' import type { CredentialRequestOptions } from '@/types/CredentialRequestOptions' import { base64urlToUint8array, initialCheckStatus, uint8arrayToBase64url } from '@/util' import type { CredentialCreationOptions } from '@/types/CredentialCreationOptions' @@ -88,7 +88,7 @@ export const useUserStore = defineStore('user', () => { streak.value = response.data console.log('Fetched Challenges:', streak.value) } else { - streak.value = undefined; + streak.value = undefined console.error('No challenge content found:', response.data) } } catch (error) { @@ -238,6 +238,6 @@ export const useUserStore = defineStore('user', () => { bioRegister, errorMessage, getUserStreak, - streak, + streak } }) diff --git a/src/types/streak.ts b/src/types/streak.ts index 79ac54fed944addd0c7943919b0d50ee84c550ff..61dafae24c954c7ca99a3596ca43a659f1ef122c 100644 --- a/src/types/streak.ts +++ b/src/types/streak.ts @@ -1,4 +1,4 @@ export interface Streak { - streakStart?: string, - streak?: number, -} \ No newline at end of file + streakStart?: string + streak?: number +}