-
Eline Evje authoredEline Evje authored
ManageChallengeView.vue 10.33 KiB
<script lang="ts" setup>
import { useRouter } from 'vue-router'
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 uploadedFile = ref<File | null>(null)
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 = 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, 10)
const challengeInstance = ref<Challenge>({
title: '',
perPurchase: 0,
saved: 0,
target: 0,
description: '',
due: ''
})
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 submitButton = computed(() => (isEdit.value ? 'Oppdater' : 'Opprett'))
const completion = computed(
() => (challengeInstance.value.saved / challengeInstance.value.target) * 100
)
function validateInputs() {
const errors = []
challengeInstance.value.due = selectedDate.value + 'T23:59:59.999Z'
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
}
const handleFileChange = (event: Event) => {
const target = event.target as HTMLInputElement
if (target.files && target.files.length > 0) {
uploadedFile.value = target.files[0]
} else {
uploadedFile.value = null
}
}
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 {
let response
if (isEdit.value) {
response = await updateChallenge()
} else {
response = await createChallenge()
}
const challengeId = isEdit.value ? challengeInstance.value.id : response.id
if (uploadedFile.value && challengeId) {
const formData = new FormData()
formData.append('file', uploadedFile.value)
formData.append('id', challengeId.toString())
await authInterceptor.post('/challenges/picture', formData, {
headers: { 'Content-Type': 'multipart/form-data' }
})
}
await router.push({ name: 'challenges' })
} catch (error) {
console.error('Error during challenge submission:', error)
modalTitle.value = 'Systemfeil'
modalMessage.value = 'En feil oppstod under lagring av utfordringen.'
errorModalOpen.value = true
}
}
onMounted(async () => {
if (isEdit.value) {
const challengeId = router.currentRoute.value.params.id
if (!challengeId) return router.push({ name: 'challenges' })
await authInterceptor(`/challenges/${challengeId}`)
.then((response) => {
if (response.data.completedOn) {
router.push({ name: 'challenges' })
}
challengeInstance.value = response.data
selectedDate.value = response.data.due.slice(0, 16)
})
.catch((error) => {
console.error(error)
router.push({ name: 'challenges' })
})
}
})
const createChallenge = async () => {
const response = await authInterceptor.post('/challenges', challengeInstance.value)
return response.data
}
const updateChallenge = async () => {
const response = await authInterceptor.put(
`/challenges/${challengeInstance.value.id}`,
challengeInstance.value
)
return response.data
}
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
}
const removeUploadedFile = () => {
uploadedFile.value = null
}
</script>
<template>
<div class="flex flex-col justify-center items-center">
<h1 class="font-bold" v-text="pageTitle" />
<div class="flex flex-col gap-5 items-center justify-center">
<div class="flex flex-col">
<p class="mx-4">Tittel*</p>
<input
v-model="challengeInstance.title"
placeholder="Skriv en tittel"
type="text"
/>
</div>
<div class="flex flex-col">
<p class="mx-4">Beskrivelse (valgfri)</p>
<textarea
v-model="challengeInstance.description"
class="w-80 h-20 no-rezise"
placeholder="Beskriv sparemålet"
/>
</div>
<div class="flex flex-col sm:flex-row gap-3">
<div class="flex flex-col">
<p class="mx-4">Spare per gang</p>
<input
v-model="challengeInstance.perPurchase"
class="w-40 text-right"
placeholder="Kr spart per sparing"
type="number"
/>
</div>
<div class="flex flex-col">
<div class="flex flex-row justify-between mx-4">
<p>Kroner spart💸</p>
</div>
<input
v-model="challengeInstance.saved"
class="w-40 text-right"
placeholder="Sparebeløp"
type="number"
/>
</div>
<div class="flex flex-col">
<p class="mx-4">Av målbeløp💯*</p>
<input
v-model="challengeInstance.target"
class="w-40 text-right"
placeholder="Målbeløp"
type="number"
/>
</div>
</div>
<ProgressBar :completion="completion" />
<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 items-center">
<p>Last opp ikon for utfordringen📸</p>
<label
for="fileUpload"
class="bg-white text-black text-lg p-1 mt-2 rounded cursor-pointer leading-none"
>
💾
</label>
<input
id="fileUpload"
type="file"
accept=".jpg"
hidden
@change="handleFileChange"
/>
<div v-if="uploadedFile" class="flex justify-center items-center mt-2">
<p class="text-sm">{{ uploadedFile.name }}</p>
<button
@click="removeUploadedFile"
class="ml-2 text-xs font-bold border-2 p-1 rounded text-red-500"
>
Fjern fil
</button>
</div>
</div>
</div>
<div class="flex flex-row justify-between w-full">
<button 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:buttons>
<button class="primary" @click="errorModalOpen = false">Lukk</button>
</template>
</ModalComponent>
<ModalComponent
:title="modalTitle"
:message="modalMessage"
:isModalOpen="confirmModalOpen"
@close="confirmModalOpen = false"
>
<template v-slot:buttons>
<button class="primary" @click="confirmCancel">Bekreft</button>
<button class="primary danger" @click="confirmModalOpen = false">Avbryt</button>
</template>
</ModalComponent>
</div>
</div>
</template>
<style scoped>
.no-rezise {
resize: none;
}
</style>