Skip to content
Snippets Groups Projects
Commit e8411034 authored by Victor Ekholt Gunrell Kaste's avatar Victor Ekholt Gunrell Kaste
Browse files

feat: redesigning roadmap for stats integrating endpoints

parent e93da3db
No related branches found
No related tags found
1 merge request!69Feat/redesign roadmap
Pipeline #282278 failed
<script lang="ts">
interface Step {
title: string;
showPanel: boolean;
description: string;
percentFinished: number;
unFinished: boolean;
}
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 {GoalService} from '@/api'
ChartJS.register(
CategoryScale,
LinearScale,
PointElement,
LineElement,
Title,
Tooltip,
Legend
)
export default {
components: {
Line
},
data() {
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,
title: 'Spain trip' as string,
//This will be changed to info gathered from backend
steps: [
{ title: 'Challenge 1', showPanel: false, description: 'Save 50kr on coffee in 3 days', percentFinished: 22, unFinished: false },
{ title: 'Challenge 2', showPanel: false, description: 'Save 500kr on food in 7 days', percentFinished: 73, unFinished: false },
{ title: 'Challenge 3', showPanel: false, description: 'Save 350kr on clothes in 5 days', percentFinished: 50, unFinished: true },
{ title: 'Challenge 4', showPanel: false, description: 'Save 150kr on coffee in 4 days', percentFinished: 10, unFinished: true },
{ title: 'Challenge 5', showPanel: false, description: 'Save 50kr on coffee in 3 days', percentFinished: 90, unFinished: true }
]
,
bluePanelMaxHeight: 'auto' as string
bluePanelMaxHeight: 'auto' as string,
roadmapSelected: true as boolean,
statsSelected: false as boolean,
chartData: {
labels: [1, 2, 3, 4, 5, 6, '7'],
datasets: [
{
label: this.selectedGoal.name,
backgroundColor: '#003A58',
data: [11, 24, 30, 47, 53, 62, 79]
}
]
},
chartOptions: {
responsive: true,
maintainAspectRatio: true,
scales: {
y: {
min: 0,
max: this.selectedGoal.targetAmount
}
}
},
newPrice: 0,
savedSoFar: 0 as number,
currentChallengeIndex: 0,
};
},
mounted() {
async mounted() {
setTimeout(() => {
this.togglePanel(this.steps[2]);
this.findCurrentChallenge()
this.disableAllChecksThatNotCurrent()
this.togglePanel(this.selectedGoal.challenges[this.currentChallengeIndex])
this.calculateSavedSoFar()
this.onLoadDisableChecks(this.selectedGoal)
this.onLoadAddDataToGraph(this.selectedGoal)
}, 500);
},
computed: {
computeImageFilter() {
return (step: Step) => {
return step.unFinished ? 'none' : 'grayscale(100%)';
return (challenge: ChallengeDTO) => {
return challenge ? 'none' : 'grayscale(100%)';
};
}
},
props: {
selectedGoal: {
type: Object,
default: null,
},
},
methods: {
togglePanel(step: Step) {
togglePanel(step: any) {
if (step.showPanel) {
step.showPanel = false;
} else {
this.steps.forEach((s) => (s.showPanel = false));
this.selectedGoal.challenges.forEach((s: any) => (s.showPanel = false));
step.showPanel = true;
this.scrollToPanel(step);
}
},
scrollToPanel(step: Step) {
scrollToPanel(step: any) {
if (step.showPanel) {
this.$nextTick(() => {
const panel = document.getElementById(`panel-${this.steps.indexOf(step)}`);
const panel = document.getElementById(`panel-${this.selectedGoal.challenges.indexOf(step)}`);
if (panel) {
panel.scrollIntoView({ behavior: 'smooth', block: 'center' });
}
});
}
},
changeDisplay() {
if (this.roadmapSelected) {
this.roadmapSelected = false
this.statsSelected = true
} else {
this.roadmapSelected = true
this.statsSelected = false
setTimeout(() => {
this.onLoadDisableChecks(this.selectedGoal)
this.disableAllChecksThatNotCurrent()
}, 100);
}
},
convertTemplateTextToChallengeText(challenge: ChallengeDTO) {
let challengeText: any
challengeText = challenge.challengeTemplate?.text
challengeText = challengeText.replace('{unit_amount}', challenge.challengeTemplate?.amount?.toString())
challengeText = challengeText.replace('{checkDays}', challenge.checkDays?.toString())
challengeText = challengeText.replace('{totalDays}', challenge.totalDays?.toString())
let totalAmount: any
if (challenge.checkDays !== undefined && challenge.amount !== undefined) {
totalAmount = challenge.checkDays * challenge.amount;
} else {
// Handle the case when challenge.checkDays or challenge.amount is undefined
}
challengeText = challengeText.replace('{total_amount}', totalAmount.toString())
challengeText = challengeText.replace('{amount}', challenge.amount?.toString())
return challengeText
},
calculateTotalAmountFromChallenges() {
let totalAmountFromChallenges = 0
for (const challenge of this.selectedGoal.challenges) {
totalAmountFromChallenges += challenge.amount
}
return totalAmountFromChallenges
},
async handleCheckboxClick(challenge: ChallengeDTO, index: number, amount: number) {
this.lockCheckBox(challenge, index)
const markChallengePayload: MarkChallengeDTO = {
id: challenge.id,
day: index,
amount: amount,
};
try {
await GoalService.updateChallenge({ requestBody: markChallengePayload });
const today: Date = new Date();
const dateString: string = today.toISOString().split('T')[0]; // Extract YYYY-MM-DD part
if (challenge.progressList) {
challenge.progressList.push({ day: index, amount: amount, completedAt: dateString })
}
this.addDataToChart(amount, dateString);
this.calculateSavedSoFar();
} catch (error: any) {
console.log(error.message);
}
},
lockCheckBox(challenge: ChallengeDTO, index: number) {
const checkboxId = challenge.id + 'inlineCheckbox' + index
const checkbox = document.getElementById(checkboxId) as HTMLInputElement | null;
if (checkbox) {
// Disable the checkbox
checkbox.disabled = true;
}
},
onLoadDisableChecks(goal: GoalDTO) {
(goal.challenges || []).forEach((challenge: any) => {
challenge.progressList.forEach((progress: any) => {
// Assuming 'amount' is the property you want to add from progressList
const checkBoxId = challenge.id + 'inlineCheckbox' + progress.day
const checkbox = document.getElementById(checkBoxId) as HTMLInputElement | null;
if (checkbox) {
// Disable the checkbox
checkbox.checked = true;
checkbox.disabled = true;
}
});
});
},
onLoadAddDataToGraph(goal: GoalDTO) {
(goal.challenges || []).forEach((challenge: any) => {
challenge.progressList.forEach((progress: any) => {
this.addDataToChart(progress.amount, progress.completedAt);
});
});
},
addDataToChart(data: number, date: string) {
// Find the last dataset
const lastDataset = this.chartData.datasets[this.chartData.datasets.length - 1];
// Calculate the new label based on the last label
const newLabel = date.split('T')[0];
// Calculate the new data point based on the last data point
const lastDataPoint = lastDataset.data[lastDataset.data.length - 1];
const newDataPoint = lastDataPoint + data;
// Add the new label and data point to the chart data
this.chartData.labels.push(newLabel);
lastDataset.data.push(newDataPoint);
},
calculateSavedSoFar() {
this.savedSoFar = 0; // Reset savedSoFar before calculating again
this.selectedGoal.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
this.savedSoFar += progress.amount;
});
}
});
},
findCurrentChallenge() {
const today: Date = new Date();
this.selectedGoal.challenges.forEach((challenge: ChallengeDTO, index: number) => {
const startDate: Date = new Date(challenge.startDate as string);
const endDate: Date = new Date(challenge.endDate as string);
if (today >= startDate && today <= endDate) {
this.currentChallengeIndex = index
}
})
},
disableAllChecksThatNotCurrent() {
this.selectedGoal.challenges.forEach((challenge: ChallengeDTO, index: number) => {
if (index != this.currentChallengeIndex) {
for (let i = 1; i < challenge.checkDays + 1; i++) {
this.lockCheckBox(challenge, i)
}
}
})
}
},
};
</script>
......@@ -64,52 +250,69 @@ export default {
<template>
<div class="col-lg-8">
<div class="SavingGoalTitle text-center">
{{title}}
{{selectedGoal.name}}
<br>
<p class="d-inline-flex gap-1">
<button class="btn btn-primary" type="button" data-bs-toggle="collapse" data-bs-target="#collapseExample" aria-expanded="false" aria-controls="collapseExample" style="font-size: 25px;">
See more info
<button @click="changeDisplay" class="btn btn-primary" type="button" style="font-size: 25px;">
<div v-if="roadmapSelected">
Se statistikk
</div>
<div v-else>
Se sparesti
</div>
</button>
</p>
<div class="collapse" id="collapseExample">
<div class="card card-body bg-primary mx-auto" style="width: 80%;">
Total saved: 20kr
</div>
</div>
</div>
<ul class="timeline">
<li v-for="(step, index) in steps" :key="index" :class="{ 'timeline-inverted': index % 2 !== 0 }">
<div class="timeline-image z-1" @click="togglePanel(step)">
<img class="circular-image" :src="step.showPanel ? altImage : image" :style="{ filter: computeImageFilter(step) }" alt="">
</div>
<div class="timeline-panel z-3" :id="'panel-' + index" v-show="step.showPanel">
<div class="timeline-heading">
<h4>{{ step.title }}</h4>
<h4 class="subheading">{{step.description}}</h4>
<div v-if="roadmapSelected">
<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="">
</div>
<div class="timeline-body">
<br>
<p class="">
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
</p>
<br>
<div class="progress">
<div class="progress-bar" role="progressbar" :style="{ width: step.percentFinished + '%' }" :aria-valuenow="step.percentFinished" aria-valuemin="0" aria-valuemax="100"></div>
<div class="timeline-panel z-3" :id="'panel-' + index" v-show="challenge.showPanel">
<div class="timeline-heading">
<h4>Utfordring {{ index +1 }}</h4>
<h4 class="subheading">{{convertTemplateTextToChallengeText(challenge)}}</h4>
</div>
<br>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="inlineCheckbox1" value="option1">
<label class="form-check-label" style="color:white;" for="inlineCheckbox1">Day 1</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="inlineCheckbox2" value="option2">
<label class="form-check-label" style="color:white;" for="inlineCheckbox1">Day 2</label>
<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">
</p>
<br>
<div class="collapse" id="collapseExample" style="background-color: white; padding: 12px; border-radius: 5px">
<div class="card card-body">
<div class="input-group mb-3">
<span class="input-group-text" id="basic-addon1">Ny pris</span>
<input v-model="newPrice" type="number" class="form-control" placeholder="Pris i kr" aria-label="newPrice" aria-describedby="basic-addon1" required>
</div>
</div>
<br>
<button class="btn btn-success">Bekreft endring</button>
</div>
<br>
<div class="progress">
<div class="progress-bar" role="progressbar" :style="{ width: challenge.percentFinished + '%' }" :aria-valuenow="challenge.percentFinished" aria-valuemin="0" aria-valuemax="100"></div>
</div>
<br>
<div class="checkbox-row">
<div class="form-check form-check-inline" v-for="(day, index) in challenge.checkDays" :key="index">
<input class="form-check-input" @click="handleCheckboxClick(challenge, index+1, challenge.challengeTemplate.amount)" type="checkbox" :id="challenge.id + 'inlineCheckbox' + (index + 1)" :value="day" :disabled="day.checked">
<label class="form-check-label" style="color:white;" :for="'inlineCheckbox' + (index + 1)">{{ day }}</label>
</div>
</div>
</div>
</div>
</div>
<div class="line" v-if="index < steps.length - 1"></div>
</li>
</ul>
<div class="line" v-if="index < selectedGoal.challenges.length - 1"></div>
</li>
</ul>
</div>
<div v-else>
<h3 style="font-weight: 600;">Du har så langt spart {{ savedSoFar }} kr</h3>
<h3 style="font-weight: 600;">Ditt penge mål er: {{selectedGoal.targetAmount}} kr</h3>
<h3 style="font-weight: 600;">Utfordringene i denne sparestien vil spare deg {{ calculateTotalAmountFromChallenges() }} kr</h3>
<Line ref="chart" :data="chartData" :options="chartOptions" />
</div>
</div>
</template>
......@@ -127,7 +330,7 @@ export default {
padding-bottom: 10px;
color: white;
border-radius: 1em;
background-color: #0A58CA;
background-color: #003A58;
}
.timeline {
......@@ -165,11 +368,12 @@ export default {
.timeline > li .timeline-panel {
position: relative;
float: left;
width: 41%;
width: 31%;
padding: 0 20px 20px 30px;
text-align: right;
background-color: #0A58CA;
background-color: #003A58;
border-radius: 1em;
margin-left: 110px;
}
.timeline>li .timeline-panel:before {
......@@ -190,7 +394,7 @@ export default {
z-index: 100;
position: absolute;
left: 50%;
border: 7px solid #001664;
border: 7px solid #003A58;
border-radius: 100%;
background-color: #00ffff;
box-shadow: 0 0 5px #00e1ff;
......@@ -210,6 +414,7 @@ export default {
float: right;
padding: 0 30px 20px 20px;
text-align: left;
margin-right: 110px;
}
.timeline>li.timeline-inverted>.timeline-panel:before {
......@@ -431,4 +636,16 @@ export default {
max-width: 100%;
height: auto;
}
.checkbox-row {
display: flex;
flex-wrap: wrap;
margin-bottom: 12px; /* Adjust as needed */
text-align: left !important;
}
.form-check {
flex: 0 0 calc(25% - 10px); /* 20% width for each checkbox, adjust margin accordingly */
margin-right: 10px; /* Adjust as needed */
}
</style>
\ No newline at end of file
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