Skip to content

Commit 1bff571

Browse files
aparcarclaude
andcommitted
feat: add Gantt chart view for boards
Add a timeline/Gantt view as an alternative to the kanban board view, allowing users to visualize card schedules across time using frappe-gantt. - Add GanttView component with Day/Week/Month view modes - Add Kanban/Gantt view toggle in board controls - Store view mode preference in localStorage via Vuex - Stack-based color coding with legend and undated cards section - Drag-and-drop support for rescheduling cards - Auto-fit column width to fill container on wider views - Add frappe-gantt dependency and webpack resolve alias for its CSS Co-Authored-By: Claude Opus 4.6 <[email protected]> Signed-off-by: Paul Spooren <[email protected]>
1 parent ab2f4d0 commit 1bff571

File tree

7 files changed

+560
-1
lines changed

7 files changed

+560
-1
lines changed

package-lock.json

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
"blueimp-md5": "^2.19.0",
4949
"chroma-js": "^3.2.0",
5050
"dompurify": "^3.3.2",
51+
"frappe-gantt": "^1.2.2",
5152
"lodash": "^4.17.23",
5253
"markdown-it": "^14.1.1",
5354
"markdown-it-link-attributes": "^4.0.1",

src/components/Controls.vue

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,21 @@
226226

227227
<NcActions :aria-label="t('deck', 'View Modes')"
228228
:name="t('deck', 'Toggle View Modes')">
229+
<NcActionButton :class="{ 'action--active': viewMode === 'kanban' }"
230+
@click="setViewMode('kanban')">
231+
<template #icon>
232+
<ViewColumnIcon :size="20" decorative />
233+
</template>
234+
{{ t('deck', 'Kanban view') }}
235+
</NcActionButton>
236+
<NcActionButton :class="{ 'action--active': viewMode === 'gantt' }"
237+
@click="setViewMode('gantt')">
238+
<template #icon>
239+
<ChartGanttIcon :size="20" decorative />
240+
</template>
241+
{{ t('deck', 'Gantt view') }}
242+
</NcActionButton>
243+
<NcActionSeparator />
229244
<NcActionButton @click="toggleShowArchived">
230245
<template #icon>
231246
<ArchiveIcon :size="20" decorative />
@@ -264,7 +279,7 @@
264279
<script>
265280
import { mapState, mapGetters } from 'vuex'
266281
import { subscribe, unsubscribe } from '@nextcloud/event-bus'
267-
import { NcActions, NcActionButton, NcAvatar, NcButton, NcPopover, NcModal } from '@nextcloud/vue'
282+
import { NcActions, NcActionButton, NcActionSeparator, NcAvatar, NcButton, NcPopover, NcModal } from '@nextcloud/vue'
268283
import labelStyle from '../mixins/labelStyle.js'
269284
import ArchiveIcon from 'vue-material-design-icons/ArchiveOutline.vue'
270285
import ImageIcon from 'vue-material-design-icons/ImageMultipleOutline.vue'
@@ -273,6 +288,8 @@ import FilterOffIcon from 'vue-material-design-icons/FilterOffOutline.vue'
273288
import TableColumnPlusAfter from 'vue-material-design-icons/TableColumnPlusAfter.vue'
274289
import ArrowCollapseVerticalIcon from 'vue-material-design-icons/ArrowCollapseVertical.vue'
275290
import ArrowExpandVerticalIcon from 'vue-material-design-icons/ArrowExpandVertical.vue'
291+
import ViewColumnIcon from 'vue-material-design-icons/ViewColumn.vue'
292+
import ChartGanttIcon from 'vue-material-design-icons/ChartGantt.vue'
276293
import SessionList from './SessionList.vue'
277294
import { isNotifyPushEnabled } from '../sessions.js'
278295
import CreateNewCardCustomPicker from '../views/CreateNewCardCustomPicker.vue'
@@ -294,6 +311,9 @@ export default {
294311
FilterOffIcon,
295312
ArrowCollapseVerticalIcon,
296313
ArrowExpandVerticalIcon,
314+
ViewColumnIcon,
315+
ChartGanttIcon,
316+
NcActionSeparator,
297317
TableColumnPlusAfter,
298318
SessionList,
299319
},
@@ -334,6 +354,7 @@ export default {
334354
showCardCover: state => state.showCardCover,
335355
searchQuery: state => state.searchQuery,
336356
showArchived: state => state.showArchived,
357+
viewMode: state => state.viewMode,
337358
}),
338359
detailsRoute() {
339360
return {
@@ -406,6 +427,9 @@ export default {
406427
toggleShowCardCover() {
407428
this.$store.dispatch('toggleShowCardCover')
408429
},
430+
setViewMode(mode) {
431+
this.$store.dispatch('setViewMode', mode)
432+
},
409433
toggleShowArchived() {
410434
this.$store.dispatch('toggleShowArchived')
411435
},

src/components/board/Board.vue

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@
4646
</form>
4747
</template>
4848
</NcEmptyContent>
49+
<GanttView v-else-if="!isEmpty && !loading && viewMode === 'gantt'"
50+
key="gantt"
51+
:board="board"
52+
:stacks="stacksByBoard" />
4953
<div v-else-if="!isEmpty && !loading"
5054
key="board"
5155
ref="board"
@@ -88,6 +92,7 @@ import Controls from '../Controls.vue'
8892
import DeckIcon from '../icons/DeckIcon.vue'
8993
import CheckIcon from 'vue-material-design-icons/Check.vue'
9094
import Stack from './Stack.vue'
95+
import GanttView from './GanttView.vue'
9196
import { NcEmptyContent, NcModal, NcButton, NcTextField, NcLoadingIcon } from '@nextcloud/vue'
9297
import GlobalSearchResults from '../search/GlobalSearchResults.vue'
9398
import { showError } from '../../helpers/errors.js'
@@ -102,6 +107,7 @@ export default {
102107
DeckIcon,
103108
Draggable,
104109
Stack,
110+
GanttView,
105111
NcEmptyContent,
106112
NcModal,
107113
NcTextField,
@@ -134,6 +140,7 @@ export default {
134140
isFullApp: state => state.isFullApp,
135141
board: state => state.currentBoard,
136142
showArchived: state => state.showArchived,
143+
viewMode: state => state.viewMode,
137144
}),
138145
...mapGetters([
139146
'canEdit',

0 commit comments

Comments
 (0)