diff --git a/client/src/components/LogoutWidget.vue b/client/src/components/LogoutWidget.vue index 199ee1c4..0769bd75 100644 --- a/client/src/components/LogoutWidget.vue +++ b/client/src/components/LogoutWidget.vue @@ -11,6 +11,7 @@ diff --git a/client/src/pages/survey.vue b/client/src/pages/survey.vue index 0d819bab..7d0a76f6 100644 --- a/client/src/pages/survey.vue +++ b/client/src/pages/survey.vue @@ -36,6 +36,7 @@ import { isTeacher } from '@/helpers/is-teacher'; import { meQuery } from '@/graphql/queries'; import { defineAsyncComponent } from 'vue'; +import { matomoTrackEvent } from '@/helpers/matomo-client'; const Solution = defineAsyncComponent(() => import( /* webpackChunkName: "content-components" @@ -120,6 +121,7 @@ export default { mounted() { if (this.surveyData) { this.loadSurveyFromServer(this.surveyData); + matomoTrackEvent('Übung', 'Übung angezeigt', this.id); } }, @@ -205,7 +207,10 @@ export default { }) .then(() => { if (exit) { + matomoTrackEvent('Übung', 'Übung erfolgreich abgeschlossen', this.id) this.$router.go(-1); + } else { + matomoTrackEvent('Übung', 'Übungsschritt abgeschlossen', this.id) } }); }; diff --git a/client/src/pages/topic-page.vue b/client/src/pages/topic-page.vue index ef3fb272..50a2d769 100644 --- a/client/src/pages/topic-page.vue +++ b/client/src/pages/topic-page.vue @@ -53,6 +53,7 @@ import TopicNavigation from '@/components/book-navigation/TopicNavigation'; import UPDATE_LAST_TOPIC_MUTATION from '@/graphql/gql/mutations/updateLastTopic.gql'; import ME_QUERY from '@/graphql/gql/queries/meQuery.gql'; + const PlayIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */ '@/components/icons/Play')); const BulbIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */ '@/components/icons/BulbIcon')); diff --git a/client/src/router/auth.routes.js b/client/src/router/auth.routes.js index 54a38d75..3d3d35a5 100644 --- a/client/src/router/auth.routes.js +++ b/client/src/router/auth.routes.js @@ -15,6 +15,7 @@ export default [ public: true, illustration: 'hello', illustrationAlign: 'top', + matomoUrl: '/auth/hello', }, }, { @@ -24,6 +25,7 @@ export default [ meta: { layout: 'public', public: true, + matomoUrl: '/auth/beta-login', }, }, { @@ -45,6 +47,7 @@ export default [ meta: { public: true, layout: 'public', + matomoUrl: '/auth/verify-email', }, }, { @@ -59,6 +62,7 @@ export default [ meta: { public: true, layout: 'public', + matomoUrl: '/auth/unknown-auth-error', }, }, { @@ -67,6 +71,7 @@ export default [ name: LICENSE_ACTIVATION, meta: { layout: 'public', + matomoUrl: '/auth/license-activation', }, }, ]; diff --git a/client/src/router/index.js b/client/src/router/index.js index 7358638a..fb579a49 100644 --- a/client/src/router/index.js +++ b/client/src/router/index.js @@ -1,4 +1,5 @@ import { createRouter, createWebHistory } from 'vue-router'; +import log from 'loglevel'; import moduleRoutes from './module.routes'; import portfolioRoutes from './portfolio.routes'; @@ -13,6 +14,7 @@ import { EMAIL_NOT_VERIFIED_STATE, NO_VALID_LICENSE_STATE, SUCCESS_STATE } from import start from '@/pages/start'; import { PAGE_LOAD_TIMEOUT } from '@/consts/navigation.consts'; +import { matomoTrackPageView } from '@/helpers/matomo-client'; const instrument = () => import(/* webpackChunkName: "instruments" */ '@/pages/instrument'); const instrumentOverview = () => import(/* webpackChunkName: "instruments" */ '@/pages/instrumentOverview'); @@ -36,6 +38,7 @@ const notFoundRoute = { component: p404, meta: { layout: 'blank', + matomoUrl: '/not-found', }, }; @@ -44,6 +47,7 @@ const routes = [ path: '/', name: 'home', component: start, + meta: { matomoUrl: '/' }, }, ...moduleRoutes, ...authRoutes, @@ -56,11 +60,27 @@ const routes = [ path: '/instruments/', name: 'instrument-overview', component: instrumentOverview, + meta: { matomoUrl: '/instrument/' }, + }, + { + path: '/instrument/:slug', + name: 'instrument', + component: instrument, + meta: { layout: LAYOUT_SIMPLE, matomoUrlCallback: (to) => `/instrument/${to.params.slug}` } }, - { path: '/instrument/:slug', name: 'instrument', component: instrument, meta: { layout: LAYOUT_SIMPLE } }, { path: '/submission/:id', name: 'submission', component: submission, meta: { layout: LAYOUT_SIMPLE } }, - { path: '/topic/:topicSlug', name: 'topic', component: topic, alias: '/book/topic/:topicSlug' }, - { path: '/join-class', name: 'join-class', component: joinClass, meta: { layout: LAYOUT_SIMPLE } }, + { + path: '/topic/:topicSlug', + name: 'topic', + component: topic, + alias: '/book/topic/:topicSlug', + meta: { matomoUrlCallback: (to) => `/topic/${to.params.topicSlug}/` }, + }, + { + path: '/join-class', + name: 'join-class', + component: joinClass, + meta: { layout: LAYOUT_SIMPLE, matomoUrl: '/join-class' } }, { path: '/survey/:id', component: surveyPage, @@ -72,6 +92,7 @@ const routes = [ path: '/news', component: news, name: 'news', + meta: { matomoUrl: '/news' }, }, { path: '/oauth-redirect', @@ -126,4 +147,16 @@ router.afterEach(() => { store.dispatch('showMobileNavigation', false); }); +router.afterEach((to) => { + log.debug('matomo router.afterEach', to); + + if (to.meta.matomoUrl) { + matomoTrackPageView(to.meta.matomoUrl); + } + + if (to.meta.matomoUrlCallback) { + matomoTrackPageView(to.meta.matomoUrlCallback(to)); + } +}); + export { router, postLoginRedirectUrlKey }; diff --git a/client/src/router/me.routes.js b/client/src/router/me.routes.js index 26c08c23..f25f2760 100644 --- a/client/src/router/me.routes.js +++ b/client/src/router/me.routes.js @@ -18,43 +18,48 @@ export default [ path: '/me', component: profilePage, children: [ - { path: 'profile', name: 'profile', component: profile, meta: { isProfile: true } }, - { path: 'class', alias: 'my-class', name: 'my-class', component: myClass, meta: { isProfile: true } }, - { path: 'activity', name: 'activity', component: activity, meta: { isProfile: true } }, - { path: '', name: 'profile-activity', component: activity, meta: { isProfile: true } }, + { path: 'profile', name: 'profile', component: profile, meta: { isProfile: true, matomoUrl: '/me/profile' } }, + { path: 'class', alias: 'my-class', name: 'my-class', component: myClass, meta: { isProfile: true, matomoUrl: '/me/class' } }, + { path: 'activity', name: 'activity', component: activity, meta: { isProfile: true, matomoUrl: '/me/activity' } }, + { path: '', name: 'profile-activity', component: activity, meta: { isProfile: true, matomoUrl: '/me/activity'} }, { path: 'old-classes', name: 'old-classes', component: oldClasses, - meta: { isProfile: true }, + meta: { isProfile: true, matomoUrl: '/me/old-classes' }, }, { path: 'class/create', alias: 'create-class', name: 'create-class', component: createClass, - meta: { layout: LAYOUT_SIMPLE }, + meta: { layout: LAYOUT_SIMPLE, matomoUrl: '/me/class/create' }, }, { path: 'class/code', alias: 'show-code', name: SHOW_SCHOOL_CLASS_CODE, component: showSchoolClassCode, - meta: { layout: LAYOUT_SIMPLE }, + meta: { layout: LAYOUT_SIMPLE, matomoUrl: '/me/class/code' }, + }, + { path: 'team', name: MY_TEAM, component: myTeam, meta: { isProfile: true, matomoUrl: '/me/team' } }, + { + path: 'team/join', + name: JOIN_TEAM, + component: joinTeam, + meta: { isProfile: true, layout: LAYOUT_SIMPLE, matomoUrl: '/me/team/join' } }, - { path: 'team', name: MY_TEAM, component: myTeam, meta: { isProfile: true } }, - { path: 'team/join', name: JOIN_TEAM, component: joinTeam, meta: { isProfile: true, layout: LAYOUT_SIMPLE } }, { path: 'team/create', name: CREATE_TEAM, component: createTeam, - meta: { isProfile: true, layout: LAYOUT_SIMPLE }, + meta: { isProfile: true, layout: LAYOUT_SIMPLE, matomoUrl: '/me/team/create' } }, { path: 'team/code', name: SHOW_TEAM_CODE, component: showTeamCode, - meta: { layout: LAYOUT_SIMPLE }, + meta: { layout: LAYOUT_SIMPLE, matomoUrl: '/me/team/code' } }, ], }, diff --git a/client/src/router/onboarding.routes.js b/client/src/router/onboarding.routes.js index 3e97a3b7..efbbac4d 100644 --- a/client/src/router/onboarding.routes.js +++ b/client/src/router/onboarding.routes.js @@ -18,6 +18,7 @@ export default [ meta: { layout: 'split', next: ONBOARDING_STEP_1, + matomoUrl: '/onboarding/start', }, }, { @@ -28,6 +29,7 @@ export default [ layout: 'split', next: ONBOARDING_STEP_2, illustration: 'contents', + matomoUrl: '/onboarding/learning', }, }, { @@ -38,6 +40,7 @@ export default [ layout: 'split', next: ONBOARDING_STEP_3, illustration: 'rooms', + matomoUrl: '/onboarding/collaboration', }, }, { @@ -48,6 +51,7 @@ export default [ layout: 'split', next: 'home', illustration: 'portfolio', + matomoUrl: '/onboarding/portfolio', }, }, ], diff --git a/client/src/router/portfolio.routes.js b/client/src/router/portfolio.routes.js index 31a91ee8..80f38a22 100644 --- a/client/src/router/portfolio.routes.js +++ b/client/src/router/portfolio.routes.js @@ -7,10 +7,26 @@ const newProject = () => import(/* webpackChunkName: "portfolio" */ '@/pages/por const editProject = () => import(/* webpackChunkName: "portfolio" */ '@/pages/portfolio/editProject'); const portfolioRoutes = [ - { path: '/portfolio', name: PROJECTS_PAGE, component: portfolio, meta: { hideFooter: true } }, - { path: '/portfolio/:slug', name: 'project', component: project, props: true }, - { path: '/new-project/', name: NEW_PROJECT_PAGE, component: newProject }, - { path: '/edit-project/:slug', name: 'edit-project', component: editProject, props: true }, + { path: '/portfolio', name: PROJECTS_PAGE, component: portfolio, meta: { hideFooter: true, matomoUrl: '/portfolio/' } }, + { + path: '/portfolio/:slug', + name: 'project', + component: project, + props: true , + meta: { + matomoUrlCallback: (route) => `/portfolio/${route.params.slug}`, + } + }, + { path: '/new-project/', name: NEW_PROJECT_PAGE, component: newProject, meta: { matomoUrl: '/portfolio/' } }, + { + path: '/edit-project/:slug', + name: 'edit-project', + component: editProject, + props: true, + meta: { + matomoUrlCallback: (route) => `/portfolio/${route.params.slug}`, + } + }, ]; const routes = flavorValues.showPortfolio ? portfolioRoutes : []; diff --git a/client/src/router/room.routes.js b/client/src/router/room.routes.js index 20d3d696..d436610d 100644 --- a/client/src/router/room.routes.js +++ b/client/src/router/room.routes.js @@ -16,24 +16,29 @@ const newRoomEntry = () => import(/* webpackChunkName: "rooms" */ '@/pages/rooms const editRoomEntry = () => import(/* webpackChunkName: "rooms" */ '@/pages/rooms/editRoomEntry'); const moduleRoom = () => import(/* webpackChunkName: "rooms" */ '@/pages/module/moduleRoom'); +function matomoRoomWithSlugCallback(route) { + return `/room/${route.params.slug}/`; +} + export default [ - { path: '/rooms', name: ROOMS_PAGE, component: rooms, meta: { filter: true, hideFooter: true } }, + { path: '/rooms', name: ROOMS_PAGE, component: rooms, meta: { filter: true, hideFooter: true, matomoUrl: '/room/' }}, { path: '/new-room/', name: NEW_ROOM_PAGE, component: newRoom }, { path: '/edit-room/:id', name: 'edit-room', component: editRoom, props: true }, - { path: '/room/:slug', name: ROOM_PAGE, component: room, props: true }, - { path: '/room/:slug/add', name: ADD_ROOM_ENTRY_PAGE, component: newRoomEntry, props: true }, - { path: '/room/:slug/edit/:entrySlug', name: UPDATE_ROOM_ENTRY_PAGE, component: editRoomEntry, props: true }, + { path: '/room/:slug', name: ROOM_PAGE, component: room, props: true, meta: { matomoUrlCallback: matomoRoomWithSlugCallback }}, + { path: '/room/:slug/add', name: ADD_ROOM_ENTRY_PAGE, component: newRoomEntry, props: true, meta: { matomoUrlCallback: matomoRoomWithSlugCallback }}, + { path: '/room/:slug/edit/:entrySlug', name: UPDATE_ROOM_ENTRY_PAGE, component: editRoomEntry, meta: { matomoUrlCallback: matomoRoomWithSlugCallback }}, { path: '/module-room/:slug', name: MODULE_ROOM_PAGE, component: moduleRoom, props: true, - meta: { layout: 'fullScreen' }, + meta: { layout: 'fullScreen', matomoUrlCallback: matomoRoomWithSlugCallback }, }, { path: '/module-room/:slug/add', name: ADD_MODULE_ROOM_ENTRY_PAGE, component: newRoomEntry, props: (route) => ({ slug: route.params.slug, isModule: true }), + meta: { matomoUrlCallback: matomoRoomWithSlugCallback }, }, ]; diff --git a/client/src/setup/plugins.ts b/client/src/setup/plugins.ts index 442df600..b1318c91 100644 --- a/client/src/setup/plugins.ts +++ b/client/src/setup/plugins.ts @@ -25,10 +25,14 @@ const registerPlugins = (app: any) => { app.use(router); app.use(flavorPlugin); if (process.env.MATOMO_HOST) { + // MS-628: we use VueMatomo "only" to setup the Matomo tracker + // we will not use any of the provided functions app.use(VueMatomo, { host: process.env.MATOMO_HOST, siteId: process.env.MATOMO_SITE_ID, - router: router, + enableHeartBeatTimer: true, + // we don't want the default vue-matomo router behaviour + router: null, }); } };