feat: add basic implementation of ganttastic
This commit is contained in:
parent
a84fb8b5df
commit
a0e6b9643b
5 changed files with 136 additions and 2 deletions
|
@ -19,6 +19,7 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@github/hotkey": "2.0.1",
|
"@github/hotkey": "2.0.1",
|
||||||
|
"@infectoone/vue-ganttastic": "^2.0.4",
|
||||||
"@kyvg/vue3-notification": "2.3.6",
|
"@kyvg/vue3-notification": "2.3.6",
|
||||||
"@sentry/tracing": "7.10.0",
|
"@sentry/tracing": "7.10.0",
|
||||||
"@sentry/vue": "7.10.0",
|
"@sentry/vue": "7.10.0",
|
||||||
|
|
118
src/components/tasks/gantt-chart.vue
Normal file
118
src/components/tasks/gantt-chart.vue
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
<template>
|
||||||
|
<g-gantt-chart
|
||||||
|
:chart-start="dateFromFormatted"
|
||||||
|
:chart-end="dateToFormatted"
|
||||||
|
precision="day"
|
||||||
|
bar-start="startDate"
|
||||||
|
bar-end="endDate"
|
||||||
|
:grid="true"
|
||||||
|
@dragend-bar="updateTask"
|
||||||
|
>
|
||||||
|
<g-gantt-row
|
||||||
|
v-for="bar in ganttBars"
|
||||||
|
label=""
|
||||||
|
:bars="bar"
|
||||||
|
/>
|
||||||
|
</g-gantt-chart>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import {computed, ref} from 'vue'
|
||||||
|
import TaskCollectionService from '@/services/taskCollection'
|
||||||
|
import {format} from 'date-fns'
|
||||||
|
import {colorIsDark} from '@/helpers/color/colorIsDark'
|
||||||
|
import TaskService from '@/services/task'
|
||||||
|
|
||||||
|
const dateFormat = 'yyyy-LL-dd kk:mm'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
listId: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
dateFrom: {
|
||||||
|
default: () => new Date(new Date().setDate(new Date().getDate() - 15)),
|
||||||
|
},
|
||||||
|
dateTo: {
|
||||||
|
default: () => new Date(new Date().setDate(new Date().getDate() + 30)),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const dateFromFormatted = computed(() => format(props.dateFrom, dateFormat))
|
||||||
|
const dateToFormatted = computed(() => format(props.dateTo, dateFormat))
|
||||||
|
|
||||||
|
const tasks = ref([])
|
||||||
|
|
||||||
|
const ganttBars = ref([])
|
||||||
|
|
||||||
|
// We need a "real" ref object for the gantt bars to instantly update the tasks when they are dragged on the chart.
|
||||||
|
// A computed won't work directly.
|
||||||
|
function mapGanttBars() {
|
||||||
|
const defaultStartDate = '2022-07-19 12:00'
|
||||||
|
const defaultEndDate = '2022-07-25 12:00'
|
||||||
|
|
||||||
|
tasks.value.forEach(t => ganttBars.value.push([{
|
||||||
|
startDate: t.startDate ? format(t.startDate, dateFormat) : defaultStartDate,
|
||||||
|
endDate: t.endDate ? format(t.endDate, dateFormat) : defaultEndDate,
|
||||||
|
ganttBarConfig: {
|
||||||
|
id: t.id,
|
||||||
|
label: t.title,
|
||||||
|
hasHandles: true,
|
||||||
|
style: {
|
||||||
|
color: colorIsDark(t.getHexColor()) ? 'black' : 'white',
|
||||||
|
backgroundColor: t.getHexColor(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}]))
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadTasks() {
|
||||||
|
tasks.value = new Map()
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
sort_by: ['start_date', 'done', 'id'],
|
||||||
|
order_by: ['asc', 'asc', 'desc'],
|
||||||
|
filter_by: ['done'], // TODO: only load tasks in the current date range
|
||||||
|
filter_comparator: ['equals'],
|
||||||
|
filter_value: ['false'],
|
||||||
|
filter_concat: 'and',
|
||||||
|
}
|
||||||
|
|
||||||
|
const taskCollectionService = new TaskCollectionService()
|
||||||
|
|
||||||
|
const getAllTasks = async (page = 1) => {
|
||||||
|
const tasks = await taskCollectionService.getAll({listId: props.listId}, params, page)
|
||||||
|
if (page < taskCollectionService.totalPages) {
|
||||||
|
const nextTasks = await getAllTasks(page + 1)
|
||||||
|
return tasks.concat(nextTasks)
|
||||||
|
}
|
||||||
|
return tasks
|
||||||
|
}
|
||||||
|
|
||||||
|
const loadedTasks = await getAllTasks()
|
||||||
|
|
||||||
|
loadedTasks
|
||||||
|
.forEach(t => {
|
||||||
|
tasks.value.set(t.id, t)
|
||||||
|
})
|
||||||
|
|
||||||
|
mapGanttBars()
|
||||||
|
}
|
||||||
|
|
||||||
|
loadTasks()
|
||||||
|
|
||||||
|
async function updateTask(e) {
|
||||||
|
const task = tasks.value.get(e.bar.ganttBarConfig.id)
|
||||||
|
task.startDate = e.bar.startDate
|
||||||
|
task.endDate = e.bar.endDate
|
||||||
|
const taskService = new TaskService()
|
||||||
|
await taskService.update(task)
|
||||||
|
// TODO: Loading animation
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.g-gantt-row-label {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -124,6 +124,9 @@ if (window.SENTRY_ENABLED) {
|
||||||
import('./sentry').then(sentry => sentry.default(app, router))
|
import('./sentry').then(sentry => sentry.default(app, router))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
import ganttastic from '@infectoone/vue-ganttastic'
|
||||||
|
app.use(ganttastic)
|
||||||
|
|
||||||
app.use(store)
|
app.use(store)
|
||||||
app.use(router)
|
app.use(router)
|
||||||
app.use(i18n)
|
app.use(i18n)
|
||||||
|
|
|
@ -72,7 +72,7 @@ import {useI18n} from 'vue-i18n'
|
||||||
import {useStore} from 'vuex'
|
import {useStore} from 'vuex'
|
||||||
|
|
||||||
import ListWrapper from './ListWrapper.vue'
|
import ListWrapper from './ListWrapper.vue'
|
||||||
import GanttChart from '@/components/tasks/gantt-component.vue'
|
import GanttChart from '@/components/tasks/gantt-chart.vue'
|
||||||
import Fancycheckbox from '@/components/input/fancycheckbox.vue'
|
import Fancycheckbox from '@/components/input/fancycheckbox.vue'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
|
|
12
yarn.lock
12
yarn.lock
|
@ -1234,6 +1234,13 @@
|
||||||
resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45"
|
resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45"
|
||||||
integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==
|
integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==
|
||||||
|
|
||||||
|
"@infectoone/vue-ganttastic@^2.0.4":
|
||||||
|
version "2.0.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/@infectoone/vue-ganttastic/-/vue-ganttastic-2.0.4.tgz#3f9d8edc7cb01ad4d513cdecc58f63a89545d081"
|
||||||
|
integrity sha512-IKvB6ht/dBwFWzwTKNOLnRBk1OIC6nUXcPX6o7Zt219d28RotukeSRp0EqDEAjTe0UzLA54BX/P5BXgBdqmccw==
|
||||||
|
dependencies:
|
||||||
|
dayjs "^1.10.5"
|
||||||
|
|
||||||
"@intlify/core-base@9.2.2":
|
"@intlify/core-base@9.2.2":
|
||||||
version "9.2.2"
|
version "9.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/@intlify/core-base/-/core-base-9.2.2.tgz#5353369b05cc9fe35cab95fe20afeb8a4481f939"
|
resolved "https://registry.yarnpkg.com/@intlify/core-base/-/core-base-9.2.2.tgz#5353369b05cc9fe35cab95fe20afeb8a4481f939"
|
||||||
|
@ -4619,6 +4626,11 @@ dayjs@^1.10.4:
|
||||||
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.3.tgz#4754eb694a624057b9ad2224b67b15d552589258"
|
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.3.tgz#4754eb694a624057b9ad2224b67b15d552589258"
|
||||||
integrity sha512-xxwlswWOlGhzgQ4TKzASQkUhqERI3egRNqgV4ScR8wlANA/A9tZ7miXa44vTTKEq5l7vWoL5G57bG3zA+Kow0A==
|
integrity sha512-xxwlswWOlGhzgQ4TKzASQkUhqERI3egRNqgV4ScR8wlANA/A9tZ7miXa44vTTKEq5l7vWoL5G57bG3zA+Kow0A==
|
||||||
|
|
||||||
|
dayjs@^1.10.5:
|
||||||
|
version "1.11.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.4.tgz#3b3c10ca378140d8917e06ebc13a4922af4f433e"
|
||||||
|
integrity sha512-Zj/lPM5hOvQ1Bf7uAvewDaUcsJoI6JmNqmHhHl3nyumwe0XHwt8sWdOVAPACJzCebL8gQCi+K49w7iKWnGwX9g==
|
||||||
|
|
||||||
debounce@^1.2.0:
|
debounce@^1.2.0:
|
||||||
version "1.2.1"
|
version "1.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.2.1.tgz#38881d8f4166a5c5848020c11827b834bcb3e0a5"
|
resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.2.1.tgz#38881d8f4166a5c5848020c11827b834bcb3e0a5"
|
||||||
|
|
Loading…
Reference in a new issue