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 .