Skip to content
Snippets Groups Projects
ManageGoalView.vue 11.1 KiB
Newer Older
Ina Martini's avatar
Ina Martini committed
<script lang="ts" setup>
import { useRouter } from 'vue-router'
import { computed, onMounted, type Ref, ref, watch } from 'vue'
Ina Martini's avatar
Ina Martini committed
import type { Goal } from '@/types/goal'
import ProgressBar from '@/components/ProgressBar.vue'
import authInterceptor from '@/services/authInterceptor'
import ModalComponent from '@/components/ModalComponent.vue'
import InteractiveSpare from '@/components/InteractiveSpare.vue'
Ina Martini's avatar
Ina Martini committed

const router = useRouter()
Eline Evje's avatar
Eline Evje committed
const uploadedFile: Ref<File | null> = ref(null)
Ina Martini's avatar
Ina Martini committed

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: 0,
    description: '',
    due: ''
})

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'
    goalInstance.value.saved = parseInt(goalInstance.value.saved.toString())
    goalInstance.value.target = parseInt(goalInstance.value.target.toString())
Ina Martini's avatar
Ina Martini committed

    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')
    }

    if (goalInstance.value.target < 1) {
        errors.push('Målbeløp må være større enn 0')
    }

    if (goalInstance.value.saved < 0) {
        errors.push('Sparebeløp kan ikke være negativt')
    }

    if (goalInstance.value.saved > goalInstance.value.target) {
        errors.push('Sparebeløp kan ikke være større enn målbeløp')
    }

    return errors
}
Ina Martini's avatar
Ina Martini committed
const submitAction = async () => {
    const errors = validateInputs()
    if (errors.length > 0) {
Eline Evje's avatar
Eline Evje committed
        const formatErrors = errors.join('<br>')
Ina Martini's avatar
Ina Martini committed
        modalTitle.value = 'Oops! Noe er feil med det du har fylt ut🚨'
Eline Evje's avatar
Eline Evje committed
        modalMessage.value = formatErrors
Ina Martini's avatar
Ina Martini committed
        errorModalOpen.value = true
        return
    }
Ina Martini's avatar
Ina Martini committed
    try {
Eline Evje's avatar
Eline Evje committed
        let response
Ina Martini's avatar
Ina Martini committed
        if (isEdit.value) {
Eline Evje's avatar
Eline Evje committed
            response = await updateGoal()
Ina Martini's avatar
Ina Martini committed
        } else {
Eline Evje's avatar
Eline Evje committed
            response = await createGoal()
Ina Martini's avatar
Ina Martini committed
        }
Eline Evje's avatar
Eline Evje committed
        const goalId = isEdit.value ? goalInstance.value.id : response.id // Adjusted to handle the returned data

        if (uploadedFile.value && goalId) {
Eline Evje's avatar
Eline Evje committed
            const formData = new FormData()
            formData.append('file', uploadedFile.value)
            formData.append('id', goalId.toString())

            await authInterceptor.post('/goals/picture', formData, {
Eline Evje's avatar
Eline Evje committed
                headers: { 'Content-Type': 'multipart/form-data' }
            })
Eline Evje's avatar
Eline Evje committed
        await router.push({ name: 'goals' })
Ina Martini's avatar
Ina Martini committed
    } catch (error) {
Eline Evje's avatar
Eline Evje committed
        console.error('Error during goal submission:', error)
Ina Martini's avatar
Ina Martini committed
        modalTitle.value = 'Systemfeil'
        modalMessage.value = 'En feil oppstod under lagring av utfordringen.'
        errorModalOpen.value = true
    }
}

onMounted(async () => {
    if (isEdit.value) {
        const goalId = router.currentRoute.value.params.id
        if (!goalId) return router.push({ name: 'goals' })

        await authInterceptor(`/goals/${goalId}`)
            .then((response) => {
                goalInstance.value = response.data
                selectedDate.value = response.data.due.slice(0, 10)
            })
            .catch((error) => {
                console.error(error)
                router.push({ name: 'goals' })
            })
    } else {
        goalInstance.value.due = selectedDate.value
    }
})

const createGoal = async (): Promise<any> => {
    try {
Eline Evje's avatar
Eline Evje committed
        const response = await authInterceptor.post('/goals', goalInstance.value)
        return response.data // Ensure the response data is returned
Eline Evje's avatar
Eline Evje committed
        console.error('Failed to create goal:', error)
        throw error // Rethrow the error to handle it in the submitAction method
const updateGoal = async (): Promise<any> => {
    try {
Eline Evje's avatar
Eline Evje committed
        const response = await authInterceptor.put(
            `/goals/${goalInstance.value.id}`,
            goalInstance.value
        )
        return response.data // Ensure the response data is returned
Eline Evje's avatar
Eline Evje committed
        console.error('Failed to update goal:', error)
        throw error // Rethrow the error to handle it in the submitAction method
Ina Martini's avatar
Ina Martini committed
}

const deleteGoal = () => {
    authInterceptor
        .delete(`/goals/${goalInstance.value.id}`)
        .then(() => {
            router.push({ name: 'goals' })
        })
        .catch((error) => {
            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
}

const handleFileChange = (event: Event) => {
Eline Evje's avatar
Eline Evje committed
    const target = event.target as HTMLInputElement
    if (target.files && target.files.length > 0) {
Eline Evje's avatar
Eline Evje committed
        uploadedFile.value = target.files[0] // Save the first selected file
Eline Evje's avatar
Eline Evje committed
        uploadedFile.value = null
Eline Evje's avatar
Eline Evje committed
}

const removeUploadedFile = () => {
Eline Evje's avatar
Eline Evje committed
    uploadedFile.value = null
}
Ina Martini's avatar
Ina Martini committed
</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="goalInstance.title" placeholder="Skriv en tittel" type="text" />
            </div>

            <div class="flex flex-col">
                <p class="mx-4">Beskrivelse (valgfri)</p>
                <textarea
                    v-model="goalInstance.description"
                    class="w-80 h-20 no-rezise"
                    placeholder="Beskriv sparemålet"
                />
            </div>

            <div class="flex flex-col sm:flex-row gap-3">
Ina Martini's avatar
Ina Martini committed
                <div class="flex flex-col">
                    <p class="mx-4">Kroner spart💸</p>
                    <input
                        v-model="goalInstance.saved"
                        class="w-40 text-right"
                        placeholder="Sparebeløp"
                        type="number"
                    />
Ina Martini's avatar
Ina Martini committed
                </div>

                <div class="flex flex-col">
                    <p class="mx-4">Av målbeløp💯*</p>
                    <input
                        v-model="goalInstance.target"
                        class="w-40 text-right"
                        placeholder="Målbeløp"
                        type="number"
Ina Martini's avatar
Ina Martini committed
                    />
                </div>
            </div>
            <ProgressBar :completion="completion" />
Ina Martini's avatar
Ina Martini committed

            <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"
                    />
Ina Martini's avatar
Ina Martini committed
                </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 cursor-pointer leading-none rounded-full border p-3 border-black"
Ina Martini's avatar
Ina Martini committed
                        >
                            Legg til 💾
                        </label>
                        <input
                            id="fileUpload"
                            type="file"
                            accept=".jpg, .png"
                            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>
Ina Martini's avatar
Ina Martini committed
                </div>
            </div>

            <div class="flex flex-row justify-between w-full">
                <button
                    v-if="isEdit"
                    class="ml-2 primary danger"
                    @click="deleteGoal"
                    v-text="'Slett'"
                />
                <button
                    v-else
                    class="ml-2 primary danger"
                    @click="cancelCreation"
                    v-text="'Avbryt'"
Ina Martini's avatar
Ina Martini committed
                />
                <button class="primary" @click="submitAction" v-text="submitButton" />
Ina Martini's avatar
Ina Martini committed
            </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>

Ina Martini's avatar
Ina Martini committed
                <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
            class="lg:absolute right-5 lg:top-1/3 max-lg:bottom-0 max-lg:mt-44 transform -translate-y-1/2 lg:w-1/4 lg:max-w-xs"
Ina Martini's avatar
Ina Martini committed
                <InteractiveSpare
                    :png-size="10"
                    :speech="[`Trenger du hjelp? Trykk på ❓ nede i høyre hjørne!`]"
Ina Martini's avatar
Ina Martini committed
                    direction="left"
                />
            </div>
Ina Martini's avatar
Ina Martini committed
</template>

<style scoped>
.no-rezise {
    resize: none;
}
</style>