From e12c7b97a076f9a5cd197c6badc29842478dd124 Mon Sep 17 00:00:00 2001 From: Paul Spooren Date: Tue, 21 Jul 2026 10:41:32 +0200 Subject: [PATCH] feat: add support for view mode in URL This allows to link directly to a specific view by adding /gantt or /kanab to a board. It does not store the information, so the default user setting isn't touched. This was generated using AI. Signed-off-by: Paul Spooren --- appinfo/routes.php | 2 ++ lib/Controller/PageController.php | 6 ++++++ src/components/board/Board.vue | 14 ++++++++++++++ src/router.js | 19 +++++++++++++++++++ src/store/main.js | 14 ++++++++++++++ 5 files changed, 55 insertions(+) diff --git a/appinfo/routes.php b/appinfo/routes.php index 6e2c273087..b75e268f07 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -13,6 +13,8 @@ ['name' => 'page#indexBoard', 'url' => '/board/{boardId}', 'verb' => 'GET'], ['name' => 'page#indexBoardDetails', 'url' => '/board/{boardId}/details', 'verb' => 'GET'], ['name' => 'page#indexCard', 'url' => '/board/{boardId}/card/{cardId}', 'verb' => 'GET'], + // Declared after the more specific board routes so those keep priority. + ['name' => 'page#indexBoardView', 'url' => '/board/{boardId}/{viewMode}', 'verb' => 'GET', 'requirements' => ['viewMode' => 'kanban|gantt']], ['name' => 'page#redirectToCard', 'url' => '/card/{cardId}', 'verb' => 'GET'], diff --git a/lib/Controller/PageController.php b/lib/Controller/PageController.php index 1cad275201..3aae08727c 100644 --- a/lib/Controller/PageController.php +++ b/lib/Controller/PageController.php @@ -101,6 +101,12 @@ public function indexBoardDetails(int $boardId): TemplateResponse { return $this->index(); } + #[NoAdminRequired] + #[NoCSRFRequired] + public function indexBoardView(int $boardId, string $viewMode): TemplateResponse { + return $this->index(); + } + #[NoAdminRequired] #[NoCSRFRequired] public function indexCard(int $cardId): TemplateResponse { diff --git a/src/components/board/Board.vue b/src/components/board/Board.vue index 3268e9bc3c..29f87acc2d 100644 --- a/src/components/board/Board.vue +++ b/src/components/board/Board.vue @@ -124,6 +124,15 @@ export default { type: Number, default: null, }, + /** + * View to open the board in, coming from the /board/:id/:viewMode + * route. Applied for this session only, so linking to a view does not + * change the user's stored preference for the board. + */ + initialViewMode: { + type: String, + default: null, + }, }, data() { return { @@ -186,6 +195,11 @@ export default { await this.$store.dispatch('loadBoardById', this.id) await this.$store.dispatch('loadStacks', this.id) + // Only after loadBoardById, as the mutation needs currentBoard + if (this.initialViewMode) { + this.$store.commit('setViewModeForSession', this.initialViewMode) + } + const routeCardId = this.$route?.params?.cardId ? parseInt(this.$route.params.cardId) : null // If an archived card is requested, and we cannot find it in the current we load the archived stacks instead if (routeCardId && !this.$store.getters.cardById(routeCardId)) { diff --git a/src/router.js b/src/router.js index 6b7387ace7..398937b46f 100644 --- a/src/router.js +++ b/src/router.js @@ -75,6 +75,25 @@ const router = new Router({ navFilter: BOARD_FILTERS.SHARED, }, }, + { + // Opens a board directly in a given view, so a board can be linked + // to as e.g. a Gantt chart. The view is applied for this session + // only and does not overwrite the user's stored preference. + path: '/board/:id/:viewMode(kanban|gantt)', + name: 'board.view', + components: { + default: Board, + sidebar: Sidebar, + }, + props: { + default: (route) => { + return { + id: parseInt(route.params.id, 10), + initialViewMode: route.params.viewMode, + } + }, + }, + }, { path: '/board/:id', name: 'board', diff --git a/src/store/main.js b/src/store/main.js index 8a735e1604..327fb2c392 100644 --- a/src/store/main.js +++ b/src/store/main.js @@ -309,6 +309,20 @@ export default function storeFactory() { Vue.set(state.viewModeByBoard, state.currentBoard.id, mode) localStorage.setItem(`deck.viewMode.${state.currentBoard.id}`, mode) }, + /** + * Set the view mode without storing it as the user's preference. + * + * Used by the /board/:id/:viewMode routes, so that opening a link + * to a specific view does not change how the recipient sees that + * board afterwards. + * + * @param {object} state the store state + * @param {string} mode the view mode to apply + */ + setViewModeForSession(state, mode) { + if (!state.currentBoard) return + Vue.set(state.viewModeByBoard, state.currentBoard.id, mode) + }, }, actions: { setFullApp({ commit }, isFullApp) {