From e3ac8a17757c2669f75411273c6d05e098f7fcde Mon Sep 17 00:00:00 2001 From: Ronnie Dutta <61982285+MetRonnie@users.noreply.github.com> Date: Wed, 22 May 2024 18:17:26 +0100 Subject: [PATCH 1/4] Improve router error handling & tidy --- src/router/index.js | 47 +++++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/src/router/index.js b/src/router/index.js index aaa8ecee3..596110f20 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -23,35 +23,29 @@ * https://router.vuejs.org/en/ */ -// Lib imports import { createRouter, createWebHashHistory } from 'vue-router' import NProgress from 'nprogress' -import { store } from '@/store/index' import 'nprogress/css/nprogress.css' -// Routes -import paths from './paths' +import paths from '@/router/paths' +import { store } from '@/store/index' import { Alert } from '@/model/Alert.model' NProgress.configure({ showSpinner: false }) -function route (path) { - const copy = Object.assign({}, path) - const view = copy.view - return Object.assign(copy, { - name: path.name || view, - component: (resolve) => import( - `@/views/${view}.vue` - ).then(resolve) - }) +function getRoute (path) { + return { + ...path, + name: path.name || path.view, + component: () => import(`@/views/${path.view}.vue`) + } } // Create a new router const router = createRouter({ history: createWebHashHistory(), - routes: paths.map(path => route(path)), - // .concat([{ path: '*', redirect: '/dashboard' }]), + routes: paths.map(getRoute), scrollBehavior (to, from, savedPosition) { if (savedPosition) { return savedPosition @@ -66,15 +60,17 @@ const router = createRouter({ router.beforeEach(async (to, from) => { NProgress.start() if (!store.state.user.user) { - try { - const user = await router.app.config.globalProperties.$userService.getUserProfile() - store.commit('user/SET_USER', user) - } catch (err) { - store.dispatch('setAlert', new Alert(err, 'error')) - } + const user = await router.app.config.globalProperties.$userService.getUserProfile() + // TODO: catch error getting user profile and redirect to static error page + store.commit('user/SET_USER', user) } - if (!store.state.user.user.permissions.includes('read') && to.name !== 'noAuth') { - return { name: 'noAuth' } + if (!store.state.user.user.permissions?.includes('read')) { + if (to.name !== 'noAuth') { // Avoid infinite redirect? + return { name: 'noAuth' } + } + } else if (to.name === 'noAuth') { + // If authorized, redirect no-auth page to home page + return { path: '/' } } if (to.name) { @@ -96,4 +92,9 @@ router.afterEach(() => { NProgress.done() }) +router.onError((err, to, from) => { + store.dispatch('setAlert', new Alert(err, 'error')) + NProgress.done() +}) + export default router From b16d196b1f78da2e24656d96efd7c0dffec627c7 Mon Sep 17 00:00:00 2001 From: Ronnie Dutta <61982285+MetRonnie@users.noreply.github.com> Date: Wed, 22 May 2024 18:17:26 +0100 Subject: [PATCH 2/4] Set page title per-route rather than per-view Multiple views may be included in a page, so the previous approach was flawed --- package.json | 1 - renovate.json | 5 -- src/components/cylc/commandMenu/Menu.vue | 2 +- src/components/cylc/workflow/Toolbar.vue | 2 +- src/layouts/Default.vue | 9 ++- src/main.js | 8 +-- src/router/index.js | 48 ++++++++++------ src/router/paths.js | 37 ++++++++----- src/utils/index.js | 14 +---- src/views/Analysis.vue | 7 --- src/views/Dashboard.vue | 7 --- src/views/Gantt.vue | 7 --- src/views/Graph.vue | 7 --- src/views/GraphiQL.vue | 5 -- src/views/Guide.vue | 6 -- src/views/Log.vue | 8 --- src/views/NotFound.vue | 12 ---- src/views/SimpleTree.vue | 8 --- src/views/Table.vue | 7 --- src/views/Tree.vue | 7 --- src/views/UserProfile.vue | 7 --- src/views/Workflows.vue | 7 --- src/views/WorkflowsTable.vue | 7 --- src/views/Workspace.vue | 7 --- tests/unit/router/index.spec.js | 48 ++++++++++++++++ tests/unit/utils/index.spec.js | 10 +--- yarn.lock | 70 ------------------------ 27 files changed, 112 insertions(+), 251 deletions(-) create mode 100644 tests/unit/router/index.spec.js diff --git a/package.json b/package.json index 4b13072ce..b9d56528b 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,6 @@ "@lumino/default-theme": "2.1.5", "@lumino/widgets": "2.3.2", "@mdi/js": "7.4.47", - "@unhead/vue": "1.9.9", "@vueuse/core": "10.9.0", "apexcharts": "3.41.0", "axios": "1.6.8", diff --git a/renovate.json b/renovate.json index 6e463f058..e81604e4e 100644 --- a/renovate.json +++ b/renovate.json @@ -55,11 +55,6 @@ "matchPackageNames": ["sass"], "schedule": ["on the 25th day of the month"] }, - { - "matchPackageNames": ["@unhead/vue"], - "automerge": true, - "schedule": ["on the 3rd day of the month"] - }, { "matchPackageNames": ["@vueuse/core"], "automerge": true, diff --git a/src/components/cylc/commandMenu/Menu.vue b/src/components/cylc/commandMenu/Menu.vue index 83b290238..3030325da 100644 --- a/src/components/cylc/commandMenu/Menu.vue +++ b/src/components/cylc/commandMenu/Menu.vue @@ -253,7 +253,7 @@ export default { // Navigate to the corresponding workflow then open the log view // (no nav occurs if already on the correct workflow page) this.$router.push({ - name: 'workspace', + name: 'Workspace', params: { workflowName: this.node.tokens.workflow } diff --git a/src/components/cylc/workflow/Toolbar.vue b/src/components/cylc/workflow/Toolbar.vue index e5d37692e..ae44c9a4e 100644 --- a/src/components/cylc/workflow/Toolbar.vue +++ b/src/components/cylc/workflow/Toolbar.vue @@ -131,7 +131,7 @@ along with this program. If not, see . { store.commit('user/SET_USER', user) } if (!store.state.user.user.permissions?.includes('read')) { - if (to.name !== 'noAuth') { // Avoid infinite redirect? - return { name: 'noAuth' } + if (to.name !== 'NoAuth') { // Avoid infinite redirect? + return { name: 'NoAuth' } } - } else if (to.name === 'noAuth') { + } else if (to.name === 'NoAuth') { // If authorized, redirect no-auth page to home page return { path: '/' } } - if (to.name) { - let title = to.name - let workflowName = null - if (to.meta.toolbar) { - // When a workflow is being displayed, we set the title to a - // different value. - title = to.params.workflowName - workflowName = to.params.workflowName - } - store.commit('app/setTitle', title) - store.commit('workflows/SET_WORKFLOW_NAME', workflowName) - store.dispatch('setAlert', null) + // Set page title: + document.title = getPageTitle(to) + + // Set toolbar title: + let title = to.name + let workflowName = null + if (to.meta.toolbar) { + // When a workflow is being displayed, we set the title to a + // different value. + title = to.params.workflowName + workflowName = to.params.workflowName } + store.commit('app/setTitle', title) + store.commit('workflows/SET_WORKFLOW_NAME', workflowName) + store.dispatch('setAlert', null) }) router.afterEach(() => { diff --git a/src/router/paths.js b/src/router/paths.js index 9339a8816..8b70e8a83 100644 --- a/src/router/paths.js +++ b/src/router/paths.js @@ -1,4 +1,4 @@ -/** +/* * Copyright (C) NIWA & British Crown (Met Office) & Contributors. * * This program is free software: you can redistribute it and/or modify @@ -17,33 +17,38 @@ import { i18n } from '@/i18n' +const workflowTitle = ({ workflowName: name }) => i18n.global.t('App.workflow', { name }) + /** * Define all of your application routes here * for more information on routes, see the * official documentation https://router.vuejs.org/en/ + * + * @type {import('vue-router').RouteRecordRaw[]} - except the `name` and + * `component` fields which are automatically added in @/src/router/index.js */ export default [ { path: '/', view: 'Dashboard', - name: i18n.global.t('App.dashboard'), meta: { + title: i18n.global.t('App.dashboard'), layout: 'default' } }, { path: '/workflow-table', - name: 'Workflow Table', view: 'WorkflowsTable', meta: { + title: 'Workflow Table', layout: 'default' } }, { path: '/workspace/:workflowName(.*)', view: 'Workspace', - name: 'workspace', meta: { + getTitle: workflowTitle, layout: 'default', toolbar: true }, @@ -51,17 +56,17 @@ export default [ }, { path: '/user-profile', - name: i18n.global.t('App.userProfile'), view: 'UserProfile', meta: { + title: i18n.global.t('App.userProfile'), layout: 'default' } }, { path: '/guide', - name: 'Guide', view: 'Guide', meta: { + title: i18n.global.t('App.guide'), layout: 'default' } }, @@ -69,6 +74,7 @@ export default [ path: '/graphiql', view: 'GraphiQL', meta: { + title: 'GraphiQL', layout: 'empty' } }, @@ -76,6 +82,7 @@ export default [ path: '/:catchAll(.*)', view: 'NotFound', meta: { + title: i18n.global.t('App.notFound'), layout: 'empty' } }, @@ -83,9 +90,9 @@ export default [ // the standalone views { path: '/workflows', - name: i18n.global.t('App.workflows'), view: 'Workflows', meta: { + title: i18n.global.t('App.workflows'), layout: 'default', toolbar: false, showSidebar: false @@ -94,8 +101,8 @@ export default [ { path: '/tree/:workflowName(.*)', view: 'Tree', - name: 'tree', meta: { + getTitle: workflowTitle, layout: 'default', toolbar: true, showSidebar: false @@ -105,8 +112,8 @@ export default [ { path: '/table/:workflowName(.*)', view: 'Table', - name: 'table', meta: { + getTitle: workflowTitle, layout: 'default', toolbar: true, showSidebar: false @@ -116,8 +123,8 @@ export default [ { path: '/graph/:workflowName(.*)', view: 'Graph', - name: 'graph', meta: { + getTitle: workflowTitle, layout: 'default', toolbar: true, showSidebar: false @@ -127,8 +134,8 @@ export default [ { path: '/log/:workflowName(.*)', view: 'Log', - name: 'log', meta: { + getTitle: workflowTitle, layout: 'default', toolbar: true, showSidebar: false @@ -138,8 +145,8 @@ export default [ { path: '/analysis/:workflowName(.*)', view: 'Analysis', - name: 'analysis', meta: { + getTitle: workflowTitle, layout: 'default', toolbar: true, showSidebar: false @@ -149,8 +156,8 @@ export default [ { path: '/gantt/:workflowName(.*)', view: 'Gantt', - name: 'gantt', meta: { + getTitle: workflowTitle, layout: 'default', toolbar: true, showSidebar: false @@ -160,9 +167,9 @@ export default [ { path: '/noAuth', view: 'NoAuth', - name: 'noAuth', meta: { + title: 'Unauthorized', layout: 'noAuth', }, - } + }, ] diff --git a/src/utils/index.js b/src/utils/index.js index dcbb6a1b1..0d794751b 100644 --- a/src/utils/index.js +++ b/src/utils/index.js @@ -1,4 +1,4 @@ -/** +/* * Copyright (C) NIWA & British Crown (Met Office) & Contributors. * * This program is free software: you can redistribute it and/or modify @@ -16,18 +16,6 @@ */ import { watch } from 'vue' -import { i18n } from '@/i18n' - -/** - * i18n-enabled operation, to get the title respecting the locale used - * in the application settings. - * @param {string} key - i18n key - * @param {Object} params - optional object key=value used in the i18n message - * @returns {string} - */ -export const getPageTitle = (key, params = {}) => { - return `${i18n.global.t('App.name')} | ${i18n.global.t(key, params)}` -} /** * Watch source until it is truthy, then call the callback (and stop watching). diff --git a/src/views/Analysis.vue b/src/views/Analysis.vue index 243a87219..d83dc805c 100644 --- a/src/views/Analysis.vue +++ b/src/views/Analysis.vue @@ -144,7 +144,6 @@ import { pick, } from 'lodash' import gql from 'graphql-tag' -import { getPageTitle } from '@/utils/index' import graphqlMixin from '@/mixins/graphql' import { initialOptions, @@ -248,12 +247,6 @@ export default { TimeSeries }, - head () { - return { - title: getPageTitle('App.workflow', { name: this.workflowName }) - } - }, - beforeMount () { this.tasksQuery() }, diff --git a/src/views/Dashboard.vue b/src/views/Dashboard.vue index 8c76abde7..62a2ee101 100644 --- a/src/views/Dashboard.vue +++ b/src/views/Dashboard.vue @@ -141,7 +141,6 @@ along with this program. If not, see . diff --git a/src/views/SimpleTree.vue b/src/views/SimpleTree.vue index d0ba70638..ca2565a58 100644 --- a/src/views/SimpleTree.vue +++ b/src/views/SimpleTree.vue @@ -106,7 +106,6 @@ along with this program. If not, see .