254 lines
8.3 KiB
Vue
254 lines
8.3 KiB
Vue
<script setup>
|
|
import { nextTick, ref, useTemplateRef } from 'vue';
|
|
import { flash } from '@/methods';
|
|
import { trans } from 'laravel-vue-i18n';
|
|
import HoverMenu from '@/Shared/HoverMenu.vue';
|
|
import PrettyButton from '@/Shared/Form/PrettyButton.vue';
|
|
import CreateOrEditTask from '@/Shared/Modules/TaskItems/CreateOrEditTask.vue';
|
|
import DateIcon from '@/Shared/Icons/DateIcon.vue';
|
|
import { LayoutList } from 'lucide-vue-next';
|
|
|
|
const props = defineProps({
|
|
data: Object,
|
|
});
|
|
|
|
const createTaskForm = useTemplateRef('createTaskForm');
|
|
const updateTaskForm = useTemplateRef('updateTaskForm');
|
|
const updateCompletedTaskForm = useTemplateRef('updateCompletedTaskForm');
|
|
const createTaskModalShown = ref(false);
|
|
const showCompletedTasks = ref(false);
|
|
const localTasks = ref(props.data.tasks);
|
|
const localCompletedTasks = ref([]);
|
|
const editedTaskId = ref(0);
|
|
const editedCompletedTaskId = ref(0);
|
|
|
|
const showCreateTaskModal = () => {
|
|
createTaskModalShown.value = true;
|
|
|
|
nextTick().then(() => createTaskForm.value.reset());
|
|
};
|
|
|
|
const showUpdateTaskModal = (task) => {
|
|
editedTaskId.value = task.id;
|
|
|
|
nextTick().then(() => updateTaskForm.value[0].reset());
|
|
};
|
|
|
|
const showUpdateCompletedTaskModal = (task) => {
|
|
editedCompletedTaskId.value = task.id;
|
|
|
|
nextTick().then(() => updateCompletedTaskForm.value[0].reset());
|
|
};
|
|
|
|
const getCompleted = () => {
|
|
axios.get(props.data.url.completed).then((response) => {
|
|
localCompletedTasks.value = response.data.data;
|
|
showCompletedTasks.value = true;
|
|
});
|
|
};
|
|
|
|
const created = (task) => {
|
|
flash(trans('The task has been created'), 'success');
|
|
localTasks.value.unshift(task);
|
|
createTaskModalShown.value = false;
|
|
};
|
|
|
|
const updated = (task) => {
|
|
flash(trans('The task has been updated'), 'success');
|
|
localTasks.value[localTasks.value.findIndex((x) => x.id === task.id)] = task;
|
|
editedTaskId.value = 0;
|
|
};
|
|
|
|
const updatedCompleted = (task) => {
|
|
flash(trans('The task has been updated'), 'success');
|
|
localCompletedTasks.value[localCompletedTasks.value.findIndex((x) => x.id === task.id)] = task;
|
|
editedCompletedTaskId.value = 0;
|
|
};
|
|
|
|
const toggle = (task) => {
|
|
axios.put(task.url.toggle).catch((error) => {
|
|
form.errors = error.response.data;
|
|
});
|
|
};
|
|
|
|
const destroy = (task) => {
|
|
if (confirm(trans('Are you sure? This action cannot be undone.'))) {
|
|
axios
|
|
.delete(task.url.destroy)
|
|
.then(() => {
|
|
flash(trans('The task has been deleted'), 'success');
|
|
var id = localTasks.value.findIndex((x) => x.id === task.id);
|
|
localTasks.value.splice(id, 1);
|
|
})
|
|
.catch((error) => {
|
|
form.errors = error.response.data;
|
|
});
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<div class="mb-10">
|
|
<!-- title + cta -->
|
|
<div class="mb-3 items-center justify-between border-b border-gray-200 pb-2 dark:border-gray-700 sm:flex">
|
|
<div class="mb-2 sm:mb-0 flex items-center gap-2">
|
|
<LayoutList class="h-4 w-4 text-gray-600" />
|
|
|
|
<span class="font-semibold"> {{ $t('Tasks') }} </span>
|
|
</div>
|
|
<PrettyButton :text="$t('Add a task')" :icon="'plus'" :class="'w-full sm:w-fit'" @click="showCreateTaskModal()" />
|
|
</div>
|
|
|
|
<!-- add a task modal -->
|
|
<CreateOrEditTask
|
|
class="mb-6"
|
|
v-if="createTaskModalShown"
|
|
:data="data"
|
|
ref="createTaskForm"
|
|
@created="created"
|
|
@close="createTaskModalShown = false" />
|
|
|
|
<!-- tasks -->
|
|
<ul
|
|
v-if="localTasks.length > 0"
|
|
class="mb-2 rounded-lg border border-gray-200 bg-white dark:border-gray-700 dark:bg-gray-900">
|
|
<li
|
|
v-for="task in localTasks"
|
|
:key="task.id"
|
|
class="item-list border-b border-gray-200 hover:bg-slate-50 dark:border-gray-700 dark:bg-slate-900 dark:hover:bg-slate-800">
|
|
<div v-if="editedTaskId !== task.id" class="flex items-center justify-between p-3">
|
|
<div class="flex items-center">
|
|
<input
|
|
:id="task.id"
|
|
v-model="task.completed"
|
|
:name="task.id"
|
|
type="checkbox"
|
|
class="focus:ring-3 relative h-4 w-4 rounded-xs border border-gray-300 bg-gray-50 focus:ring-blue-300 dark:border-gray-600 dark:bg-gray-700 dark:ring-offset-gray-800 dark:focus:ring-blue-600"
|
|
@change="toggle(task)" />
|
|
<label :for="task.id" class="ms-2 flex cursor-pointer text-gray-900 dark:text-gray-50">
|
|
{{ task.label }}
|
|
|
|
<!-- due date -->
|
|
<span
|
|
v-if="task.due_at !== null"
|
|
:class="
|
|
task.due_at.is_late
|
|
? 'bg-red-400/10 text-red-600 dark:bg-red-600/10 dark:text-red-400'
|
|
: 'bg-sky-400/10 text-sky-600 dark:bg-sky-600/10 dark:text-sky-400'
|
|
"
|
|
class="ms-2 flex items-center rounded-full px-2 py-0.5 text-xs font-medium leading-5">
|
|
<DateIcon />
|
|
<span>{{ task.due_at.formatted }}</span>
|
|
</span>
|
|
</label>
|
|
</div>
|
|
|
|
<HoverMenu :show-edit="true" :show-delete="true" @edit="showUpdateTaskModal(task)" @delete="destroy(task)" />
|
|
</div>
|
|
|
|
<!-- edit task -->
|
|
<CreateOrEditTask
|
|
class="mb-3"
|
|
v-if="editedTaskId === task.id"
|
|
ref="updateTaskForm"
|
|
:data="data"
|
|
:task="task"
|
|
@update:task="updated"
|
|
@close="editedTaskId = 0" />
|
|
</li>
|
|
</ul>
|
|
|
|
<!-- button to display completed tasks -->
|
|
<p
|
|
v-if="data.completed_tasks_count > 0 && !showCompletedTasks"
|
|
class="mx-4 mb-6 cursor-pointer text-xs text-blue-500 hover:underline"
|
|
@click="getCompleted()">
|
|
{{ $t('Show completed tasks (:count)', { count: data.completed_tasks_count }) }}
|
|
</p>
|
|
|
|
<!-- list of completed tasks -->
|
|
<div v-if="showCompletedTasks" class="mx-4 text-xs">
|
|
<ul
|
|
v-for="task in localCompletedTasks"
|
|
:key="task.id"
|
|
class="mb-2 rounded-lg border border-gray-200 bg-white dark:border-gray-700 dark:bg-gray-900">
|
|
<li>
|
|
<div v-if="editedCompletedTaskId !== task.id" class="flex items-center justify-between p-3">
|
|
<div class="flex items-center">
|
|
<input
|
|
:id="task.id"
|
|
v-model="task.completed"
|
|
:name="task.id"
|
|
type="checkbox"
|
|
class="focus:ring-3 relative h-4 w-4 rounded-xs border border-gray-300 bg-gray-50 focus:ring-blue-300 dark:border-gray-600 dark:bg-gray-700 dark:ring-offset-gray-800 dark:focus:ring-blue-600"
|
|
@change="toggle(task)" />
|
|
|
|
<label :for="task.id" class="ms-2 flex cursor-pointer items-center text-gray-900 dark:text-gray-50">
|
|
{{ task.label }}
|
|
|
|
<!-- due date -->
|
|
<span
|
|
v-if="task.due_at !== null"
|
|
class="ms-2 flex items-center rounded-full bg-sky-400/10 px-2 py-0.5 text-xs font-medium leading-5 text-sky-600 dark:text-sky-400">
|
|
<DateIcon />
|
|
<span>{{ task.due_at.formatted }}</span>
|
|
</span>
|
|
</label>
|
|
</div>
|
|
|
|
<HoverMenu
|
|
:show-edit="true"
|
|
:show-delete="true"
|
|
@edit="showUpdateCompletedTaskModal(task)"
|
|
@delete="destroy(task)" />
|
|
</div>
|
|
|
|
<!-- edit task -->
|
|
<CreateOrEditTask
|
|
v-if="editedCompletedTaskId === task.id"
|
|
ref="updateCompletedTaskForm"
|
|
:data="data"
|
|
:task="task"
|
|
@update:task="updatedCompleted"
|
|
@close="editedCompletedTaskId = 0" />
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<!-- blank state -->
|
|
<div
|
|
v-if="localTasks.length === 0"
|
|
class="mb-6 rounded-lg border border-gray-200 bg-white dark:border-gray-700 dark:bg-gray-900">
|
|
<img src="/img/dashboard_blank_tasks.svg" :alt="$t('Tasks')" class="mx-auto mt-4 h-14 w-14" />
|
|
<p class="px-5 pb-5 pt-2 text-center">{{ $t('There are no tasks yet.') }}</p>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style lang="scss" scoped>
|
|
.icon-sidebar {
|
|
color: #737e8d;
|
|
top: -2px;
|
|
}
|
|
|
|
.item-list {
|
|
&:hover:first-child {
|
|
border-top-left-radius: 8px;
|
|
border-top-right-radius: 8px;
|
|
}
|
|
|
|
&:last-child {
|
|
border-bottom: 0;
|
|
}
|
|
|
|
&:hover:last-child {
|
|
border-bottom-left-radius: 8px;
|
|
border-bottom-right-radius: 8px;
|
|
}
|
|
}
|
|
|
|
input[type='checkbox'] {
|
|
top: -1px;
|
|
}
|
|
</style>
|