Skip to content
Snippets Groups Projects
Commit 0ba974d4 authored by Malin Haugland Høli's avatar Malin Haugland Høli
Browse files

refactor: :ambulance: Update challenge views with final design

parent 94ff0847
No related branches found
No related tags found
3 merge requests!66Final merge,!50fix(styling):,!4Pipeline fix
......@@ -4,113 +4,90 @@ import { computed, onMounted, ref, watch } from 'vue'
import ProgressBar from '@/components/ProgressBar.vue'
import authInterceptor from '@/services/authInterceptor'
import type { Challenge } from '@/types/challenge'
import ModalComponent from '@/components/ModalComponent.vue'
const router = useRouter()
const modalTitle = ref('');
const modalMessage = ref('');
const confirmModalOpen = ref(false);
const errorModalOpen = ref(false);
const oneWeekFromNow = new Date()
oneWeekFromNow.setDate(oneWeekFromNow.getDate() + 7)
const minDate = oneWeekFromNow.toISOString().slice(0, 16)
const minDate = new Date(new Date().setDate(new Date().getDate() + 1)).toISOString().slice(0, 10)
const selectedDate = ref<string>(minDate)
const thirtyDaysFromNow = new Date()
thirtyDaysFromNow.setDate(thirtyDaysFromNow.getDate() + 30)
const maxDate = thirtyDaysFromNow.toISOString().slice(0, 16)
const maxDate = thirtyDaysFromNow.toISOString().slice(0, 10)
const challengeInstance = ref<Challenge>({
title: '',
perPurchase: 20,
perPurchase: 0,
saved: 0,
target: 100,
target: 0,
description: '',
due: minDate + ':00.000Z',
type: ''
due: ''
})
const isAmountSaved = ref(false)
const timesSaved = ref(challengeInstance.value.saved / challengeInstance.value.perPurchase)
watch(
() => timesSaved.value,
(newVal) => {
challengeInstance.value.saved = newVal * challengeInstance.value.perPurchase
challengeInstance.value.saved = parseFloat(challengeInstance.value.saved.toFixed(2))
}
)
watch(
() => challengeInstance.value.saved,
(newVal) => {
challengeInstance.value.saved = Math.max(
0,
Math.min(challengeInstance.value.target, newVal)
)
challengeInstance.value.saved = parseFloat(challengeInstance.value.saved.toFixed(2))
timesSaved.value = challengeInstance.value.saved / challengeInstance.value.perPurchase
timesSaved.value = parseFloat(timesSaved.value.toFixed(2))
}
)
watch(
() => challengeInstance.value.perPurchase,
(newVal) => {
challengeInstance.value.perPurchase = Math.max(
1,
Math.min(challengeInstance.value.target, newVal)
)
challengeInstance.value.perPurchase = parseFloat(
challengeInstance.value.perPurchase.toFixed(2)
)
timesSaved.value = challengeInstance.value.saved / challengeInstance.value.perPurchase
timesSaved.value = parseFloat(timesSaved.value.toFixed(2))
}
)
watch(
() => challengeInstance.value.target,
(newVal) => {
challengeInstance.value.target = Math.max(
Math.max(challengeInstance.value.saved, 1),
newVal
)
}
)
const selectedDate = ref(minDate)
watch(
() => selectedDate.value,
(newVal) => {
if (newVal) {
selectedDate.value = newVal < minDate ? minDate : newVal
challengeInstance.value.due = selectedDate.value + ':00.000Z'
}
}
)
watch(selectedDate, (newDate) => {
challengeInstance.value.due = newDate;
});
const isEdit = computed(() => router.currentRoute.value.name === 'edit-challenge')
const pageTitle = computed(() => (isEdit.value ? 'Rediger utfordring' : 'Ny utfordring'))
const pageTitle = computed(() => (isEdit.value ? 'Rediger utfordring🎨' : 'Ny utfordring🎨'))
const submitButton = computed(() => (isEdit.value ? 'Oppdater' : 'Opprett'))
const completion = computed(
() => (challengeInstance.value.saved / challengeInstance.value.target) * 100
)
const isInputValid = computed(() => {
return (
challengeInstance.value.title.length > 0 &&
challengeInstance.value.title.length <= 20 &&
challengeInstance.value.description.length <= 280 &&
challengeInstance.value.target > 0 &&
challengeInstance.value.due !== ''
)
})
function validateInputs() {
const errors = [];
challengeInstance.value.due = selectedDate.value + 'T23:59:59.999Z';
const submitAction = () => {
if (!isInputValid.value) {
return () => alert('Fyll ut alle feltene')
if (!challengeInstance.value.title || challengeInstance.value.title.length > 20) {
errors.push("Tittelen må være mellom 1 og 20 tegn.");
}
if (challengeInstance.value.description.length > 280) {
errors.push("Beskrivelsen må være under 280 tegn.");
}
if (challengeInstance.value.target <= 0) {
errors.push("Målbeløpet må være større enn 0.");
}
if (new Date(challengeInstance.value.due) < new Date(minDate)) {
errors.push("Forfallsdatoen må være minst en uke frem i tid.");
}
if (challengeInstance.value.perPurchase <= 0) {
errors.push("Pris per sparing må være større enn 0.");
}
return errors;
}
if (isEdit.value) {
updateChallenge()
} else {
createChallenge()
const submitAction = async() => {
const errors = validateInputs();
if(errors.length > 0) {
const formatErrors = errors.join('\n');
modalTitle.value = 'Oops! Noe er feil med det du har fylt ut🚨';
modalMessage.value = formatErrors;
errorModalOpen.value = true;
return;
}
try {
if (isEdit.value) {
updateChallenge();
} else {
createChallenge();
}
} catch (error) {
console.error(error);
modalTitle.value = 'Systemfeil';
modalMessage.value = 'En feil oppstod under lagring av utfordringen.';
errorModalOpen.value = true;
}
}
......@@ -156,6 +133,27 @@ const updateChallenge = () => {
console.error(error)
})
}
const cancelCreation = () => {
if (challengeInstance.value.title !== '' ||
challengeInstance.value.description !== '' ||
challengeInstance.value.perPurchase !== 0 ||
challengeInstance.value.saved !== 0 ||
challengeInstance.value.target !== 0)
{
modalTitle.value = 'Du er i ferd med å avbryte redigeringen🚨';
modalMessage.value = 'Er du sikker på at du vil avbryte?';
confirmModalOpen.value = true;
} else {
router.push({ name: 'challenges' })
}
}
const confirmCancel = () => {
router.push({ name: 'challenges' })
confirmModalOpen.value = false;
}
</script>
<template>
......@@ -172,12 +170,7 @@ const updateChallenge = () => {
</div>
<div class="flex flex-col">
<p class="mx-4">Type</p>
<input v-model="challengeInstance.type" placeholder="Skriv en type" type="text" />
</div>
<div class="flex flex-col">
<p class="mx-4">Beskrivelse</p>
<p class="mx-4">Beskrivelse (valgfri)</p>
<textarea
v-model="challengeInstance.description"
class="w-80 h-20 no-rezise"
......@@ -187,7 +180,7 @@ const updateChallenge = () => {
<div class="flex flex-col sm:flex-row gap-3">
<div class="flex flex-col">
<p class="mx-4">Pris per sparing</p>
<p class="mx-4">Spare per gang</p>
<input
v-model="challengeInstance.perPurchase"
class="w-40 text-right"
......@@ -198,30 +191,18 @@ const updateChallenge = () => {
<div class="flex flex-col">
<div class="flex flex-row justify-between mx-4">
<p>{{ isAmountSaved ? 'Kroner spart' : 'Antall sparinger' }}</p>
<button class="p-0 bg-transparent" @click="isAmountSaved = !isAmountSaved">
🔄️
</button>
<p>Kroner spart💸</p>
</div>
<input
v-if="isAmountSaved"
v-model="challengeInstance.saved"
class="w-40 text-right"
min="0"
placeholder="Sparebeløp"
type="number"
/>
<input
v-else
v-model="timesSaved"
class="w-40 text-right"
placeholder="Kr spart per sparing"
type="number"
/>
</div>
<div class="flex flex-col">
<p class="mx-4">Av målbeløp...*</p>
<p class="mx-4">Av målbeløp💯*</p>
<input
v-model="challengeInstance.target"
class="w-40 text-right"
......@@ -232,26 +213,81 @@ const updateChallenge = () => {
</div>
<ProgressBar :completion="completion" />
<div class="flex flex-col">
<p class="mx-4">Forfallsdato*</p>
<input
v-model="selectedDate"
:max="maxDate"
:min="minDate"
placeholder="Forfallsdato"
type="datetime-local"
/>
<div class="flex flex-row gap-4">
<div class="flex flex-col">
<p class="mx-4">Forfallsdato*</p>
<input
:min="minDate"
:max="maxDate"
v-model="selectedDate"
placeholder="Forfallsdato"
type="date"
/>
</div>
<div class="flex flex-col">
<p>Last opp ikon for utfordringen📸</p>
<button class="mt-2 font-bold cursor-pointer transition-transform duration-300 ease-in-out hover:scale-110 hover:opacity-90">💾</button>
</div>
</div>
<div class="flex flex-row justify-between w-full">
<button :disabled="!isInputValid" @click="submitAction" v-text="submitButton" />
<button
class="bg-button-other"
@click="router.push({ name: 'challenges' })"
class="primary danger"
@click="cancelCreation"
v-text="'Avbryt'"
/>
<button
class="primary"
@click="submitAction"
v-text="submitButton" />
</div>
<ModalComponent
:title="modalTitle"
:message="modalMessage"
:isModalOpen="errorModalOpen"
@close="errorModalOpen = false"
>
<template v-slot:input>
<div class="flex justify-center items-center">
<div class="flex flex-col gap-5">
<button
class="primary"
@click="errorModalOpen = false"
>
Lukk
</button>
</div>
</div>
</template>
</ModalComponent>
<ModalComponent
:title="modalTitle"
:message="modalMessage"
:isModalOpen="confirmModalOpen"
@close="confirmModalOpen = false"
>
<template v-slot:input>
<div class="flex justify-center items-center">
<div class="flex flex-col gap-5">
<button
class="primary"
@click="confirmCancel"
>
Bekreft
</button>
<button
class="primary danger"
@click="confirmModalOpen = false"
>
Avbryt
</button>
</div>
</div>
</template>
</ModalComponent>
</div>
</div>
</template>
......
......@@ -50,18 +50,23 @@ onMounted(async () => {
<h1 class="font-bold text-center">Dine utfordringer</h1>
<div class="flex flex-col gap-5 items-center">
<div class="flex flex-row gap-5">
<button @click="router.push({ name: 'new-challenge' })">
<button
class="primary"
@click="router.push({ name: 'new-challenge' })">
Opprett en ny utfordring
</button>
</div>
<h2 class="font-bold">Aktive utfordringer</h2>
<h2 class="font-bold">Aktive utfordringer🚀</h2>
<div class="flex flex-row justify-center gap-10 flex-wrap">
<CardChallenge
v-for="challenge in activeChallenges"
:key="challenge.id"
:challenge-instance="challenge"
/>
<p v-if="!activeChallenges">
Du har ingen aktive spareutfordringer😢
</p>
</div>
<PageControl
:currentPage="currentPageActive"
......@@ -69,9 +74,10 @@ onMounted(async () => {
:totalPages="totalPagesActive"
/>
<h2 class="font-bold">Fullførte utfordringer</h2>
<h2 class="font-bold">Fullførte utfordringer💯</h2>
<div class="flex flex-row justify-center gap-10 flex-wrap">
<CardChallenge
class="border-2 border-slate-200 hover:bg-slate-50"
v-for="challenge in completedChallenges"
:key="challenge.id"
:challenge-instance="challenge"
......@@ -85,4 +91,5 @@ onMounted(async () => {
</div>
</template>
<style scoped></style>
<style scoped>
</style>
\ No newline at end of file
......@@ -81,14 +81,14 @@ const completeChallenge = () => {
<div class="flex flex-row flex-wrap items-center justify-center gap-10">
<div class="flex flex-col gap-5 max-w-96">
<button
class="w-min"
class="w-min bg-transparent rounded-lg font-bold left-10 cursor-pointer transition-transform duration-300 ease-in-out hover:scale-110 hover:opacity-100 justify-start"
@click="router.push({ name: 'challenges', params: { id: challengeInstance.id } })"
>
Oversikt
👈Oversikt
</button>
<div
class="flex flex-col justify-center border-4 border-black rounded-3xl align-middle p-5 card-shadow overflow-hidden w-full"
class="flex flex-col justify-center border-2 rounded-3xl align-middle p-5 card-shadow overflow-hidden w-full"
>
<h2 class="my-0">Spareutfordring:</h2>
<h2 class="font-light">
......@@ -106,10 +106,25 @@ const completeChallenge = () => {
Du sparer {{ challengeInstance.perPurchase }}kr hver gang du dropper å bruke
penger på {{ challengeInstance.type }}
</p>
<div class="justify-center pl-20">
<button
class="primary danger mt-2 rounded-2xl p-2 w-40"
@click="
authInterceptor
.delete(`/challenges/${challengeInstance.id}`)
.then(() => router.push({ name: 'challenges' }))
.catch((error) => console.error(error))
"
>
Slett
</button>
</div>
</div>
<div class="flex flex-row justify-between w-full">
<button
class="primary secondary"
v-if="!isCompleted"
@click="
router.push({
......@@ -122,22 +137,11 @@ const completeChallenge = () => {
</button>
<button
class="primary"
v-if="!isCompleted"
@click="completeChallenge"
v-text="'Sett utfordring til ferdig'"
/>
<button
class="bg-button-danger hover:bg-button-danger"
@click="
authInterceptor
.delete(`/challenges/${challengeInstance.id}`)
.then(() => router.push({ name: 'challenges' }))
.catch((error) => console.error(error))
"
>
Slett
</button>
</div>
</div>
<InteractiveSpare :png-size="10" :speech="motivation" direction="left" />
......
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