dragging task in the calendar works visually
This commit is contained in:
@@ -6,6 +6,7 @@ import type { DateTime } from 'luxon';
|
|||||||
|
|
||||||
const events = defineModel<Event[]>('events', { required: true })
|
const events = defineModel<Event[]>('events', { required: true })
|
||||||
const date = defineModel<DateTime>('date', { required: true })
|
const date = defineModel<DateTime>('date', { required: true })
|
||||||
|
const draggedTask = defineModel<DraggedTask | undefined>('draggedTask', { required: true })
|
||||||
|
|
||||||
const emits = defineEmits<{
|
const emits = defineEmits<{
|
||||||
(e: 'createEvent', event: Event): void
|
(e: 'createEvent', event: Event): void
|
||||||
@@ -15,7 +16,9 @@ const emits = defineEmits<{
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<UCard class="flex grow" :ui="{ body: 'w-full h-full' }">
|
<UCard class="flex grow" :ui="{ body: 'w-full h-full' }">
|
||||||
<Calendar @create="(event) => emits('createEvent', event)"v-model:events="events" v-model:date="date"></Calendar>
|
<Calendar @create="(event) => emits('createEvent', event)" v-model:events="events" v-model:date="date" ,
|
||||||
|
v-model:dragged-task="draggedTask">
|
||||||
|
</Calendar>
|
||||||
</UCard>
|
</UCard>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { DateTime } from 'luxon';
|
|||||||
|
|
||||||
const colorMode = useColorMode();
|
const colorMode = useColorMode();
|
||||||
const toast = useToast()
|
const toast = useToast()
|
||||||
|
const instance = getCurrentInstance()
|
||||||
|
|
||||||
const currentTheme = ref<'dark' | 'system' | 'light'>(colorMode.preference as 'dark' | 'system' | 'light');
|
const currentTheme = ref<'dark' | 'system' | 'light'>(colorMode.preference as 'dark' | 'system' | 'light');
|
||||||
const showTaskCreateModal = ref(false);
|
const showTaskCreateModal = ref(false);
|
||||||
@@ -20,6 +21,7 @@ const emits = defineEmits<{
|
|||||||
(e: 'createTask', task: Task): void
|
(e: 'createTask', task: Task): void
|
||||||
(e: 'deleteTask', id: number): void
|
(e: 'deleteTask', id: number): void
|
||||||
(e: 'editTask', task: Task): void
|
(e: 'editTask', task: Task): void
|
||||||
|
(e: 'scheduleTask', task: Task): void
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const isLight = computed(() => currentTheme.value === 'light');
|
const isLight = computed(() => currentTheme.value === 'light');
|
||||||
@@ -96,6 +98,7 @@ function deleteTask(task: Task) {
|
|||||||
|
|
||||||
emits('deleteTask', task.id)
|
emits('deleteTask', task.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
function editTask(task: Task) {
|
function editTask(task: Task) {
|
||||||
emits('editTask', task)
|
emits('editTask', task)
|
||||||
}
|
}
|
||||||
@@ -110,6 +113,10 @@ function openTaskEditModal(task: Task) {
|
|||||||
showTaskEditModal.value = true
|
showTaskEditModal.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function scheduleTask(task: Task) {
|
||||||
|
emits('scheduleTask', task)
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -129,7 +136,8 @@ function openTaskEditModal(task: Task) {
|
|||||||
<Title1>Tasks</Title1>
|
<Title1>Tasks</Title1>
|
||||||
<div class="flex gap-2 flex-col">
|
<div class="flex gap-2 flex-col">
|
||||||
<ListItem v-for="task in todoTasks">
|
<ListItem v-for="task in todoTasks">
|
||||||
<div class="flex w-full gap-4 items-center">
|
<div class="flex w-full gap-4 items-center" @dragstart="scheduleTask(task)"
|
||||||
|
draggable="true">
|
||||||
<span
|
<span
|
||||||
class="grow overflow-scroll py-3 overflow-shadow flex flex-row gap-2 items-center">
|
class="grow overflow-scroll py-3 overflow-shadow flex flex-row gap-2 items-center">
|
||||||
<UCheckbox v-model="task.done" @change="() => editTask(task)" />{{ task.title }}
|
<UCheckbox v-model="task.done" @change="() => editTask(task)" />{{ task.title }}
|
||||||
@@ -144,7 +152,8 @@ function openTaskEditModal(task: Task) {
|
|||||||
</ListItem>
|
</ListItem>
|
||||||
<USeparator label="Done" v-if="todoTasks.length !== 0" />
|
<USeparator label="Done" v-if="todoTasks.length !== 0" />
|
||||||
<ListItem v-for="task in doneTasks">
|
<ListItem v-for="task in doneTasks">
|
||||||
<div class="flex w-full gap-4 items-center">
|
<div class="flex w-full gap-4 items-center" @dragstart="scheduleTask(task)"
|
||||||
|
draggable="true">
|
||||||
<span
|
<span
|
||||||
class="grow overflow-scroll py-3 overflow-shadow flex flex-row gap-2 items-center">
|
class="grow overflow-scroll py-3 overflow-shadow flex flex-row gap-2 items-center">
|
||||||
<UCheckbox v-model="task.done" @change="() => editTask(task)" />{{ task.title }}
|
<UCheckbox v-model="task.done" @change="() => editTask(task)" />{{ task.title }}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import * as z from 'zod';
|
|||||||
const open = defineModel<boolean>('open', { required: true })
|
const open = defineModel<boolean>('open', { required: true })
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: 'submnitted', event: SimpleTask): void
|
(e: 'submnitted', event: Task): void
|
||||||
(e: 'canceled'): void
|
(e: 'canceled'): void
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
@@ -63,14 +63,15 @@ function submit() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
emit('submnitted', {
|
emit('submnitted', Task.fromSimpleTask({
|
||||||
id: props.input.id,
|
id: props.input.id,
|
||||||
title: form.data.title,
|
title: form.data.title,
|
||||||
done: props.input.done ?? false,
|
done: props.input.done ?? false,
|
||||||
description: form.data.description,
|
description: form.data.description,
|
||||||
estimated_time: form.data.estimated_time,
|
estimated_time: form.data.estimated_time,
|
||||||
due_date: DateTime.fromISO(form.data.due_date)
|
due_date: DateTime.fromISO(form.data.due_date),
|
||||||
})
|
scheduled_at: props.input.scheduled_at
|
||||||
|
}))
|
||||||
open.value = false
|
open.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import EventFormModal from '../EventFormModal.vue';
|
|||||||
|
|
||||||
const events = defineModel<Event[]>('events', { required: true })
|
const events = defineModel<Event[]>('events', { required: true })
|
||||||
const date = defineModel<DateTime>('date', { required: true })
|
const date = defineModel<DateTime>('date', { required: true })
|
||||||
|
const draggedTask = defineModel<DraggedTask | undefined>('draggedTask', { required: true })
|
||||||
const draggedEvent = ref<DraggedEvent | undefined>()
|
const draggedEvent = ref<DraggedEvent | undefined>()
|
||||||
const createInput = ref<Partial<SimpleEvent>>({})
|
const createInput = ref<Partial<SimpleEvent>>({})
|
||||||
const createModalOpened = ref(false)
|
const createModalOpened = ref(false)
|
||||||
@@ -161,7 +162,8 @@ function moveEvent(event: Event) {
|
|||||||
<EventFormModal action="edit" @submnitted="event => edit(event)" :input="editInput"
|
<EventFormModal action="edit" @submnitted="event => edit(event)" :input="editInput"
|
||||||
v-model:open="editModalOpened" />
|
v-model:open="editModalOpened" />
|
||||||
|
|
||||||
<UModal v-model:open="deleteModalOpened" title="Delete Event" description="Are you sure you want to delete this event?">
|
<UModal v-model:open="deleteModalOpened" title="Delete Event"
|
||||||
|
description="Are you sure you want to delete this event?">
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<UButton variant="solid" @click="deleteEvent">
|
<UButton variant="solid" @click="deleteEvent">
|
||||||
Delete
|
Delete
|
||||||
@@ -176,8 +178,8 @@ function moveEvent(event: Event) {
|
|||||||
<CalendarHeader :seperators="seperators" />
|
<CalendarHeader :seperators="seperators" />
|
||||||
|
|
||||||
<CalendarCollumn v-for="day in days" :seperators="seperators" :day="day.date" :events="day.events"
|
<CalendarCollumn v-for="day in days" :seperators="seperators" :day="day.date" :events="day.events"
|
||||||
:date="date" v-model:draggedEvent="draggedEvent" @quick-create="openCreateModal"
|
:date="date" v-model:draggedEvent="draggedEvent" @quick-create="openCreateModal" @edit="openEditModal"
|
||||||
@edit="openEditModal" @delete="openDeleteModal" @moved="moveEvent" />
|
@delete="openDeleteModal" @moved="moveEvent" v-model:dragged-task="draggedTask" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ const startY = ref(0)
|
|||||||
const endY = ref(0)
|
const endY = ref(0)
|
||||||
const column = useTemplateRef('column')
|
const column = useTemplateRef('column')
|
||||||
const draggedEvent = defineModel<DraggedEvent | undefined>('draggedEvent')
|
const draggedEvent = defineModel<DraggedEvent | undefined>('draggedEvent')
|
||||||
|
const draggedTask = defineModel<DraggedTask | undefined>('draggedTask')
|
||||||
|
|
||||||
const height = computed(() => {
|
const height = computed(() => {
|
||||||
return Math.abs(endY.value - startY.value)
|
return Math.abs(endY.value - startY.value)
|
||||||
@@ -83,6 +84,29 @@ function eventMove(mouseEvent: MouseEvent, event: Event) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function dragover(e: DragEvent) {
|
function dragover(e: DragEvent) {
|
||||||
|
drawDraggedEvent(e)
|
||||||
|
drawDraggedTask(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawDraggedTask(event: DragEvent) {
|
||||||
|
if (draggedTask.value === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (draggedTask.value.dragInfo === undefined) {
|
||||||
|
draggedTask.value.dragInfo = {
|
||||||
|
height: (draggedTask.value.target.estimated_time / 60 / 24) * (column.value?.offsetHeight ?? 0),
|
||||||
|
top: absoluteToRelativeY(event.clientY),
|
||||||
|
date: props.day
|
||||||
|
}
|
||||||
|
drawDraggedTask(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
draggedTask.value.dragInfo.top = absoluteToRelativeY(event.clientY)
|
||||||
|
draggedTask.value.dragInfo.date = props.day
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawDraggedEvent(event: DragEvent) {
|
||||||
if (draggedEvent.value === undefined) {
|
if (draggedEvent.value === undefined) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -92,13 +116,13 @@ function dragover(e: DragEvent) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
draggedEvent.value.top = absoluteToRelativeY(e.clientY) - draggedEvent.value.offset
|
draggedEvent.value.top = absoluteToRelativeY(event.clientY) - draggedEvent.value.offset
|
||||||
}
|
}
|
||||||
|
|
||||||
function dragDrop(_: DragEvent) {
|
function dragDrop(_: DragEvent) {
|
||||||
draggedEvent.value?.target.updateWithDraggedEvent(draggedEvent.value, column.value?.offsetHeight ?? 0)
|
draggedEvent.value?.target.updateWithDraggedEvent(draggedEvent.value, column.value?.offsetHeight ?? 0)
|
||||||
|
|
||||||
if (draggedEvent.value === undefined){
|
if (draggedEvent.value === undefined) {
|
||||||
draggedEvent.value = undefined
|
draggedEvent.value = undefined
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -134,6 +158,9 @@ function dragDrop(_: DragEvent) {
|
|||||||
<div v-if="draggedEvent !== undefined && draggedEvent.date.equals(props.day)"
|
<div v-if="draggedEvent !== undefined && draggedEvent.date.equals(props.day)"
|
||||||
class="absolute w-11/12 top-20 bg-black opacity-45 rounded-lg"
|
class="absolute w-11/12 top-20 bg-black opacity-45 rounded-lg"
|
||||||
:style="{ height: `${draggedEvent.height}px`, top: `${draggedEvent.top}px` }"></div>
|
:style="{ height: `${draggedEvent.height}px`, top: `${draggedEvent.top}px` }"></div>
|
||||||
|
<div v-if="draggedTask !== undefined && draggedTask.dragInfo !== undefined && draggedTask.dragInfo.date.equals(props.day)"
|
||||||
|
class="absolute w-11/12 top-20 bg-black opacity-45 rounded-lg"
|
||||||
|
:style="{ height: `${draggedTask.dragInfo.height}px`, top: `${draggedTask.dragInfo.top}px` }"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { Event, type SerializableEvent } from '~/utils/event';
|
|||||||
const date = ref<DateTime>(DateTime.now())
|
const date = ref<DateTime>(DateTime.now())
|
||||||
const events = ref<Event[]>([])
|
const events = ref<Event[]>([])
|
||||||
const tasks = ref<Task[]>([])
|
const tasks = ref<Task[]>([])
|
||||||
|
const draggedTask = ref<DraggedTask | undefined>(undefined)
|
||||||
|
|
||||||
const { data: eventsResponse } = await useAsyncData<SerializableEvent[]>(
|
const { data: eventsResponse } = await useAsyncData<SerializableEvent[]>(
|
||||||
'events',
|
'events',
|
||||||
@@ -42,13 +43,18 @@ async function deleteTask(id: number) {
|
|||||||
await refresh()
|
await refresh()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function scheduleTask(task: Task) {
|
||||||
|
draggedTask.value = { target: task, dragInfo: undefined }
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="h-screen w-screen p-4 flex flex-row gap-5">
|
<div class="h-screen w-screen p-4 flex flex-row gap-5">
|
||||||
<Sidebar v-if="tasks !== null" v-model:tasks="tasks" v-model:date="date" @create-task="postTask"
|
<Sidebar v-if="tasks !== null" v-model:tasks="tasks" v-model:date="date" @create-task="postTask"
|
||||||
@delete-task="deleteTask" />
|
@delete-task="deleteTask" @schedule-task="scheduleTask"/>
|
||||||
<MainContent v-if="events !== null" v-model:events="events" v-model:date="date" @create-event="postEvent" />
|
<MainContent v-if="events !== null" v-model:events="events" v-model:date="date"
|
||||||
|
v-model:dragged-task="draggedTask" @create-event="postEvent" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,8 @@ export class Task {
|
|||||||
public description: string,
|
public description: string,
|
||||||
public done: boolean,
|
public done: boolean,
|
||||||
public estimated_time: number,
|
public estimated_time: number,
|
||||||
public due_date: DateTime | undefined
|
public due_date: DateTime | undefined,
|
||||||
|
public scheduled_at: DateTime | undefined
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
static fromSimpleTask(simpleTask: SimpleTask) {
|
static fromSimpleTask(simpleTask: SimpleTask) {
|
||||||
@@ -17,7 +18,8 @@ export class Task {
|
|||||||
simpleTask.description,
|
simpleTask.description,
|
||||||
simpleTask.done,
|
simpleTask.done,
|
||||||
simpleTask.estimated_time,
|
simpleTask.estimated_time,
|
||||||
simpleTask.due_date
|
simpleTask.due_date,
|
||||||
|
simpleTask.scheduled_at
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,9 +31,18 @@ export class Task {
|
|||||||
serializableTask.description,
|
serializableTask.description,
|
||||||
serializableTask.done,
|
serializableTask.done,
|
||||||
serializableTask.estimated_time,
|
serializableTask.estimated_time,
|
||||||
DateTime.now()
|
stringToDate(serializableTask.due_date),
|
||||||
|
stringToDate(serializableTask.scheduled_at),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isPersistent() {
|
||||||
|
return this.id !== undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
isScheduled() {
|
||||||
|
return this.scheduled_at !== undefined
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SimpleTask = {
|
export type SimpleTask = {
|
||||||
@@ -40,6 +51,7 @@ export type SimpleTask = {
|
|||||||
description: string
|
description: string
|
||||||
done: boolean
|
done: boolean
|
||||||
estimated_time: number
|
estimated_time: number
|
||||||
|
scheduled_at: DateTime | undefined
|
||||||
due_date: DateTime | undefined
|
due_date: DateTime | undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,7 +62,24 @@ export type SerializableTask = {
|
|||||||
done: boolean
|
done: boolean
|
||||||
estimated_time: number
|
estimated_time: number
|
||||||
due_date: string | undefined
|
due_date: string | undefined
|
||||||
|
scheduled_at: string | undefined
|
||||||
created_at: string
|
created_at: string
|
||||||
updated_at: string
|
updated_at: string
|
||||||
userid: string
|
userid: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type DraggedTask = {
|
||||||
|
target: Task,
|
||||||
|
dragInfo: {
|
||||||
|
top: number,
|
||||||
|
date: DateTime
|
||||||
|
height: number
|
||||||
|
} | undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
function stringToDate(date: string | undefined) {
|
||||||
|
if (date === undefined) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
return DateTime.fromISO(date)
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user