diff --git a/spec.json b/spec.json index d22821d7fd6ecd38fdd014cc2859ec7fcbe472f1..5cb3ddffdb672c18b8376ff8d1823378c1381155 100644 --- a/spec.json +++ b/spec.json @@ -2383,6 +2383,10 @@ "type": "integer", "format": "int64" }, + "bannerImage": { + "type": "integer", + "format": "int64" + }, "email": { "type": "string" }, @@ -2395,6 +2399,14 @@ }, "subscriptionLevel": { "type": "string" + }, + "checkingAccountBBAN": { + "type": "integer", + "format": "int64" + }, + "savingsAccountBBAN": { + "type": "integer", + "format": "int64" } } }, @@ -2606,9 +2618,21 @@ "type": "integer", "format": "int64" }, + "bannerImage": { + "type": "integer", + "format": "int64" + }, "createdAt": { "type": "string", "format": "date-time" + }, + "totalPoints": { + "type": "integer", + "format": "int32" + }, + "currentStreak": { + "type": "integer", + "format": "int32" } } }, diff --git a/src/api/models/ProfileDTO.ts b/src/api/models/ProfileDTO.ts index 4afa17a7faad4191cabd0e5be6450a80507779c1..f3ae4cb44568b5ceba6bd9fe2e611db7a7b42eea 100644 --- a/src/api/models/ProfileDTO.ts +++ b/src/api/models/ProfileDTO.ts @@ -7,6 +7,9 @@ export type ProfileDTO = { firstName?: string; lastName?: string; profileImage?: number; + bannerImage?: number; createdAt?: string; + totalPoints?: number; + currentStreak?: number; }; diff --git a/src/api/models/UserDTO.ts b/src/api/models/UserDTO.ts index 2020ee7c36cf2f4a1a8304f61f398d76c8d5cda2..c7278e15f4e19c8ed1ce3e836fe3840419268bec 100644 --- a/src/api/models/UserDTO.ts +++ b/src/api/models/UserDTO.ts @@ -7,9 +7,12 @@ export type UserDTO = { firstName?: string; lastName?: string; profileImage?: number; + bannerImage?: number; email?: string; createdAt?: string; role?: string; subscriptionLevel?: string; + checkingAccountBBAN?: number; + savingsAccountBBAN?: number; }; diff --git a/src/assets/loadingPig.png b/src/assets/loadingPig.png new file mode 100644 index 0000000000000000000000000000000000000000..d92b61b238d8684b57208a2bc94b951598e320ca Binary files /dev/null and b/src/assets/loadingPig.png differ diff --git a/src/assets/savingPigRun.png b/src/assets/savingPigRun.png new file mode 100644 index 0000000000000000000000000000000000000000..d7ad033c8220621dbb2f34547c8925387adbc17d Binary files /dev/null and b/src/assets/savingPigRun.png differ diff --git a/src/components/SavingGoal/SavingGoal.vue b/src/components/SavingGoal/SavingGoal.vue index 3644980546960ef148747fa116d97ccdade6a8a6..73b63403e45151d2dce1b24f6da6b60340f1d81e 100644 --- a/src/components/SavingGoal/SavingGoal.vue +++ b/src/components/SavingGoal/SavingGoal.vue @@ -2,18 +2,21 @@ import SavingGoalList from "@/components/SavingGoal/SavingGoalList.vue"; import SavingGoalRoadmap from "@/components/SavingGoal/SavingGoalRoadmap.vue"; import SavingGoalCreate from "@/components/SavingGoal/SavingGoalCreate.vue"; +import SavingGoalDefault from "@/components/SavingGoal/SavingGoalDefault.vue"; import type {GoalDTO} from "@/api"; import {GoalService} from "@/api"; export default { - components: {SavingGoalCreate, SavingGoalRoadmap, SavingGoalList}, + components: {SavingGoalDefault, SavingGoalCreate, SavingGoalRoadmap, SavingGoalList}, data() { return { bluePanelMaxHeight: 'auto' as string, - createClicked: true as boolean, + createClicked: false as boolean, + savingGoalClicked: false as boolean, selectedGoal: [] as any, createdGoal: [] as any, key: 0 as number, + keyForList: 0 as number, }; }, mounted() { @@ -26,7 +29,8 @@ export default { if (timelineElement instanceof HTMLElement) { // Calculate the max-height based on the height of the timeline const timelineHeight = timelineElement.offsetHeight; - this.bluePanelMaxHeight = '700px'; + console.log(timelineHeight) + this.bluePanelMaxHeight = (timelineHeight * 1.5)+'px'; } else { this.bluePanelMaxHeight = '700px'; } @@ -36,16 +40,24 @@ export default { }, async goToSavingGoal(savingGoal: GoalDTO) { this.$emit('goToSavingGoal', savingGoal); - let response = await GoalService.getGoal({id: savingGoal.id as number}); - console.log(response) - this.selectedGoal = response + this.selectedGoal = await GoalService.getGoal({id: savingGoal.id as number}) this.createClicked = false; + this.savingGoalClicked = true; this.key++ + setTimeout(() => { + this.calculateBluePanelMaxHeight() + }, 500); }, - createSavingGoal(savingGoal: GoalDTO) { - this.$emit('createSavingGoal', savingGoal) - this.createdGoal = savingGoal; - this.createClicked = false; + async handleCreateGoalClicked(savingGoal: GoalDTO) { + this.$emit('goToSavingGoal', savingGoal); + let response = await GoalService.getGoal({id: savingGoal.id as number}); + setTimeout(() => { + this.selectedGoal = response + this.createClicked = false; + this.key++ + this.savingGoalClicked = true; + this.keyForList++ + }, 100); } }, }; @@ -55,15 +67,16 @@ export default { <div class="cont"> <div class="row"> <div class="col-lg-4 blue-background overflow-scroll" :style="{ 'max-height': bluePanelMaxHeight }"> - <h3 style="color: white; margin-bottom: 16px">Your saving goals</h3> + <h2>Dine sparemål</h2> <div> - <button class="btn btn-success btn-lg" style="font-weight: 600; margin-bottom: 20px" @click="createGoal">Create new saving goal</button> + <button class="btn btn-success btn-lg" style="font-weight: 600; margin-bottom: 20px" @click="createGoal">+ Lag et nytt sparemål</button> </div> - <saving-goal-list @goToSavingGoal="goToSavingGoal"></saving-goal-list> + <saving-goal-list :key="keyForList" @goToSavingGoal="goToSavingGoal"></saving-goal-list> </div> <div class="spacer"/> - <saving-goal-create @createGoalClick="createSavingGoal" v-if="createClicked"></saving-goal-create> - <saving-goal-roadmap :key="key" :selected-goal="selectedGoal" v-else></saving-goal-roadmap> + <saving-goal-create @createGoalClicked="handleCreateGoalClicked" v-if="createClicked"></saving-goal-create> + <saving-goal-roadmap :key="key" :selected-goal="selectedGoal" v-else-if="savingGoalClicked"></saving-goal-roadmap> + <saving-goal-default v-else></saving-goal-default> </div> </div> </template> @@ -82,11 +95,18 @@ export default { padding: 12px; background-color: #003A58; width: 27%; - border-radius: 0 2em 2em 0; + border-radius: 0 1em 1em 0; } .spacer { width: 10%; background-color: transparent; } + +h2 { + color: white; + margin-bottom: 16px; + font-weight: 600; + +} </style> \ No newline at end of file diff --git a/src/components/SavingGoal/SavingGoalCreate.vue b/src/components/SavingGoal/SavingGoalCreate.vue index e5b1d5a99201c6b085e6c8e97cd3783da1dd5120..2fe1e2cdd1914d0792453c465ea2b8789076a673 100644 --- a/src/components/SavingGoal/SavingGoalCreate.vue +++ b/src/components/SavingGoal/SavingGoalCreate.vue @@ -1,6 +1,8 @@ <script setup lang="ts"> import {GoalService, type CreateGoalDTO, type GoalDTO} from "@/api" -import {ref} from "vue"; +import {ref, defineProps, defineEmits} from "vue"; + +const emits = defineEmits(['createGoalClicked']); const name = ref("") const desc = ref("") @@ -10,13 +12,6 @@ const createdConfirm = ref(""); const errorMessage = ref("") const createGoalClick = async () => { - console.log("create goal clicked") - console.log(name.value) - console.log(desc.value) - console.log(date.value) - console.log(amount.value) - - const createGoalPayload: CreateGoalDTO = { name: name.value, description: desc.value, @@ -28,8 +23,9 @@ const createGoalClick = async () => { try { let response = await GoalService.createGoal({ requestBody: createGoalPayload }); if(response.name != "") { - createdConfirm.value = "Your goal has been created! Refresh the page to se it in the list" + createdConfirm.value = "Your goal has been created!" errorMessage.value = "" + emits('createGoalClicked', response) } } catch (error: any) { console.log(error.message); @@ -41,36 +37,37 @@ const createGoalClick = async () => { <template> <div class="col-lg-8"> - <h1>Create a new saving goal!</h1> + <h1>Lag et nytt sparemål!</h1> <br> - <p>Create a name for this saving goal </p> + <p>Gi sparemålet et navn </p> <div class="input-group mb-3"> - <span class="input-group-text" id="basic-addon1">Name</span> - <input v-model="name" type="text" class="form-control" placeholder="Name of Saving Goal" aria-label="Username" aria-describedby="basic-addon1" required> + <span class="input-group-text" id="basic-addon1">Navn</span> + <input v-model="name" type="text" class="form-control" placeholder="Navn på sparemålet" + aria-label="Username" aria-describedby="basic-addon1" required> </div> - <p>Add a description to this saving goal </p> + <p>Legg til en beskrivelse for sparemålet </p> <div class="input-group mb-3"> - <span class="input-group-text" id="basic-addon2">Description</span> + <span class="input-group-text" id="basic-addon2">Beskrivelse</span> <textarea v-model="desc" class="form-control" aria-label="With textarea"></textarea> </div> <!--Change this to date picker?--> - <p>When should this saving goal end?</p> + <p>Når skal pengene være spart?</p> <div class="input-group mb-3"> - <input v-model="date" type="date" class="form-control" aria-label="Amount of days" placeholder="Amount of days (as number)" required> + <input v-model="date" type="date" class="form-control" aria-label="Amount of days" required> </div> - <p>How much do you want to save during this saving goal? </p> + <p>Hvor mye vil du spare?</p> <div class="input-group"> - <input v-model="amount" type="number" class="form-control" aria-label="" placeholder="NOK (as number)" required> + <input v-model="amount" type="number" class="form-control" aria-label="NOK" required> <span class="input-group-text">NOK</span> </div> <br> <button class="btn btn-primary btn-lg" @click="createGoalClick">Create goal!</button> - <div style="color: green; font-size: 32px"> + <div class="confirmMessage"> {{ createdConfirm }} </div> @@ -87,4 +84,10 @@ const createGoalClick = async () => { padding-right: 56px; padding-bottom: 28px; } + +.confirmMessage { + color: green; + font-size: 32px; + min-height: 100px; +} </style> \ No newline at end of file diff --git a/src/components/SavingGoal/SavingGoalDefault.vue b/src/components/SavingGoal/SavingGoalDefault.vue new file mode 100644 index 0000000000000000000000000000000000000000..788e7e44ad896fdc1d6c9a0ee879cebdbe0136e3 --- /dev/null +++ b/src/components/SavingGoal/SavingGoalDefault.vue @@ -0,0 +1,103 @@ +<script setup lang="ts"> + +</script> + +<template> + <section id="hero" class="hero section"> + <div class="col-lg-8"> + <div class="container text-center"> + <div class="d-flex flex-column justify-content-center align-items-center"> + <h1 class="">Velkommen til <span>SpareSti</span></h1> + <br> + <p class="">Kom i økonomisk form: Ta på deg våre spareutfordringer!<br></p> + <img src="../../assets/savingPigRun.png" alt="SpareSti-logo"> + </div> + </div> + </div> + </section> +</template> + +<style scoped> +.col-lg-8 { + width: 100%; + margin-top: 50px; + padding-right: 56px; + padding-bottom: 28px; +} + +.hero { + width: 63%; + min-height: 70vh; + overflow: hidden; +} + +.hero img { + inset: 0; + display: block; + width: 60%; + height: 60%; + object-fit: cover; + z-index: 1; +} + +.hero .container { + position: relative; + z-index: 3; +} + +.hero h1 { + margin: 0; + font-size: 56px; + font-weight: 700; + line-height: 56px; +} + +.hero h1 span { + color: white; + background-color: #003A58; + padding: 4px 24px 14px 24px; + border-radius: 6px; +} + + +.hero p { + color: #003A58; + margin: 5px 0 30px 0; + font-size: 28px; + font-weight: 400; +} + +.hero .btn-watch-video i { + color: #003A58; + font-size: 32px; + transition: 0.3s; + line-height: 0; + margin-right: 8px; +} + +.hero .btn-watch-video:hover i { + color: #003A58; +} + +@media (max-width: 640px) { + .hero h1 { + font-size: 28px; + line-height: 36px; + } + + .hero p { + font-size: 18px; + line-height: 24px; + margin-bottom: 30px; + } + + .hero img { + inset: 0; + display: block; + width: 100%; + height: 100%; + object-fit: cover; + z-index: 1; + } +} +</style> \ No newline at end of file diff --git a/src/components/SavingGoal/SavingGoalList.vue b/src/components/SavingGoal/SavingGoalList.vue index 23604b4bea8ff280bb018c04811c900a44fb362b..975aa69196b6faca35be84f99823b1e4d3068fc8 100644 --- a/src/components/SavingGoal/SavingGoalList.vue +++ b/src/components/SavingGoal/SavingGoalList.vue @@ -1,15 +1,28 @@ <script setup lang="ts"> import {ref, onMounted} from "vue"; -import {GoalService, type GoalDTO, type CreateGoalDTO} from "@/api" +import {GoalService, type GoalDTO, type ChallengeDTO} from "@/api" -const savingGoalList = ref([] as any) - -defineProps() +const savingGoalList = ref(null as any) +const oldSavingGoalList = ref(null as any) const getGoals = async () => { try { let response = await GoalService.getGoals(); - savingGoalList.value = response + savingGoalList.value = [] + oldSavingGoalList.value = [] + const date = new Date() + response.forEach((goal: GoalDTO) => { + if(goal.targetDate) { + const targetDate = new Date(goal.targetDate) + if(targetDate < date) { + console.log("old") + oldSavingGoalList.value.push(goal) + } else { + console.log("current") + savingGoalList.value.push(goal) + } + } + }) } catch (error: any) { console.log(error.message); } @@ -26,19 +39,70 @@ const goToSavingGoal = (savingGoal: GoalDTO) => { const deleteSavingGoal = () => { }; + +function calculateSavedSoFar (goal: GoalDTO) { + console.log("hehe") + let savedSoFar = 0; // Reset savedSoFar before calculating again + if(goal.challenges){ + console.log("hehehe") + goal.challenges.forEach((challenge: ChallengeDTO) => { + // Check if progressList exists before accessing its elements + if (challenge.progressList) { + challenge.progressList.forEach((progress: any) => { + // Assuming 'amount' is the property you want to add from progressList + savedSoFar += progress.amount; + console.log("he") + }); + } + }); + } + return savedSoFar +} + +function formatDate(date: string) { + const date1 = new Date(date); + return date1.toISOString().split('T')[0] +} </script> <template> - <div v-for="(savingGoal, index) in savingGoalList" :key="index" class="card bg-body"> - <div class="card-header"> - Saving goal {{ index + 1 }} + <div v-if="savingGoalList"> + <div v-if="oldSavingGoalList.lenght > 0"> + <h3>Current:</h3> </div> - <div class="card-body"> - <h5 class="card-title">{{ savingGoal.goalName }}</h5> - <p class="card-text">{{ savingGoal.description }}</p> - <a href="#" class="btn btn-primary" @click="goToSavingGoal(savingGoal)">Go to saving goal</a> - <a href="#" class="btn btn-danger" @click="deleteSavingGoal" style="margin-left: 8px">Delete</a> + <div v-for="(savingGoal, index) in savingGoalList" :key="index" class="card bg-body"> + <div class="card-header"> + Sparemål {{ index + 1 }} + </div> + <div class="card-body"> + <h5 class="card-title">{{ savingGoal.name }}</h5> + <p class="card-text">{{ savingGoal.description }}</p> + <p class="card-text">Spart: {{calculateSavedSoFar(savingGoal)}}/{{savingGoal.targetAmount}} Kr</p> + <p class="card-text">Avsluttes: {{formatDate(savingGoal.targetDate)}} </p> + <a class="btn btn-primary" @click="goToSavingGoal(savingGoal)">Gå til sparemål</a> + <a class="btn btn-danger" @click="deleteSavingGoal" style="margin-left: 8px"> + <img src="../../assets/icons/trash-can.svg"> Slett</a> + </div> </div> + <div v-if="oldSavingGoalList.lenght > 0"> + <h3>Old:</h3> + <div v-for="(savingGoal, index) in oldSavingGoalList" :key="index" class="card bg-body"> + <div class="card-header"> + Sparemål {{ index + 1 }} + </div> + <div class="card-body"> + <h5 class="card-title">{{ savingGoal.name }}</h5> + <p class="card-text">{{ savingGoal.description }}</p> + <p class="card-text">{{calculateSavedSoFar(savingGoal)}}/{{savingGoal.targetAmount}}</p> + <a class="btn btn-primary" @click="goToSavingGoal(savingGoal)">Gå til sparemål</a> + <a class="btn btn-danger" @click="deleteSavingGoal" style="margin-left: 8px">Slett</a> + </div> + </div> + </div> + </div> + <div class="loading" v-else> + Laster... + <img src="../../assets/loadingPig.png" alt="loadingPig"> </div> </template> @@ -63,4 +127,19 @@ const deleteSavingGoal = () => { margin-bottom: 20px; border: none; } + +.loading { + color: white; + font-size: 40px; + font-weight: 500; +} + +.loading img { + width: 50%; + height: 50%; +} + +h3 { + color: white; +} </style> \ No newline at end of file diff --git a/src/components/SavingGoal/SavingGoalRoadmap.vue b/src/components/SavingGoal/SavingGoalRoadmap.vue index 65bbfb8876c1ed6f2b076ee56cbed0621f3a187f..54a8ffd8fdf1e8652fbe69791511e41a52441ae4 100644 --- a/src/components/SavingGoal/SavingGoalRoadmap.vue +++ b/src/components/SavingGoal/SavingGoalRoadmap.vue @@ -1,8 +1,9 @@ <script lang="ts"> import {CategoryScale, Chart as ChartJS, Legend, LinearScale, LineElement, PointElement, Title, Tooltip} from 'chart.js' import {Line} from 'vue-chartjs' -import type {ChallengeDTO, GoalDTO, MarkChallengeDTO} from "@/api"; +import type {ChallengeDTO, CreateGoalDTO, GoalDTO, MarkChallengeDTO} from "@/api"; import {GoalService} from '@/api' +import {useUserInfoStore} from "@/stores/UserStore"; ChartJS.register( CategoryScale, @@ -23,6 +24,8 @@ export default { return { image: 'https://th.bing.com/th/id/OIG3.NMbdxmKYKVnxYGLOa0Z0?w=1024&h=1024&rs=1&pid=ImgDetMain' as string, altImage: 'https://th.bing.com/th/id/OIG4.gVWUC.rwCb8faTNx31yU?w=1024&h=1024&rs=1&pid=ImgDetMain' as string, + failedImage: 'https://cdn-icons-png.flaticon.com/512/6659/6659895.png' as string, + successImage: 'https://static-00.iconduck.com/assets.00/checkmark-running-icon-1024x1024-aakqv1qi.png' as string, title: 'Spain trip' as string, bluePanelMaxHeight: 'auto' as string, roadmapSelected: true as boolean, @@ -50,12 +53,14 @@ export default { newPrice: 0, savedSoFar: 0 as number, currentChallengeIndex: 0, + feedback: "" as string, }; }, async mounted() { setTimeout(() => { this.findCurrentChallenge() this.disableAllChecksThatNotCurrent() + this.checkIfToAmbitious() this.togglePanel(this.selectedGoal.challenges[this.currentChallengeIndex]) this.calculateSavedSoFar() this.onLoadDisableChecks(this.selectedGoal) @@ -65,8 +70,18 @@ export default { }, computed: { computeImageFilter() { - return (challenge: ChallengeDTO) => { - return challenge ? 'none' : 'grayscale(100%)'; + return (challenge: any) => { + const today = new Date() + const startDate = new Date(challenge.startDate) + + // Check if the challenge is in the past or future + if (today < startDate) { + // Challenge is in the future, apply grayscale + return 'grayscale(100%)' + } else { + // Challenge is currently active, no grayscale + return 'none'; + } }; } }, @@ -115,7 +130,7 @@ export default { convertTemplateTextToChallengeText(challenge: ChallengeDTO) { let challengeText: any challengeText = challenge.challengeTemplate?.text - challengeText = challengeText.replace('{unit_amount}', challenge.challengeTemplate?.amount?.toString()) + challengeText = challengeText.replace('{unit_amount}', challenge.amount?.toString()) challengeText = challengeText.replace('{checkDays}', challenge.checkDays?.toString()) challengeText = challengeText.replace('{totalDays}', challenge.totalDays?.toString()) let totalAmount: any @@ -132,7 +147,7 @@ export default { calculateTotalAmountFromChallenges() { let totalAmountFromChallenges = 0 for (const challenge of this.selectedGoal.challenges) { - totalAmountFromChallenges += challenge.amount + totalAmountFromChallenges += challenge.amount * challenge.checkDays } return totalAmountFromChallenges }, @@ -231,6 +246,12 @@ export default { if (today >= startDate && today <= endDate) { this.currentChallengeIndex = index + } else { + if (today >= endDate) { + console.log("In the past") + } else { + console.log("In the future") + } } }) }, @@ -251,15 +272,10 @@ export default { }, calculateSavedSoFarPerChallengeInPercent(challenge: ChallengeDTO) { - let savedSoFarOnChallenge = 0 + let savedSoFarOnChallenge = this.calculateSavedSoFarPerChallenge(challenge) let targetAmount = 1 - challenge.progressList?.forEach(progress => { - if(progress.amount) { - savedSoFarOnChallenge += progress.amount - } - }) - if(challenge.amount) { - targetAmount = challenge.amount + if(challenge.amount && challenge.checkDays) { + targetAmount = challenge.amount * challenge.checkDays } return (savedSoFarOnChallenge / targetAmount) * 100 @@ -273,7 +289,54 @@ export default { } }) return savedSoFar - } + }, + + async updateUnitPrice (challenge: ChallengeDTO) { + const createGoalPayload: MarkChallengeDTO = { + id: challenge.id, + amount: this.newPrice + }; + try { + await GoalService.updateChallengeAmount({requestBody: createGoalPayload}) + } catch (e: any) { + console.log(e.message) + } + }, + + checkIfToAmbitious() { + let possibleSaving = this.calculateTotalAmountFromChallenges() + let wantedSaving = this.selectedGoal.targetAmount + + console.log(possibleSaving + "," + wantedSaving) + if(wantedSaving > possibleSaving) { + this.feedback = "Vi beundrer din ambisjon, men å oppnå den ettertraktede" + + " summen er ikke lett. Men disse utfordringene tar deg på god vei!" + } + }, + + getImageSource(challenge: ChallengeDTO) { + const today = new Date(); + const endDate = new Date(challenge.endDate as any); + + // Check if the challenge is in the past + if (today > endDate) { + // Challenge is in the past, return alternative image source + if(challenge.progressList) { + if(challenge.checkDays == challenge.progressList.length) { + return this.successImage + } else { + return this.failedImage; + } + } + } else { + // Challenge is currently active or in the future, return default image source + return this.image; + } + }, + + transferMoney(amount: number) { + //need users bank accounts + }, }, }; </script> @@ -298,17 +361,19 @@ export default { <ul class="timeline"> <li v-for="(challenge, index) in selectedGoal.challenges" :key="index" :class="{ 'timeline-inverted': index % 2 !== 0 }"> <div class="timeline-image z-1" @click="togglePanel(challenge)"> - <img class="circular-image" :src="challenge.showPanel ? altImage : image" :style="{ filter: computeImageFilter(challenge) }" alt=""> + <img class="circular-image" :src="challenge.showPanel ? altImage : getImageSource(challenge)" :style="{ filter: computeImageFilter(challenge) }" alt=""> </div> <div class="timeline-panel z-3" :id="'panel-' + index" v-show="challenge.showPanel"> <div class="timeline-heading"> + <h5 style="margin-top: 12px">{{challenge.points}}<img src="../../assets/items/pigcoin.png" alt="pig coint" style="width: 2rem"></h5> <h4>Utfordring {{ index +1 }}</h4> + <p style="font-size: 12px">{{formatDate(challenge.startDate)}} til {{formatDate(challenge.endDate)}}</p> <h4 class="subheading">{{convertTemplateTextToChallengeText(challenge)}}</h4> </div> <div class="timeline-body"> <br> <p> - Pris per enhet: {{challenge.challengeTemplate.amount}} kr <img src="../../assets/icons/edit-button.svg" alt="editIcon" data-bs-toggle="collapse" href="#collapseExample" role="button" aria-expanded="false" aria-controls="exampleModal"> + Pris per enhet: {{challenge.amount}} kr <img src="../../assets/icons/edit-button.svg" alt="editIcon" data-bs-toggle="collapse" href="#collapseExample" role="button" aria-expanded="false" aria-controls="exampleModal"> </p> <br> <div class="collapse" id="collapseExample" style="background-color: white; padding: 12px; border-radius: 5px"> @@ -319,11 +384,12 @@ export default { </div> </div> <br> - <button class="btn btn-success">Bekreft endring</button> + <button @click="updateUnitPrice(challenge)" class="btn btn-success">Bekreft endring</button> </div> <br> + <p>Spart: {{ calculateSavedSoFarPerChallenge(challenge)}} Kr</p> <div class="progress"> - <div class="progress-bar" role="progressbar" :style="{ width: calculateSavedSoFarPerChallengeInPercent(challenge) + '%' }" :aria-valuenow="calculateSavedSoFarPerChallengeInPercent(challenge)" aria-valuemin="0" aria-valuemax="100">{{ calculateSavedSoFarPerChallenge(challenge)}} Kr</div> + <div class="progress-bar" role="progressbar" :style="{ width: calculateSavedSoFarPerChallengeInPercent(challenge) + '%' }" :aria-valuenow="calculateSavedSoFarPerChallengeInPercent(challenge)" aria-valuemin="0" aria-valuemax="100"></div> </div> <br> <div class="checkbox-row"> @@ -341,6 +407,10 @@ export default { <div v-else> <div class="row"> + <div v-if="feedback != ''" class="feedbackBox"> + <h3>Oops!</h3> + <h5 class="">{{ feedback }}</h5> + </div> <div class="col-sm-3"> <div class="card-box tilebox-one"><i class="icon-layers float-right text-muted"></i> <h6 class="text-muted text-uppercase mt-0">Du ønsker å spare</h6> @@ -384,7 +454,7 @@ export default { margin-bottom:40px; padding-bottom: 10px; color: white; - border-radius: 1em; + border-radius: 20px; background-color: #003A58; } @@ -393,6 +463,7 @@ export default { padding:4px 0 0 0; margin-top:22px; list-style: none; + margin-bottom: 300px; } .timeline>li:nth-child(even) { @@ -428,7 +499,8 @@ export default { text-align: right; background-color: #003A58; border-radius: 1em; - margin-left: 110px; + margin-left: 100px; + color: white; } .timeline>li .timeline-panel:before { @@ -451,7 +523,6 @@ export default { left: 50%; border: 7px solid #003A58; border-radius: 100%; - background-color: #00ffff; box-shadow: 0 0 5px #00e1ff; width: 100px; height: 100px; @@ -469,7 +540,7 @@ export default { float: right; padding: 0 30px 20px 20px; text-align: left; - margin-right: 110px; + margin-right: 100px; } .timeline>li.timeline-inverted>.timeline-panel:before { @@ -491,7 +562,7 @@ export default { } .timeline .timeline-heading h4 { - margin-top:22px; + margin-top:0px; margin-bottom: 4px; padding:0; color: white; @@ -704,4 +775,11 @@ export default { margin-right: 10px; /* Adjust as needed */ } +.feedbackBox { + box-shadow: rgba(255, 0, 0, 0.2) 0px 7px 29px 0px; + border-radius: 1em; + margin-bottom: 40px; + margin-top: 10px; + padding: 12px; +} </style> \ No newline at end of file