Skip to content

feat: add Gantt chart view for boards#7812

Open
aparcar wants to merge 1 commit intonextcloud:mainfrom
aparcar:gantt
Open

feat: add Gantt chart view for boards#7812
aparcar wants to merge 1 commit intonextcloud:mainfrom
aparcar:gantt

Conversation

@aparcar
Copy link
Copy Markdown
Contributor

@aparcar aparcar commented Mar 31, 2026

Summary

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
image

This is inspired by https://github.com/nextcloud-community/ncgantt

TODO

  • Sometimes entries "jump" around while dragging, however this might be an issue with my browser

Checklist

  • Code is properly formatted
  • Sign-off message is added to all commits
  • Tests (unit, integration, api and/or acceptance) are included
  • Documentation (manuals or wiki) has been updated or is not required

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 <noreply@anthropic.com>
Signed-off-by: Paul Spooren <mail@aparcar.org>
Copy link
Copy Markdown
Member

@grnd-alt grnd-alt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

works thanks a lot for this contribution. But there is some room for improvement. also darkmode looks like this:

Image

...mapState({
filter: state => state.filter,
}),
ganttViewModes() {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why have this as a computed does not seem to depend on anything?

deep: true,
handler() {
if (!this.isDragging) {
this.$nextTick(() => this.renderGantt())
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can also update the chart with refresh() no need to render every time

// Inject dynamic stack colors as CSS
this.injectStackStyles()

this.ganttInstance = new Gantt(this.$refs.ganttContainer, this.ganttTasks, {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

parsing this.ganttTasks causes infinite rerenders and tab crash as the gantt library can update the param itself, we should pass a copy and also not rerender if this.ganttTasks changes

this.ganttInstance = null
},
methods: {
formatDate(date) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not needed library can handle date object

this.ganttInstance.change_view_mode(mode)
this.$nextTick(() => this.fitColumnsToWidth())
} else {
this.$nextTick(() => this.renderGantt())
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why try rendering again? if ganttInstance is null that means there are no tasks to render why would that change if the user changes viewmode?

end = new Date(start)
end.setDate(end.getDate() + 1)
}
if (!start && !end) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unreachable case because of the filter L189?

const styleId = 'gantt-stack-styles'
let styleEl = document.getElementById(styleId)
if (!styleEl) {
styleEl = document.createElement('style')
Copy link
Copy Markdown
Member

@grnd-alt grnd-alt Apr 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not a fan of creating a style element at document root here, we can also achieve generated classes by using scss and set the custom-class param to classname-${stackId % x)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add diagramme gantt

2 participants