diff --git a/src/components/CardGoal.vue b/src/components/CardGoal.vue index bceaaeb00071dd4752ae74d75326e69d8620dc8c..f04058b90dc740d2ec1d11b132f3cd6639344005 100644 --- a/src/components/CardGoal.vue +++ b/src/components/CardGoal.vue @@ -8,6 +8,10 @@ const props = defineProps({ goalInstance: { type: Object as PropType<Goal>, required: true + }, + isClickable: { + type: Boolean, + default: true } }) @@ -15,7 +19,11 @@ const goalInstance = props.goalInstance const displayDate = computed(() => goalInstance.due?.slice(0, 16).split('T').join(' ')) const isCompleted = computed(() => goalInstance.completedOn != null) -const handleCardClick = () => router.push({ name: 'view-goal', params: { id: goalInstance.id } }) +const handleCardClick = () => { + if (props.isClickable) { + router.push({ name: 'view-goal', params: { id: goalInstance.id } }) + } +} </script> <template> diff --git a/src/components/__tests__/CardGoalTest.spec.ts b/src/components/__tests__/CardGoalTest.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..1ada562db10c85a49537a0a0833891479848aa42 --- /dev/null +++ b/src/components/__tests__/CardGoalTest.spec.ts @@ -0,0 +1,67 @@ +import { describe, expect, it } from 'vitest' +import { mount } from '@vue/test-utils' +import CardGoal from '../CardGoal.vue' +import type { Goal } from '../../types/goal' + +describe('CardGoal', () => { + let incompleteWrapper: any + let completeWrapper: any + + const incompleteGoal: Goal = { + id: 1, + title: 'Test title', + saved: 100, + target: 1000, + description: 'Test description', + due: '2022-01-01T00:00:00Z', + createdOn: '2021-01-01T00:00:00Z', + completion: 10 + } + + const completeGoal: Goal = { + id: 1, + title: 'Test title', + saved: 100, + target: 1000, + description: 'Test description', + due: '2022-01-01T00:00:00Z', + createdOn: '2021-01-01T00:00:00Z', + completion: 10, + completedOn: '2022-01-01T00:00:00Z' + } + + const mountIncompletedWrapper = async () => { + incompleteWrapper = mount(CardGoal, { + propsData: { + goalInstance: incompleteGoal + } + }) + await incompleteWrapper.vm.$nextTick() + } + + const mountCompleteWrapper = async () => { + completeWrapper = mount(CardGoal, { + propsData: { + goalInstance: completeGoal + } + }) + await completeWrapper.vm.$nextTick() + } + + it('renders correctly', () => { + mountIncompletedWrapper() + expect(incompleteWrapper.text()).toContain('Test title') + expect(incompleteWrapper.text()).toContain('100kr / 1000kr') + expect(incompleteWrapper.text()).toContain('2022-01-01 00:00') + }) + + it('sets isCompleted to false', () => { + mountIncompletedWrapper() + expect(incompleteWrapper.vm.isCompleted).toBe(false) + }) + + it('sets isCompleted to true', () => { + mountCompleteWrapper() + expect(completeWrapper.vm.isCompleted).toBe(true) + }) +}) diff --git a/src/views/ManageGoalView.vue b/src/views/ManageGoalView.vue index 5ffe17e953a870a436d6f0235b30756aa55667de..e7815353e4866619109828db0314686fefccd1c8 100644 --- a/src/views/ManageGoalView.vue +++ b/src/views/ManageGoalView.vue @@ -7,24 +7,15 @@ import authInterceptor from '@/services/authInterceptor' const router = useRouter() -const oneWeekFromNow = new Date() -oneWeekFromNow.setDate(oneWeekFromNow.getDate() + 7) -const minDate = oneWeekFromNow.toISOString().slice(0, 16) - -const thirtyDaysFromNow = new Date() -thirtyDaysFromNow.setDate(thirtyDaysFromNow.getDate() + 30) -const maxDate = thirtyDaysFromNow.toISOString().slice(0, 16) +const selectedDate = ref<string>('') +const minDate = new Date(new Date().setDate(new Date().getDate() + 1)).toISOString().slice(0, 16) const goalInstance = ref<Goal>({ - id: 0, title: '', saved: 0, target: 100, - completion: 0, description: '', - priority: 0, - createdOn: undefined, - due: minDate + ':00.000Z' + due: '' }) watch( @@ -41,15 +32,11 @@ watch( } ) -const selectedDate = ref(minDate) watch( () => selectedDate.value, (newVal) => { - if (newVal) { - selectedDate.value = newVal < minDate ? minDate : newVal - goalInstance.value.due = selectedDate.value + ':00.000Z' - } - console.log(selectedDate.value) + if (newVal < minDate) selectedDate.value = minDate + goalInstance.value.due = newVal + ':00.000Z' } ) @@ -178,9 +165,8 @@ const deleteGoal = () => { <div class="flex flex-col"> <p class="mx-4">Forfallsdato*</p> <input - v-model="selectedDate" - :max="maxDate" :min="minDate" + v-model="selectedDate" placeholder="Forfallsdato" type="datetime-local" /> diff --git a/src/views/UserGoalsView.vue b/src/views/UserGoalsView.vue index 21cb66386a39be8b472de08ec9126987e224c4b6..558f7737c278aac8d840322c78315c7f8a653593 100644 --- a/src/views/UserGoalsView.vue +++ b/src/views/UserGoalsView.vue @@ -2,7 +2,7 @@ import CardGoal from '@/components/CardGoal.vue' import { useRouter } from 'vue-router' -import { onMounted, ref, watch } from 'vue' +import { onMounted, ref } from 'vue' import authInterceptor from '@/services/authInterceptor' import type { Goal } from '@/types/goal' import draggable from 'vuedraggable' @@ -16,24 +16,19 @@ const totalPages = ref(1) const activeGoals = ref<Goal[]>([]) const completedGoals = ref<Goal[]>([]) +const isDraggable = ref(false) + onMounted(async () => { await authInterceptor('/users/me/goals/active') .then((response) => { activeGoals.value = response.data + activeGoals.value.sort((a, b) => (a.priority || 0) - (b.priority || 0)) }) .catch((error) => { console.error(error) }) - await authInterceptor(`/users/me/goals/completed?page=${currentPage.value}&size=5`) - .then((response) => { - currentPage.value = response.data.number - totalPages.value = response.data.totalPages - completedGoals.value = response.data.content - }) - .catch((error) => { - console.error(error) - }) + await updatePage(0) }) const updatePage = async (page: number) => { @@ -48,34 +43,49 @@ const updatePage = async (page: number) => { }) } -watch(activeGoals, (newGoals) => { - console.log(newGoals) -}) +const changeOrder = async () => { + if (isDraggable.value) { + const priorities = activeGoals.value.map((goal) => goal.id) + await authInterceptor.put('/users/me/goals', priorities).catch((error) => { + console.error(error) + }) + isDraggable.value = false + await updatePage(currentPage.value) + } else { + isDraggable.value = true + } +} </script> <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-bold m-0">Aktive sparemål</h2> + <h2 class="font-thin m-0">Aktive sparemål</h2> <p v-if="activeGoals.length === 0">Du har ingen aktive sparemål</p> <draggable v-else v-model="activeGoals" class="flex flex-row flex-wrap justify-center gap-10" item-key="id" + :disabled="!isDraggable" > <template #item="{ element, index }"> - <CardGoal :key="index" :goal-instance="element" /> + <CardGoal + :key="index" + :class="[ + { 'cursor-move shadow-xl -translate-y-2 duration-300': isDraggable }, + { 'border-4 border-green-500': index === 0 } + ]" + :goal-instance="element" + :is-clickable="!isDraggable" + /> </template> </draggable> - <button - :disabled="activeGoals.length === 0" - @click="router.push({ name: 'edit-goal', params: { id: 1 } })" - > - Rediger rekkefølge + <button :disabled="activeGoals.length === 0" @click="changeOrder()"> + {{ isDraggable ? 'Lagre rekkefølge' : 'Endre rekkefølge' }} </button> - <h2 class="font-bold m-0">Fullførte sparemål</h2> + <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> <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" />