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

refactor: :ambulance: Update goal views with final design

parent 0ba974d4
No related branches found
No related tags found
3 merge requests!66Final merge,!50fix(styling):,!4Pipeline fix
......@@ -4,69 +4,94 @@ import { computed, onMounted, ref, watch } from 'vue'
import type { Goal } from '@/types/goal'
import ProgressBar from '@/components/ProgressBar.vue'
import authInterceptor from '@/services/authInterceptor'
import ModalComponent from '@/components/ModalComponent.vue'
const router = useRouter()
const selectedDate = ref<string>('')
const minDate = new Date(new Date().setDate(new Date().getDate() + 1)).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 modalMessage = ref<string>('')
const modalTitle = ref<string>('')
const errorModalOpen = ref<boolean>(false)
const confirmModalOpen = ref<boolean>(false)
const goalInstance = ref<Goal>({
title: '',
saved: 0,
target: 100,
target: 0,
description: '',
due: ''
})
watch(
() => goalInstance.value.saved,
(newVal) => {
goalInstance.value.saved = Math.max(0, Math.min(goalInstance.value.target, newVal))
watch(selectedDate, (newDate) => {
goalInstance.value.due = newDate;
});
const isEdit = computed(() => router.currentRoute.value.name === 'edit-goal')
const pageTitle = computed(() => (isEdit.value ? 'Rediger sparemål🎨' : 'Nytt sparemål🎨'))
const submitButton = computed(() => (isEdit.value ? 'Oppdater' : 'Opprett'))
const completion = computed(() => (goalInstance.value.saved / goalInstance.value.target) * 100)
function validateInputs() {
const errors = [];
goalInstance.value.due = selectedDate.value + 'T23:59:59.999Z';
if (!goalInstance.value.title) {
errors.push('Tittel må fylles ut');
}
if (!goalInstance.value.target ) {
errors.push('Målbeløp må fylles ut');
}
if (!goalInstance.value.due) {
errors.push('Forfallsdato må fylles ut');
}
)
watch(
() => goalInstance.value.target,
(newVal) => {
goalInstance.value.target = Math.max(Math.max(goalInstance.value.saved, 1), newVal)
if (goalInstance.value.target < 1) {
errors.push('Målbeløp må være større enn 0');
}
)
watch(
() => selectedDate.value,
(newVal) => {
if (newVal < minDate) selectedDate.value = minDate
goalInstance.value.due = newVal + ':00.000Z'
if (goalInstance.value.saved < 0) {
errors.push('Sparebeløp kan ikke være negativt');
}
)
const isEdit = computed(() => router.currentRoute.value.name === 'edit-goal')
const pageTitle = computed(() => (isEdit.value ? 'Rediger sparemål' : 'Nytt sparemål'))
const submitButton = computed(() => (isEdit.value ? 'Oppdater' : 'Opprett'))
const completion = computed(() => (goalInstance.value.saved / goalInstance.value.target) * 100)
if (goalInstance.value.saved > goalInstance.value.target) {
errors.push('Sparebeløp kan ikke være større enn målbeløp');
}
const isInputValid = computed(() => {
return (
goalInstance.value.title.length > 0 &&
goalInstance.value.title.length <= 20 &&
goalInstance.value.description.length <= 280 &&
goalInstance.value.target > 0 &&
goalInstance.value.due !== ''
)
})
return errors;
const submitAction = () => {
if (!isInputValid.value) {
return () => alert('Fyll ut alle feltene')
}
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.replace(/\n/g, "<br>")
errorModalOpen.value = true;
return;
}
if (isEdit.value) {
updateGoal()
} else {
createGoal()
try {
if (isEdit.value) {
updateGoal();
} else {
createGoal();
}
} catch (error) {
console.error(error);
modalTitle.value = 'Systemfeil';
modalMessage.value = 'En feil oppstod under lagring av utfordringen.';
errorModalOpen.value = true;
}
}
watch(selectedDate, (newDate) => {
console.log(newDate)
})
onMounted(async () => {
if (isEdit.value) {
const goalId = router.currentRoute.value.params.id
......@@ -75,12 +100,14 @@ onMounted(async () => {
await authInterceptor(`/goals/${goalId}`)
.then((response) => {
goalInstance.value = response.data
selectedDate.value = response.data.due.slice(0, 16)
selectedDate.value = response.data.due.slice(0, 10)
})
.catch((error) => {
console.error(error)
router.push({ name: 'goals' })
})
} else {
goalInstance.value.due = selectedDate.value
}
})
......@@ -116,6 +143,24 @@ const deleteGoal = () => {
console.error(error)
})
}
function cancelCreation() {
if (goalInstance.value.title !== '' ||
goalInstance.value.description !== '' ||
goalInstance.value.target !== 0 ||
selectedDate.value !== '') {
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: 'goals' })
}
}
const confirmCancel = () => {
router.push({ name: 'goals' })
confirmModalOpen.value = false;
}
</script>
<template>
......@@ -128,7 +173,7 @@ const deleteGoal = () => {
</div>
<div class="flex flex-col">
<p class="mx-4">Beskrivelse</p>
<p class="mx-4">Beskrivelse (valgfri)</p>
<textarea
v-model="goalInstance.description"
class="w-80 h-20 no-rezise"
......@@ -138,18 +183,17 @@ const deleteGoal = () => {
<div class="flex flex-col sm:flex-row gap-3">
<div class="flex flex-col">
<p class="mx-4">Kroner spart...</p>
<p class="mx-4">Kroner spart💸</p>
<input
v-model="goalInstance.saved"
class="w-40 text-right"
min="0"
placeholder="Sparebeløp"
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="goalInstance.target"
class="w-40 text-right"
......@@ -160,31 +204,85 @@ const deleteGoal = () => {
</div>
<ProgressBar :completion="completion" />
<div class="flex flex-col">
<p class="mx-4">Forfallsdato*</p>
<input
:min="minDate"
v-model="selectedDate"
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"
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
v-if="isEdit"
class="ml-2 bg-button-danger"
class="ml-2 primary danger"
@click="deleteGoal"
v-text="'Slett'"
/>
<button
v-else
class="ml-2 bg-button-other"
@click="router.push({ name: 'goals' })"
class="ml-2 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>
......@@ -193,4 +291,4 @@ const deleteGoal = () => {
.no-rezise {
resize: none;
}
</style>
</style>
\ No newline at end of file
......@@ -60,8 +60,10 @@ const changeOrder = async () => {
<template>
<div class="flex flex-col gap-5 items-center">
<h1 class="font-bold m-0">Dine sparemål</h1>
<button @click="router.push({ name: 'new-goal' })">Opprett et nytt sparemål</button>
<h2 class="font-thin m-0">Aktive sparemål</h2>
<button
class="primary"
@click="router.push({ name: 'new-goal' })">Opprett et nytt sparemål</button>
<h2 class="font-bold m-0">Aktive sparemål🚀</h2>
<p v-if="activeGoals.length === 0">Du har ingen aktive sparemål</p>
<draggable
v-else
......@@ -75,20 +77,28 @@ const changeOrder = async () => {
:key="index"
:class="[
{ 'cursor-move shadow-xl -translate-y-2 duration-300': isDraggable },
{ 'border-4 border-green-500': index === 0 }
{ 'border-2 border-lime-400': index === 0 },
{ 'border-2 border-slate-200 hover:bg-slate-50': index !== 0 }
]"
:goal-instance="element"
:is-clickable="!isDraggable"
/>
</template>
</draggable>
<button :disabled="activeGoals.length === 0" @click="changeOrder()">
<button
class="primary secondary"
:disabled="activeGoals.length === 0" @click="changeOrder()">
{{ isDraggable ? 'Lagre rekkefølge' : 'Endre rekkefølge' }}
</button>
<h2 class="font-thin m-0">Fullførte sparemål</h2>
<p v-if="completedGoals.length === 0">Du har ingen fullførte sparemål</p>
<h2 class="font-bold m-0">Fullførte sparemål💯</h2>
<p v-if="completedGoals.length === 0">Du har ingen fullførte sparemål😢</p>
<div v-else class="flex flex-row flex-wrap justify-center gap-10">
<CardGoal v-for="goal in completedGoals" :key="goal.id" :goal-instance="goal" />
<CardGoal
class="border-2 border-slate-200 hover:bg-slate-50"
v-for="goal in completedGoals"
:key="goal.id"
:goal-instance="goal"
/>
</div>
<PageControl
:current-page="currentPage"
......@@ -98,4 +108,5 @@ const changeOrder = async () => {
</div>
</template>
<style scoped></style>
<style scoped>
</style>
\ No newline at end of file
......@@ -73,29 +73,36 @@ const completeGoal = () => {
<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"
@click="router.push({ name: 'goals', params: { id: goalInstance.id } })"
class="w-min bg-transparent rounded-lg font-bold left-10 cursor-pointer transition-transform duration-200 ease-in-out hover:scale-110 hover:opacity-100 justify-start"
@click="router.push({ name: 'goals', params: { id: goalInstance.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">Sparemål:</h2>
<h2 class="font-light">
{{ goalInstance.title }}
</h2>
<p class="text-wrap break-words">{{ goalInstance.description }}</p>
<div class="flex flex-row gap-4 justify-center">
<p class="text-wrap break-words">{{ goalInstance.description }}</p>
<div>
<img
class="w-20 h-20"
src="https://cdn.pixabay.com/photo/2015/10/05/22/37/blank-profile-picture-973460_1280.png"
alt="Profilbilde"
/>
</div>
</div>
<br />
<p class="text-center">
Du har spart {{ goalInstance.saved }}kr av {{ goalInstance.target }}kr
</p>
<ProgressBar :completion="completion" />
</div>
<div class="flex flex-row justify-between gap-2 w-full">
<button
<button class="primary secondary mt-6"
v-if="!isCompleted"
@click="
router.push({
......@@ -106,15 +113,8 @@ const completeGoal = () => {
>
Rediger
</button>
<button
v-if="!isCompleted"
@click="completeGoal"
v-text="'Marker målet som ferdig'"
/>
<button
class="bg-button-danger hover:bg-button-danger"
class="danger mt-2 rounded-2xl p-1"
@click="
authInterceptor
.delete(`/goals/${goalInstance.id}`)
......@@ -124,9 +124,16 @@ const completeGoal = () => {
>
Slett
</button>
<button class="primary mt-4"
v-if="!isCompleted"
@click="completeGoal"
v-text="'Marker målet som ferdig'"
/>
</div>
</div>
<InteractiveSpare :png-size="10" :speech="motivation" direction="left" />
<div>
</div>
<InteractiveSpare :png-size="10" :speech="motivation" direction="left" />
</div>
</template>
......
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