Compare commits

...

6 commits

Author SHA1 Message Date
kolaente
1817a1f958
Add background stripes and such 2021-05-20 17:44:25 +02:00
kolaente
affaa95e3f
Add pen icon to link to task detail 2021-05-20 17:15:53 +02:00
kolaente
7e6d5956ba
Make it look more like the old gantt chart 2021-05-20 14:14:10 +02:00
kolaente
a1ee90df33
Add dragged handler 2021-05-19 23:01:09 +02:00
kolaente
1604f5b4aa
Color 2021-05-19 19:44:55 +02:00
kolaente
6a0427b216
Start adding vue-ganttastic 2021-05-19 19:43:25 +02:00
4 changed files with 167 additions and 4 deletions

View file

@ -22,6 +22,7 @@
"highlight.js": "10.7.2", "highlight.js": "10.7.2",
"lodash": "4.17.21", "lodash": "4.17.21",
"marked": "2.0.3", "marked": "2.0.3",
"moment": "^2.29.1",
"register-service-worker": "1.7.2", "register-service-worker": "1.7.2",
"sass": "1.32.13", "sass": "1.32.13",
"snake-case": "3.0.4", "snake-case": "3.0.4",
@ -30,6 +31,7 @@
"vue-advanced-cropper": "1.5.2", "vue-advanced-cropper": "1.5.2",
"vue-drag-resize": "1.5.4", "vue-drag-resize": "1.5.4",
"vue-easymde": "1.4.0", "vue-easymde": "1.4.0",
"vue-ganttastic": "^0.9.32",
"vue-shortkey": "3.1.7", "vue-shortkey": "3.1.7",
"vue-smooth-dnd": "0.8.1", "vue-smooth-dnd": "0.8.1",
"vuex": "3.6.2" "vuex": "3.6.2"

View file

@ -16,6 +16,40 @@
v-model="params" v-model="params"
/> />
</div> </div>
<g-gantt-chart
:chart-start="dateFrom.toString()"
:chart-end="dateTo.toString()"
:push-on-overlap="true"
row-label-width="0"
:row-height="37"
:grid="false"
@dragend-bar="dragged($event)"
:style="{'--day-width': dayWidthPercent + 'px'}"
ref="g-gantt-chart-container"
>
<g-gantt-row
v-for="(t, k) in ganttBars"
:key="t ? t.id : 'k'+k"
label=""
bar-start="start"
bar-end="end"
:bars="t.bars"
:highlight-on-hover="true"
>
<template v-slot:bar-label>
<span>
{{ t.title }}
</span>
<router-link :to="{name: 'task.detail', params: {id: t.id}}" class="edit-task-link">
<icon icon="pen"/>
</router-link>
</template>
</g-gantt-row>
</g-gantt-chart>
<div class="dates"> <div class="dates">
<template v-for="(y, yk) in days"> <template v-for="(y, yk) in days">
<div :key="yk + 'year'" class="months"> <div :key="yk + 'year'" class="months">
@ -191,16 +225,30 @@
<script> <script>
import VueDragResize from 'vue-drag-resize' import VueDragResize from 'vue-drag-resize'
import {GGanttChart, GGanttRow} from 'vue-ganttastic'
import EditTask from './edit-task' import EditTask from './edit-task'
import {mapState} from 'vuex'
import TaskService from '../../services/task' import TaskService from '../../services/task'
import TaskModel from '../../models/task' import TaskModel from '../../models/task'
import priorities from '../../models/priorities' import priorities from '../../models/priorities'
import PriorityLabel from './partials/priorityLabel' import PriorityLabel from './partials/priorityLabel'
import TaskCollectionService from '../../services/taskCollection' import TaskCollectionService from '../../services/taskCollection'
import {mapState} from 'vuex'
import Rights from '../../models/rights.json' import Rights from '../../models/rights.json'
import FilterPopup from '@/components/list/partials/filter-popup' import FilterPopup from '@/components/list/partials/filter-popup'
import {colorIsDark} from '@/helpers/color/colorIsDark'
// Source: https://stackoverflow.com/a/11252167/10924593
const treatAsUTC = date => {
const result = new Date(date)
result.setMinutes(result.getMinutes() - result.getTimezoneOffset())
return result
}
const daysBetween = (startDate, endDate) => {
const millisecondsPerDay = 24 * 60 * 60 * 1000;
return (treatAsUTC(endDate) - treatAsUTC(startDate)) / millisecondsPerDay;
}
export default { export default {
name: 'GanttChart', name: 'GanttChart',
@ -209,6 +257,8 @@ export default {
PriorityLabel, PriorityLabel,
EditTask, EditTask,
VueDragResize, VueDragResize,
GGanttChart,
GGanttRow,
}, },
props: { props: {
listId: { listId: {
@ -251,6 +301,8 @@ export default {
taskCollectionService: TaskCollectionService, taskCollectionService: TaskCollectionService,
showTaskFilter: false, showTaskFilter: false,
ganttBars: [],
params: { params: {
sort_by: ['done', 'id'], sort_by: ['done', 'id'],
order_by: ['asc', 'desc'], order_by: ['asc', 'desc'],
@ -275,9 +327,21 @@ export default {
mounted() { mounted() {
this.buildTheGanttChart() this.buildTheGanttChart()
}, },
computed: mapState({ computed: {
...mapState({
canWrite: (state) => state.currentList.maxRight > Rights.READ, canWrite: (state) => state.currentList.maxRight > Rights.READ,
}), }),
dayWidthPercent() {
const days = daysBetween(this.startDate, this.endDate)
let containerWidth = 1
if(this.$refs['g-gantt-chart-container']) {
containerWidth = this.$refs['g-gantt-chart-container'].$el.clientWidth
}
return Math.round(containerWidth / days)
},
},
methods: { methods: {
buildTheGanttChart() { buildTheGanttChart() {
this.setDates() this.setDates()
@ -360,6 +424,25 @@ export default {
if (a.startDate > b.startDate) return 1 if (a.startDate > b.startDate) return 1
return 0 return 0
}) })
this.ganttBars = this.theTasks.map(t => {
return {
id: t.id,
title: t.title,
bars: [{
id: t.id,
start: t.startDate.toString(),
end: t.endDate.toString(),
label: t.title,
ganttBarConfig: {
color: colorIsDark(t.getHexColor()) ? 'black' : 'white',
backgroundColor: t.getHexColor(),
handles: true,
},
}],
}
})
}) })
.catch((e) => { .catch((e) => {
this.error(e, this) this.error(e, this)
@ -487,6 +570,14 @@ export default {
this.error(e, this) this.error(e, this)
}) })
}, },
dragged(e) {
const bar = e.movedBars.entries().next().value[0]
const index = this.theTasks.findIndex(t => t.id === bar.id)
const task = this.theTasks[index]
task.startDate = new Date(bar.start)
task.endDate = new Date(bar.end)
this.$set(this.theTasks, index, task)
},
}, },
} }
</script> </script>

View file

@ -235,3 +235,63 @@ $gantt-vertical-border-color: $grey-100;
.vdr.active::before { .vdr.active::before {
display: none; display: none;
} }
:root {
--day-width: 50px;
}
#g-gantt-chart {
* {
font-family: $family-sans-serif !important;
}
.g-gantt-row {
&:nth-child(even) {
background: rgba(0, 0, 0, 0) repeating-linear-gradient(90deg, #ededed, #ededed 1px, #fff 1px, #fff var(--day-width)) repeat scroll 0% 0%;
}
&:nth-child(odd) {
background: rgba(0, 0, 0, 0) repeating-linear-gradient(90deg, #ededed, #ededed 1px, #fafafa 1px, #fafafa var(--day-width)) repeat scroll 0% 0%;
}
> .g-gantt-row-bars-container {
border: 0;
}
}
.g-gantt-bar {
border-radius: 6px !important;
overflow: visible !important;
cursor: grab;
&:active {
cursor: grabbing;
}
.g-gantt-bar-label {
font-size: .85rem;
justify-content: space-between;
padding: 0 .5rem !important;
overflow: hidden;
.edit-task-link {
color: inherit;
padding: 0 .25rem;
}
}
.g-gantt-bar-handle-left, .g-gantt-bar-handle-right {
width: 8px !important;
height: 8px !important;
opacity: 1 !important;
border-radius: 0 !important;
margin-left: -2px !important;
border: 1px solid $grey-700 !important;
}
.g-gantt-bar-handle-right {
margin-right: -2px !important;
}
}
}

View file

@ -9543,6 +9543,11 @@ mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.4, mkdirp@~0.5.0:
dependencies: dependencies:
minimist "^1.2.5" minimist "^1.2.5"
moment@^2.29.1:
version "2.29.1"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3"
integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==
move-concurrently@^1.0.1: move-concurrently@^1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92" resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92"
@ -13340,6 +13345,11 @@ vue-flatpickr-component@8.1.6:
dependencies: dependencies:
flatpickr "^4.6.6" flatpickr "^4.6.6"
vue-ganttastic@^0.9.32:
version "0.9.32"
resolved "https://registry.yarnpkg.com/vue-ganttastic/-/vue-ganttastic-0.9.32.tgz#7dc37db63fddd39413444a6674c85d3300f552af"
integrity sha512-xVWOFXwBFJ/1piQyQ842RZtpEIRZWOjfhSZeQUTX5oGWskRe1P7yOdZ6SI30x+S/kNGVICcyfawbnMHNix/TYg==
vue-hot-reload-api@^2.3.0: vue-hot-reload-api@^2.3.0:
version "2.3.4" version "2.3.4"
resolved "https://registry.yarnpkg.com/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz#532955cc1eb208a3d990b3a9f9a70574657e08f2" resolved "https://registry.yarnpkg.com/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz#532955cc1eb208a3d990b3a9f9a70574657e08f2"