feat(gantt): trying to load gantt-chart
This commit is contained in:

committed by
kolaente

parent
df02dd5291
commit
e968c88cfd
@ -1,31 +1,32 @@
|
||||
<template>
|
||||
<Loading
|
||||
v-if="taskService.loading || taskCollectionService.loading || dayjsLanguageLoading"
|
||||
class="gantt-container"
|
||||
/>
|
||||
<div class="gantt-container" v-else>
|
||||
<GGanttChart
|
||||
dateFormat="YYYY-MM-DDTHH:mm:ssZ[Z]"
|
||||
:chart-start="`${dateFrom} 00:00`"
|
||||
:chart-end="`${dateTo} 23:59`"
|
||||
precision="day"
|
||||
bar-start="startDate"
|
||||
bar-end="endDate"
|
||||
:grid="true"
|
||||
@dragend-bar="updateTask"
|
||||
@dblclick-bar="openTask"
|
||||
:width="ganttChartWidth + 'px'"
|
||||
>
|
||||
<template #timeunit="{label, value}">
|
||||
<div
|
||||
class="timeunit-wrapper"
|
||||
:class="{'today': dayIsToday(label)}">
|
||||
<span>{{ value }}</span>
|
||||
<span class="weekday">
|
||||
{{ weekdayFromTimeLabel(label) }}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
<div>
|
||||
<Loading
|
||||
v-if="taskService.loading || taskCollectionService.loading || dayjsLanguageLoading"
|
||||
class="gantt-container"
|
||||
/>
|
||||
<div class="gantt-container" v-else>
|
||||
<GGanttChart
|
||||
:chart-start="`${props.dateFrom} 00:00`"
|
||||
:chart-end="`${props.dateTo} 23:59`"
|
||||
precision="day"
|
||||
bar-start="startDate"
|
||||
bar-end="endDate"
|
||||
:grid="true"
|
||||
@dragend-bar="updateTask"
|
||||
@dblclick-bar="openTask"
|
||||
:width="ganttChartWidth + 'px'"
|
||||
>
|
||||
<template #timeunit="{label, value}">
|
||||
<div
|
||||
class="timeunit-wrapper"
|
||||
:class="{'today': dayIsToday(label)}"
|
||||
>
|
||||
<span>{{ value }}</span>
|
||||
<span class="weekday">
|
||||
{{ weekdayFromTimeLabel(label) }}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
<GGanttRow
|
||||
v-for="(bar, k) in ganttBars"
|
||||
:key="k"
|
||||
@ -33,15 +34,17 @@
|
||||
:bars="bar"
|
||||
/>
|
||||
</GGanttChart>
|
||||
</div>
|
||||
<TaskForm v-if="canWrite" @create-task="createTask" />
|
||||
</div>
|
||||
<TaskForm v-if="canWrite" @create-task="createTask" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {computed, ref, watch, watchEffect, shallowReactive, type PropType} from 'vue'
|
||||
import {computed, ref, watch, watchEffect, shallowReactive} from 'vue'
|
||||
import {useRouter} from 'vue-router'
|
||||
import {format, parse} from 'date-fns'
|
||||
import dayjs from 'dayjs'
|
||||
import isToday from 'dayjs/plugin/isToday'
|
||||
|
||||
import {useDayjsLanguageSync} from '@/i18n'
|
||||
import TaskCollectionService from '@/services/taskCollection'
|
||||
@ -71,9 +74,12 @@ export type DateRange = {
|
||||
dateTo: string,
|
||||
}
|
||||
|
||||
export interface GanttChartProps extends DateRange {
|
||||
// export interface GanttChartProps extends DateRange {
|
||||
export interface GanttChartProps {
|
||||
listId: IList['id']
|
||||
showTasksWithoutDates: boolean
|
||||
dateFrom: string,
|
||||
dateTo: string,
|
||||
}
|
||||
|
||||
// export const DATE_FORMAT = 'yyyy-LL-dd HH:mm'
|
||||
@ -83,7 +89,9 @@ const props = withDefaults(defineProps<GanttChartProps>(), {
|
||||
})
|
||||
|
||||
// setup dayjs for vue-ganttastic
|
||||
const dayjsLanguageLoading = useDayjsLanguageSync(dayjs)
|
||||
const dayjsLanguageLoading = ref(false)
|
||||
// const dayjsLanguageLoading = useDayjsLanguageSync(dayjs)
|
||||
dayjs.extend(isToday)
|
||||
extendDayjs()
|
||||
|
||||
const baseStore = useBaseStore()
|
||||
@ -92,8 +100,8 @@ const router = useRouter()
|
||||
const taskCollectionService = shallowReactive(new TaskCollectionService())
|
||||
const taskService = shallowReactive(new TaskService())
|
||||
|
||||
const dateFromDate = computed(() => parse(props.dateFrom, 'yyyy-LL-dd', new Date()))
|
||||
const dateToDate = computed(() => parse(props.dateTo, 'yyyy-LL-dd', new Date()))
|
||||
const dateFromDate = computed(() => parse(props.dateFrom, 'yyyy-LL-dd', new Date(new Date().setHours(0,0,0,0))))
|
||||
const dateToDate = computed(() => parse(props.dateTo, 'yyyy-LL-dd', new Date(new Date().setHours(23,59,0,0))))
|
||||
|
||||
const DAY_WIDTH_PIXELS = 30
|
||||
const ganttChartWidth = computed(() => {
|
||||
@ -116,6 +124,13 @@ watch(
|
||||
{deep: true}
|
||||
)
|
||||
|
||||
type DateKebab = `${string}-${string}-${string}`
|
||||
type DateISO = string
|
||||
const DATE_FORMAT_KEBAB = 'yyyy-LL-dd'
|
||||
function isoToKebabDate(isoDate: DateISO) {
|
||||
return format(new Date(isoDate), DATE_FORMAT_KEBAB) as DateKebab
|
||||
}
|
||||
|
||||
const defaultStartDate = new Date().toISOString()
|
||||
const defaultEndDate = new Date((new Date()).setDate((new Date()).getDate() + 7)).toISOString()
|
||||
|
||||
@ -123,8 +138,8 @@ function transformTaskToGanttBar(t: ITask) {
|
||||
const black = 'var(--grey-800)'
|
||||
console.log(t)
|
||||
return [{
|
||||
startDate: t.startDate ? new Date(t.startDate).toISOString() : defaultStartDate,
|
||||
endDate: t.endDate ? new Date(t.endDate).toISOString() : defaultEndDate,
|
||||
startDate: isoToKebabDate(new Date(t.startDate ? t.startDate : defaultStartDate).toISOString()),
|
||||
endDate: isoToKebabDate(new Date(t.endDate ? t.endDate : defaultEndDate).toISOString()),
|
||||
ganttBarConfig: {
|
||||
id: String(t.id),
|
||||
label: t.title,
|
||||
|
@ -29,12 +29,12 @@
|
||||
<pre>{{dateRange}}</pre>
|
||||
<pre>{{new Date(dateRange.dateFrom).toISOString()}}</pre>
|
||||
<pre>{{new Date(dateRange.dateTo).toISOString()}}</pre>
|
||||
<!-- <gantt-chart
|
||||
<gantt-chart
|
||||
:list-id="filters.listId"
|
||||
:date-from="filters.dateFrom"
|
||||
:date-to="filters.dateTo"
|
||||
:date-from="isoToKebabDate(filters.dateFrom)"
|
||||
:date-to="isoToKebabDate(filters.dateTo)"
|
||||
:show-tasks-without-dates="showTasksWithoutDates"
|
||||
/> -->
|
||||
/>
|
||||
</card>
|
||||
</div>
|
||||
</template>
|
||||
@ -56,6 +56,7 @@ import ListWrapper from './ListWrapper.vue'
|
||||
import Fancycheckbox from '@/components/input/fancycheckbox.vue'
|
||||
|
||||
import {createAsyncComponent} from '@/helpers/createAsyncComponent'
|
||||
import GanttChart from '@/components/tasks/gantt-chart.vue'
|
||||
import type { IList } from '@/modelTypes/IList'
|
||||
|
||||
export type DateKebab = `${string}-${string}-${string}`
|
||||
@ -82,7 +83,7 @@ export interface GanttFilter {
|
||||
|
||||
type Options = Flatpickr.Options.Options
|
||||
|
||||
const GanttChart = createAsyncComponent(() => import('@/components/tasks/gantt-chart.vue'))
|
||||
// const GanttChart = createAsyncComponent(() => import('@/components/tasks/gantt-chart.vue'))
|
||||
|
||||
const props = defineProps<GanttParams>()
|
||||
|
||||
@ -123,10 +124,14 @@ function parseBooleanProp(booleanProp: string) {
|
||||
: Boolean(booleanProp)
|
||||
}
|
||||
|
||||
const DATE_FORMAT_KEBAB = 'yyyy-LL-dd'
|
||||
function isoToKebabDate(isoDate: DateISO) {
|
||||
return format(new Date(isoDate), DATE_FORMAT_KEBAB) as DateKebab
|
||||
}
|
||||
|
||||
const DEFAULT_SHOW_TASKS_WITHOUT_DATES = false
|
||||
|
||||
const DEFAULT_DATEFROM_DAY_OFFSET = 0
|
||||
// const DEFAULT_DATEFROM_DAY_OFFSET = -15
|
||||
const DEFAULT_DATEFROM_DAY_OFFSET = -15
|
||||
const DEFAULT_DATETO_DAY_OFFSET = +55
|
||||
|
||||
const now = new Date()
|
||||
@ -140,6 +145,8 @@ function getDefaultDateTo() {
|
||||
}
|
||||
|
||||
function routeToFilter(route: RouteLocationNormalized): GanttFilter {
|
||||
console.log('parseDateProp', parseDateProp(route.query.dateTo as DateKebab))
|
||||
console.log(parseDateProp(route.query.dateTo as DateKebab))
|
||||
return {
|
||||
listId: Number(route.params.listId as string),
|
||||
dateFrom: parseDateProp(route.query.dateFrom as DateKebab) || getDefaultDateFrom(),
|
||||
@ -155,8 +162,8 @@ function filterToRoute(filters: GanttFilter): RouteLocationRaw {
|
||||
filters.dateTo !== getDefaultDateTo()
|
||||
) {
|
||||
query = {
|
||||
dateFrom: format(new Date(filters.dateFrom), 'yyyy-LL-dd'),
|
||||
dateTo: format(new Date(filters.dateTo), 'yyyy-LL-dd'),
|
||||
dateFrom: isoToKebabDate(filters.dateFrom),
|
||||
dateTo: isoToKebabDate(filters.dateTo),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -61,7 +61,6 @@ import {useTitle} from '@/composables/useTitle'
|
||||
|
||||
import {useBaseStore} from '@/stores/base'
|
||||
import {useListStore} from '@/stores/lists'
|
||||
import {useKanbanStore} from '@/stores/kanban'
|
||||
|
||||
const props = defineProps({
|
||||
listId: {
|
||||
@ -77,7 +76,6 @@ const props = defineProps({
|
||||
const route = useRoute()
|
||||
|
||||
const baseStore = useBaseStore()
|
||||
const kanbanStore = useKanbanStore()
|
||||
const listStore = useListStore()
|
||||
const listService = ref(new ListService())
|
||||
const loadedListId = ref(0)
|
||||
@ -90,6 +88,7 @@ const currentList = computed(() => {
|
||||
maxRight: null,
|
||||
} : baseStore.currentList
|
||||
})
|
||||
useTitle(() => currentList.value.id ? getListTitle(currentList.value) : '')
|
||||
|
||||
// watchEffect would be called every time the prop would get a value assigned, even if that value was the same as before.
|
||||
// This resulted in loading and setting the list multiple times, even when navigating away from it.
|
||||
@ -98,62 +97,47 @@ const currentList = computed(() => {
|
||||
// of it, most likely due to the rights not being properly populated.
|
||||
watch(
|
||||
() => props.listId,
|
||||
listId => loadList(listId),
|
||||
// loadList
|
||||
async (listIdToLoad: number) => {
|
||||
const listData = {id: listIdToLoad}
|
||||
saveListToHistory(listData)
|
||||
|
||||
// Don't load the list if we either already loaded it or aren't dealing with a list at all currently and
|
||||
// the currently loaded list has the right set.
|
||||
if (
|
||||
(
|
||||
listIdToLoad === loadedListId.value ||
|
||||
typeof listIdToLoad === 'undefined' ||
|
||||
listIdToLoad === currentList.value.id
|
||||
)
|
||||
&& typeof currentList.value !== 'undefined' && currentList.value.maxRight !== null
|
||||
) {
|
||||
loadedListId.value = props.listId
|
||||
return
|
||||
}
|
||||
|
||||
console.debug(`Loading list, props.viewName = ${props.viewName}, $route.params =`, route.params, `, loadedListId = ${loadedListId.value}, currentList = `, currentList.value)
|
||||
|
||||
// Set the current list to the one we're about to load so that the title is already shown at the top
|
||||
loadedListId.value = 0
|
||||
const listFromStore = listStore.getListById(listData.id)
|
||||
if (listFromStore !== null) {
|
||||
baseStore.setBackground(null)
|
||||
baseStore.setBlurHash(null)
|
||||
baseStore.handleSetCurrentList({list: listFromStore})
|
||||
}
|
||||
|
||||
// We create an extra list object instead of creating it in list.value because that would trigger a ui update which would result in bad ux.
|
||||
const list = new ListModel(listData)
|
||||
try {
|
||||
const loadedList = await listService.value.get(list)
|
||||
await baseStore.handleSetCurrentList({list: loadedList})
|
||||
} finally {
|
||||
loadedListId.value = props.listId
|
||||
}
|
||||
},
|
||||
{immediate: true},
|
||||
)
|
||||
|
||||
useTitle(() => currentList.value.id ? getListTitle(currentList.value) : '')
|
||||
|
||||
async function loadList(listIdToLoad: number) {
|
||||
const listData = {id: listIdToLoad}
|
||||
saveListToHistory(listData)
|
||||
|
||||
// This invalidates the loaded list at the kanban board which lets it reload its content when
|
||||
// switched to it. This ensures updates done to tasks in the gantt or list views are consistently
|
||||
// shown in all views while preventing reloads when closing a task popup.
|
||||
// We don't do this for the table view because that does not change tasks.
|
||||
// FIXME: remove this
|
||||
if (
|
||||
props.viewName === 'list.list' ||
|
||||
props.viewName === 'list.gantt'
|
||||
) {
|
||||
kanbanStore.setListId(0)
|
||||
}
|
||||
|
||||
// Don't load the list if we either already loaded it or aren't dealing with a list at all currently and
|
||||
// the currently loaded list has the right set.
|
||||
if (
|
||||
(
|
||||
listIdToLoad === loadedListId.value ||
|
||||
typeof listIdToLoad === 'undefined' ||
|
||||
listIdToLoad === currentList.value.id
|
||||
)
|
||||
&& typeof currentList.value !== 'undefined' && currentList.value.maxRight !== null
|
||||
) {
|
||||
loadedListId.value = props.listId
|
||||
return
|
||||
}
|
||||
|
||||
console.debug(`Loading list, props.viewName = ${props.viewName}, $route.params =`, route.params, `, loadedListId = ${loadedListId.value}, currentList = `, currentList.value)
|
||||
|
||||
// Set the current list to the one we're about to load so that the title is already shown at the top
|
||||
loadedListId.value = 0
|
||||
const listFromStore = listStore.getListById(listData.id)
|
||||
if (listFromStore !== null) {
|
||||
baseStore.setBackground(null)
|
||||
baseStore.setBlurHash(null)
|
||||
baseStore.handleSetCurrentList({list: listFromStore})
|
||||
}
|
||||
|
||||
// We create an extra list object instead of creating it in list.value because that would trigger a ui update which would result in bad ux.
|
||||
const list = new ListModel(listData)
|
||||
try {
|
||||
const loadedList = await listService.value.get(list)
|
||||
await baseStore.handleSetCurrentList({list: loadedList})
|
||||
} finally {
|
||||
loadedListId.value = props.listId
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
Reference in New Issue
Block a user