Merged in feature/ms-628-metrics (pull request #126)

MS-628: First version of custom matomo integration

Approved-by: Ramon Wenger
This commit is contained in:
Daniel Egger 2023-04-12 19:00:04 +00:00 committed by Ramon Wenger
commit a98935a87f
21 changed files with 354 additions and 100 deletions

View File

@ -11,6 +11,7 @@
<script> <script>
import LOGOUT_MUTATION from '@/graphql/gql/mutations/logoutUser.gql'; import LOGOUT_MUTATION from '@/graphql/gql/mutations/logoutUser.gql';
import { matomoTrackUser } from '@/helpers/matomo-client';
export default { export default {
methods: { methods: {
@ -21,6 +22,7 @@ export default {
}) })
.then(({ data }) => { .then(({ data }) => {
if (data.logout.success) { if (data.logout.success) {
matomoTrackUser('');
location.replace('/logout'); location.replace('/logout');
} }
}); });

View File

@ -66,6 +66,7 @@ import debounce from 'lodash/debounce';
import cloneDeep from 'lodash/cloneDeep'; import cloneDeep from 'lodash/cloneDeep';
import { sanitize } from '@/helpers/text'; import { sanitize } from '@/helpers/text';
import { defineAsyncComponent } from 'vue'; import { defineAsyncComponent } from 'vue';
import { matomoTrackEvent } from '@/helpers/matomo-client';
const SubmissionForm = defineAsyncComponent(() => const SubmissionForm = defineAsyncComponent(() =>
import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/assignment/SubmissionForm.vue') import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/assignment/SubmissionForm.vue')
@ -189,6 +190,10 @@ export default {
*/ */
this.assignment.submission.text = answer; this.assignment.submission.text = answer;
this._save(this.assignment.submission); this._save(this.assignment.submission);
if (this.assignment.submission.text.length > 0) {
matomoTrackEvent('Auftrag', 'Text mit Eingabe gespeichert', this.assignment.id);
}
}, },
changeDocumentUrl(documentUrl) { changeDocumentUrl(documentUrl) {
this.assignment.submission.document = documentUrl; this.assignment.submission.document = documentUrl;
@ -210,6 +215,7 @@ export default {
}, },
}, },
}); });
matomoTrackEvent('Auftrag', 'Ergebnis mit Lehrperson geteilt', this.assignment.id);
}, },
reopen() { reopen() {
this.$apollo.mutate({ this.$apollo.mutate({
@ -259,6 +265,7 @@ export default {
.then(() => { .then(() => {
this.spellcheckLoading = false; this.spellcheckLoading = false;
}); });
matomoTrackEvent('Auftrag', 'Rechtschreibung geprüft', this.assignment.id);
}, },
}, },

View File

@ -56,6 +56,7 @@ import me from '@/mixins/me';
import APPLY_SNAPSHOT_MUTATION from 'gql/mutations/snapshots/applySnapshot.gql'; import APPLY_SNAPSHOT_MUTATION from 'gql/mutations/snapshots/applySnapshot.gql';
import { MODULE_PAGE } from '@/router/module.names'; import { MODULE_PAGE } from '@/router/module.names';
import { matomoTrackEvent } from '@/helpers/matomo-client';
const _getChange = (snapshot, index) => { const _getChange = (snapshot, index) => {
try { try {
@ -131,6 +132,11 @@ export default {
}, },
}, },
}) => { }) => {
if (this.snapshot.mine) {
matomoTrackEvent('Modul Snapshot', 'Snapshot angewendet', slug);
} else {
matomoTrackEvent('Modul Snapshot', 'Team Snapshot angewendet', slug);
}
this.$router.push({ this.$router.push({
name: MODULE_PAGE, name: MODULE_PAGE,
params: { params: {

View File

@ -55,6 +55,7 @@ import gql from 'graphql-tag';
import PenIcon from '@/components/icons/PenIcon'; import PenIcon from '@/components/icons/PenIcon';
import TrashIcon from '@/components/icons/TrashIcon'; import TrashIcon from '@/components/icons/TrashIcon';
import { removeAtIndex } from '@/graphql/immutable-operations'; import { removeAtIndex } from '@/graphql/immutable-operations';
import { matomoTrackEvent } from '@/helpers/matomo-client';
export default { export default {
props: { props: {
@ -134,49 +135,53 @@ export default {
title: 'Snapshot löschen', title: 'Snapshot löschen',
}) })
.then(() => { .then(() => {
this.$apollo.mutate({ this.$apollo
mutation: DELETE_SNAPSHOT_MUTATION, .mutate({
variables: { mutation: DELETE_SNAPSHOT_MUTATION,
input: { variables: {
id: this.snapshot.id, input: {
}, id: this.snapshot.id,
},
update: (
store,
{
data: {
deleteSnapshot: { result },
}, },
} },
) => { update: (
if (result.__typename === 'Success') { store,
const slug = this.$route.params.slug; {
const query = SNAPSHOTS_QUERY; data: {
const variables = { deleteSnapshot: { result },
slug,
};
const { module } = store.readQuery({
query,
variables,
});
const index = module.snapshots.findIndex((snapshot) => snapshot.id === this.snapshot.id);
const snapshots = removeAtIndex(module.snapshots, index);
const data = {
module: {
...module,
snapshots,
}, },
}; }
) => {
if (result.__typename === 'Success') {
const slug = this.$route.params.slug;
const query = SNAPSHOTS_QUERY;
const variables = {
slug,
};
const { module } = store.readQuery({
query,
variables,
});
const index = module.snapshots.findIndex((snapshot) => snapshot.id === this.snapshot.id);
const snapshots = removeAtIndex(module.snapshots, index);
store.writeQuery({ const data = {
query, module: {
variables, ...module,
data, snapshots,
}); },
} };
},
}); store.writeQuery({
query,
variables,
data,
});
}
},
})
.then(() => {
matomoTrackEvent('Modul Snapshot', 'Snapshot gelöscht', this.$route.params.slug);
});
}) })
.catch(() => {}); .catch(() => {});
}, },
@ -213,6 +218,13 @@ export default {
}); });
}, },
}); });
const slug = this.$route.params.slug;
if (this.snapshot.shared) {
matomoTrackEvent('Modul Snapshot', 'Snapshot mit Team geteilt', slug);
} else {
matomoTrackEvent('Modul Snapshot', 'Snapshot nicht mehr geteilt', slug);
}
}, },
}, },
}; };

View File

@ -42,6 +42,8 @@ import me from '@/mixins/me';
import CREATE_SNAPSHOT_MUTATION from '@/graphql/gql/mutations/createSnapshot.gql'; import CREATE_SNAPSHOT_MUTATION from '@/graphql/gql/mutations/createSnapshot.gql';
import MODULE_SNAPSHOTS_QUERY from '@/graphql/gql/queries/moduleSnapshots.gql'; import MODULE_SNAPSHOTS_QUERY from '@/graphql/gql/queries/moduleSnapshots.gql';
import log from 'loglevel';
import { matomoTrackEvent } from '@/helpers/matomo-client';
export default { export default {
mixins: [me], mixins: [me],
@ -109,12 +111,15 @@ export default {
createSnapshot: { snapshot }, createSnapshot: { snapshot },
}, },
}) => { }) => {
log.debug('snapshot created', snapshot);
this.showPopover = false; this.showPopover = false;
this.$modal.open('snapshot-created', { this.$modal.open('snapshot-created', {
snapshot, snapshot,
}); });
} }
); );
matomoTrackEvent('Modul Snapshot', 'Snapshot erstellt', this.$route.params.slug);
}, },
}, },
}; };

View File

@ -12,6 +12,7 @@ import Toggle from '@/components/ui/Toggle';
// import MODULE_DETAILS_QUERY from '@/graphql/gql/queries/modules/moduleDetailsQuery.gql'; // import MODULE_DETAILS_QUERY from '@/graphql/gql/queries/modules/moduleDetailsQuery.gql';
import { gql } from '@apollo/client/core'; import { gql } from '@apollo/client/core';
import { setModuleEditMode } from '@/graphql/cache-operations'; import { setModuleEditMode } from '@/graphql/cache-operations';
import { matomoTrackEvent } from '@/helpers/matomo-client';
const QUERY = gql` const QUERY = gql`
query ModuleEditModeQuery($slug: String) { query ModuleEditModeQuery($slug: String) {
@ -56,6 +57,11 @@ export default {
methods: { methods: {
toggle(newChecked) { toggle(newChecked) {
setModuleEditMode(this.slug, newChecked); setModuleEditMode(this.slug, newChecked);
if (newChecked) {
matomoTrackEvent('Modul Anpassen', 'Eingeschaltet', this.slug);
} else {
matomoTrackEvent('Modul Anpassen', 'Ausgeschaltet', this.slug);
}
}, },
}, },
}; };

View File

@ -20,10 +20,12 @@
<script> <script>
import me from '@/mixins/me'; import me from '@/mixins/me';
import { TYPES, CONTENT_TYPE } from '@/consts/types'; import { CONTENT_TYPE, TYPES } from '@/consts/types';
import { createVisibilityMutation, hidden } from '@/helpers/visibility'; import { createVisibilityMutation, hidden } from '@/helpers/visibility';
import { defineAsyncComponent } from 'vue'; import { defineAsyncComponent } from 'vue';
import { matomoTrackModuleVisibilityEvent } from '@/helpers/matomo-client';
const EyeIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */ '@/components/icons/EyeIcon')); const EyeIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */ '@/components/icons/EyeIcon'));
const ClosedEyeIcon = defineAsyncComponent(() => const ClosedEyeIcon = defineAsyncComponent(() =>
import(/* webpackChunkName: "icons" */ '@/components/icons/ClosedEyeIcon') import(/* webpackChunkName: "icons" */ '@/components/icons/ClosedEyeIcon')
@ -76,6 +78,8 @@ export default {
mutation, mutation,
variables, variables,
}); });
matomoTrackModuleVisibilityEvent(this.type, this.block, hidden);
}, },
}, },
}; };

View File

@ -2,6 +2,9 @@
query RoomEntryQuery($slug: String!) { query RoomEntryQuery($slug: String!) {
roomEntry(slug: $slug) { roomEntry(slug: $slug) {
...RoomEntryParts ...RoomEntryParts
room {
slug
}
comments { comments {
text text
owner { owner {

View File

@ -0,0 +1,101 @@
import log from 'loglevel';
import {
CHAPTER_DESCRIPTION_TYPE,
CHAPTER_TITLE_TYPE,
CONTENT_TYPE,
OBJECTIVE_GROUP_TYPE,
OBJECTIVE_TYPE,
} from '@/consts/types';
export type matomoUserRole = 'Student' | 'Teacher';
let matomoLastUrl = '';
let matomoLastUserId = '';
let matomoLastEventData = '';
export function matomoTrackPageView(absolutePath: string = '/', title?: string) {
// see https://developer.matomo.org/guides/spa-tracking for details
const url = window.location.origin + absolutePath;
if (matomoLastUrl !== url) {
// do not track the same url twice
log.debug('trackMatomoPageView', { url, matomoLastUrl, title });
// @ts-ignore
window._paq.push(['setCustomUrl', url]);
// @ts-ignore
window._paq.push(['trackPageView', title]);
matomoLastUrl = url;
}
}
export function matomoTrackEvent(category: string, action: string, name?: string, value?: number) {
const data = { category, action, name, value };
const dataJSON = JSON.stringify(data);
if (matomoLastEventData !== dataJSON) {
log.debug('matomoTrackEvent', { category, action, name, value });
// @ts-ignore
window._paq.push(['trackEvent', category, action, name, value]);
matomoLastEventData = dataJSON;
}
}
export function matomoTrackModuleVisibilityEvent(blockType: string, block: any, hidden: boolean) {
let eventAction = '';
let eventName = undefined;
switch (blockType) {
case CONTENT_TYPE:
eventAction = hidden ? 'Inhalt ausgeblendet' : 'Inhalt eingeblendet';
eventName = block.title;
break;
case OBJECTIVE_TYPE:
eventAction = hidden ? 'Lernziel ausgeblendet' : 'Lernziel eingeblendet';
eventName = block.text;
break;
case OBJECTIVE_GROUP_TYPE:
eventAction = hidden ? 'Lernzielgruppe ausgeblendet' : 'Lernzielgruppe eingeblendet';
eventName = block.displayTitle;
break;
case CHAPTER_TITLE_TYPE:
eventAction = hidden ? 'Kapitelüberschrift ausgeblendet' : 'Kapitelüberschrift eingeblendet';
eventName = block.title;
break;
case CHAPTER_DESCRIPTION_TYPE:
eventAction = hidden ? 'Kapitelbeschreibung ausgeblendet' : 'Kapitelbeschreibung eingeblendet';
eventName = block.title;
break;
}
matomoTrackEvent('Modul Anpassen - Sichtbarkeit', eventAction, eventName);
}
export function matomoTrackUser(userId: string, role: matomoUserRole = 'Student') {
// see https://developer.matomo.org/guides/tracking-javascript-guide#user-id for the process
if (matomoLastUserId !== userId) {
log.debug('matomoTrackUser', { userId, matomoLastUserId, role });
if (userId) {
// @ts-ignore
window._paq.push(['setUserId', userId]);
// @ts-ignore
window._paq.push(['setCustomDimension', 1, role]);
} else {
// @ts-ignore
window._paq.push(['deleteCustomDimension', 1]);
// User has just logged out, we reset the User ID
// @ts-ignore
window._paq.push(['resetUserId']);
// we also force a new visit to be created for the pageviews after logout
// @ts-ignore
window._paq.push(['appendToTrackingUrl', 'new_visit=1']);
// @ts-ignore
window._paq.push(['trackPageView']);
// we finally make sure to not again create a new visit afterwards (important for Single Page Applications)
// @ts-ignore
window._paq.push(['appendToTrackingUrl', '']);
}
matomoLastUserId = userId;
}
}

View File

@ -1,6 +1,7 @@
import ME_QUERY from '@/graphql/gql/queries/meQuery.gql'; import ME_QUERY from '@/graphql/gql/queries/meQuery.gql';
import { computed } from 'vue'; import { computed } from 'vue';
import { useQuery } from '@vue/apollo-composable'; import { useQuery } from '@vue/apollo-composable';
import { matomoTrackPageView, matomoTrackUser } from '@/helpers/matomo-client';
export interface Me { export interface Me {
selectedClass: { selectedClass: {
@ -110,7 +111,6 @@ const meMixin = {
return getTopicRoute(this.$data.me); return getTopicRoute(this.$data.me);
}, },
schoolClass(): any { schoolClass(): any {
console.log('schoolClass legacy');
// @ts-ignore // @ts-ignore
return getSelectedClass(this.$data.me); return getSelectedClass(this.$data.me);
}, },
@ -137,6 +137,13 @@ const meMixin = {
// @ts-ignore // @ts-ignore
return this.$getRidOfEdges(data).me; return this.$getRidOfEdges(data).me;
}, },
// @ts-ignore
result({ data }) {
const me = data.me || defaultMe.me;
if (me.id) {
matomoTrackUser(me.id ?? '', me.isTeacher ? 'Teacher' : 'Student');
}
},
}, },
}, },
}; };

View File

@ -39,6 +39,7 @@ import ADD_COMMENT_MUTATION from 'gql/mutations/addComment.gql';
import CommentInput from '@/components/rooms/CommentInput'; import CommentInput from '@/components/rooms/CommentInput';
import Comment from '@/components/rooms/Comment'; import Comment from '@/components/rooms/Comment';
import { defineAsyncComponent } from 'vue'; import { defineAsyncComponent } from 'vue';
import { matomoTrackPageView } from '@/helpers/matomo-client';
const TextBlock = defineAsyncComponent(() => const TextBlock = defineAsyncComponent(() =>
import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/TextBlock') import(/* webpackChunkName: "content-components" */ '@/components/content-blocks/TextBlock')
); );
@ -148,6 +149,9 @@ export default {
variables: { variables: {
slug: this.$route.params.slug, slug: this.$route.params.slug,
}, },
result() {
matomoTrackPageView(`/room/${this.roomEntry.room.slug}/${this.roomEntry.slug}`);
},
}; };
}, },
}, },

View File

@ -9,6 +9,9 @@
<script> <script>
import ModuleNavigation from '@/components/modules/ModuleNavigation.vue'; import ModuleNavigation from '@/components/modules/ModuleNavigation.vue';
import ModuleSubNavigation from '@/components/modules/ModuleSubNavigation.vue'; import ModuleSubNavigation from '@/components/modules/ModuleSubNavigation.vue';
import MODULE_DETAILS_QUERY from '@/graphql/gql/queries/modules/moduleDetailsQuery.gql';
import { matomoTrackPageView } from '@/helpers/matomo-client';
export default { export default {
components: { components: {
@ -16,6 +19,12 @@ export default {
ModuleSubNavigation, ModuleSubNavigation,
}, },
data() {
return {
module: {},
};
},
computed: { computed: {
showNavigation() { showNavigation() {
return !this.$route.meta.hideNavigation && !this.$route.meta.showSubNavigation; return !this.$route.meta.hideNavigation && !this.$route.meta.showSubNavigation;
@ -24,6 +33,21 @@ export default {
return !!this.$route.meta.showSubNavigation; return !!this.$route.meta.showSubNavigation;
}, },
}, },
apollo: {
module() {
return {
query: MODULE_DETAILS_QUERY,
variables: {
slug: this.$route.params.slug,
},
result() {
matomoTrackPageView(`/topic/${this.module.topic.slug}/${this.module.slug}`);
},
fetchPolicy: 'cache-first',
};
},
},
}; };
</script> </script>

View File

@ -36,6 +36,7 @@ import { isTeacher } from '@/helpers/is-teacher';
import { meQuery } from '@/graphql/queries'; import { meQuery } from '@/graphql/queries';
import { defineAsyncComponent } from 'vue'; import { defineAsyncComponent } from 'vue';
import { matomoTrackEvent } from '@/helpers/matomo-client';
const Solution = defineAsyncComponent(() => const Solution = defineAsyncComponent(() =>
import( import(
/* webpackChunkName: "content-components" /* webpackChunkName: "content-components"
@ -120,6 +121,7 @@ export default {
mounted() { mounted() {
if (this.surveyData) { if (this.surveyData) {
this.loadSurveyFromServer(this.surveyData); this.loadSurveyFromServer(this.surveyData);
matomoTrackEvent('Übung', 'Übung angezeigt', this.id);
} }
}, },
@ -172,42 +174,42 @@ export default {
data: JSON.stringify(data), data: JSON.stringify(data),
}; };
this.$apollo this.$apollo.mutate({
.mutate({ mutation: UPDATE_ANSWER,
mutation: UPDATE_ANSWER, variables: {
variables: { input: {
input: { answer,
answer, },
},
update: (
store,
{
data: {
updateAnswer: { answer },
}, },
},
update: (
store,
{
data: {
updateAnswer: { answer },
},
}
) => {
const query = SURVEY_QUERY;
const variables = { id: this.id };
const { survey } = store.readQuery({ query, variables });
if (survey) {
const newData = {
// data is already in use in parent scope
survey: {
...survey,
answer,
},
};
store.writeQuery({ query, variables, data: newData });
}
},
})
.then(() => {
if (exit) {
this.$router.go(-1);
} }
}); ) => {
const query = SURVEY_QUERY;
const variables = { id: this.id };
const { survey } = store.readQuery({ query, variables });
if (survey) {
const newData = {
// data is already in use in parent scope
survey: {
...survey,
answer,
},
};
store.writeQuery({ query, variables, data: newData });
}
},
});
if (exit) {
matomoTrackEvent('Übung', 'Übung erfolgreich abgeschlossen', this.id);
this.$router.go(-1);
} else {
matomoTrackEvent('Übung', 'Übungsschritt abgeschlossen', this.id);
}
}; };
survey.onComplete.add((sender, options) => { survey.onComplete.add((sender, options) => {

View File

@ -53,6 +53,7 @@ import TopicNavigation from '@/components/book-navigation/TopicNavigation';
import UPDATE_LAST_TOPIC_MUTATION from '@/graphql/gql/mutations/updateLastTopic.gql'; import UPDATE_LAST_TOPIC_MUTATION from '@/graphql/gql/mutations/updateLastTopic.gql';
import ME_QUERY from '@/graphql/gql/queries/meQuery.gql'; import ME_QUERY from '@/graphql/gql/queries/meQuery.gql';
const PlayIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */ '@/components/icons/Play')); const PlayIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */ '@/components/icons/Play'));
const BulbIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */ '@/components/icons/BulbIcon')); const BulbIcon = defineAsyncComponent(() => import(/* webpackChunkName: "icons" */ '@/components/icons/BulbIcon'));

View File

@ -15,6 +15,7 @@ export default [
public: true, public: true,
illustration: 'hello', illustration: 'hello',
illustrationAlign: 'top', illustrationAlign: 'top',
matomoUrl: '/auth/hello',
}, },
}, },
{ {
@ -24,6 +25,7 @@ export default [
meta: { meta: {
layout: 'public', layout: 'public',
public: true, public: true,
matomoUrl: '/auth/beta-login',
}, },
}, },
{ {
@ -45,6 +47,7 @@ export default [
meta: { meta: {
public: true, public: true,
layout: 'public', layout: 'public',
matomoUrl: '/auth/verify-email',
}, },
}, },
{ {
@ -59,6 +62,7 @@ export default [
meta: { meta: {
public: true, public: true,
layout: 'public', layout: 'public',
matomoUrl: '/auth/unknown-auth-error',
}, },
}, },
{ {
@ -67,6 +71,7 @@ export default [
name: LICENSE_ACTIVATION, name: LICENSE_ACTIVATION,
meta: { meta: {
layout: 'public', layout: 'public',
matomoUrl: '/auth/license-activation',
}, },
}, },
]; ];

View File

@ -13,6 +13,7 @@ import { EMAIL_NOT_VERIFIED_STATE, NO_VALID_LICENSE_STATE, SUCCESS_STATE } from
import start from '@/pages/start'; import start from '@/pages/start';
import { PAGE_LOAD_TIMEOUT } from '@/consts/navigation.consts'; import { PAGE_LOAD_TIMEOUT } from '@/consts/navigation.consts';
import { matomoTrackPageView } from '@/helpers/matomo-client';
const instrument = () => import(/* webpackChunkName: "instruments" */ '@/pages/instrument'); const instrument = () => import(/* webpackChunkName: "instruments" */ '@/pages/instrument');
const instrumentOverview = () => import(/* webpackChunkName: "instruments" */ '@/pages/instrumentOverview'); const instrumentOverview = () => import(/* webpackChunkName: "instruments" */ '@/pages/instrumentOverview');
@ -36,6 +37,7 @@ const notFoundRoute = {
component: p404, component: p404,
meta: { meta: {
layout: 'blank', layout: 'blank',
matomoUrl: '/not-found',
}, },
}; };
@ -44,6 +46,7 @@ const routes = [
path: '/', path: '/',
name: 'home', name: 'home',
component: start, component: start,
meta: { matomoUrl: '/' },
}, },
...moduleRoutes, ...moduleRoutes,
...authRoutes, ...authRoutes,
@ -56,11 +59,28 @@ const routes = [
path: '/instruments/', path: '/instruments/',
name: 'instrument-overview', name: 'instrument-overview',
component: instrumentOverview, 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: '/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', path: '/survey/:id',
component: surveyPage, component: surveyPage,
@ -72,6 +92,7 @@ const routes = [
path: '/news', path: '/news',
component: news, component: news,
name: 'news', name: 'news',
meta: { matomoUrl: '/news' },
}, },
{ {
path: '/oauth-redirect', path: '/oauth-redirect',
@ -126,4 +147,14 @@ router.afterEach(() => {
store.dispatch('showMobileNavigation', false); store.dispatch('showMobileNavigation', false);
}); });
router.afterEach((to) => {
if (to.meta.matomoUrl) {
matomoTrackPageView(to.meta.matomoUrl);
}
if (to.meta.matomoUrlCallback) {
matomoTrackPageView(to.meta.matomoUrlCallback(to));
}
});
export { router, postLoginRedirectUrlKey }; export { router, postLoginRedirectUrlKey };

View File

@ -18,43 +18,48 @@ export default [
path: '/me', path: '/me',
component: profilePage, component: profilePage,
children: [ children: [
{ path: 'profile', name: 'profile', component: profile, 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 } }, { 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 } }, { path: 'activity', name: 'activity', component: activity, meta: { isProfile: true, matomoUrl: '/me/activity' } },
{ path: '', name: 'profile-activity', component: activity, meta: { isProfile: true } }, { path: '', name: 'profile-activity', component: activity, meta: { isProfile: true, matomoUrl: '/me/activity'} },
{ {
path: 'old-classes', path: 'old-classes',
name: 'old-classes', name: 'old-classes',
component: oldClasses, component: oldClasses,
meta: { isProfile: true }, meta: { isProfile: true, matomoUrl: '/me/old-classes' },
}, },
{ {
path: 'class/create', path: 'class/create',
alias: 'create-class', alias: 'create-class',
name: 'create-class', name: 'create-class',
component: createClass, component: createClass,
meta: { layout: LAYOUT_SIMPLE }, meta: { layout: LAYOUT_SIMPLE, matomoUrl: '/me/class/create' },
}, },
{ {
path: 'class/code', path: 'class/code',
alias: 'show-code', alias: 'show-code',
name: SHOW_SCHOOL_CLASS_CODE, name: SHOW_SCHOOL_CLASS_CODE,
component: showSchoolClassCode, 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', path: 'team/create',
name: CREATE_TEAM, name: CREATE_TEAM,
component: createTeam, component: createTeam,
meta: { isProfile: true, layout: LAYOUT_SIMPLE }, meta: { isProfile: true, layout: LAYOUT_SIMPLE, matomoUrl: '/me/team/create' }
}, },
{ {
path: 'team/code', path: 'team/code',
name: SHOW_TEAM_CODE, name: SHOW_TEAM_CODE,
component: showTeamCode, component: showTeamCode,
meta: { layout: LAYOUT_SIMPLE }, meta: { layout: LAYOUT_SIMPLE, matomoUrl: '/me/team/code' }
}, },
], ],
}, },

View File

@ -18,6 +18,7 @@ export default [
meta: { meta: {
layout: 'split', layout: 'split',
next: ONBOARDING_STEP_1, next: ONBOARDING_STEP_1,
matomoUrl: '/onboarding/start',
}, },
}, },
{ {
@ -28,6 +29,7 @@ export default [
layout: 'split', layout: 'split',
next: ONBOARDING_STEP_2, next: ONBOARDING_STEP_2,
illustration: 'contents', illustration: 'contents',
matomoUrl: '/onboarding/learning',
}, },
}, },
{ {
@ -38,6 +40,7 @@ export default [
layout: 'split', layout: 'split',
next: ONBOARDING_STEP_3, next: ONBOARDING_STEP_3,
illustration: 'rooms', illustration: 'rooms',
matomoUrl: '/onboarding/collaboration',
}, },
}, },
{ {
@ -48,6 +51,7 @@ export default [
layout: 'split', layout: 'split',
next: 'home', next: 'home',
illustration: 'portfolio', illustration: 'portfolio',
matomoUrl: '/onboarding/portfolio',
}, },
}, },
], ],

View File

@ -7,10 +7,26 @@ const newProject = () => import(/* webpackChunkName: "portfolio" */ '@/pages/por
const editProject = () => import(/* webpackChunkName: "portfolio" */ '@/pages/portfolio/editProject'); const editProject = () => import(/* webpackChunkName: "portfolio" */ '@/pages/portfolio/editProject');
const portfolioRoutes = [ const portfolioRoutes = [
{ path: '/portfolio', name: PROJECTS_PAGE, component: portfolio, meta: { hideFooter: true } }, { path: '/portfolio', name: PROJECTS_PAGE, component: portfolio, meta: { hideFooter: true, matomoUrl: '/portfolio/' } },
{ path: '/portfolio/:slug', name: 'project', component: project, props: true }, {
{ path: '/new-project/', name: NEW_PROJECT_PAGE, component: newProject }, path: '/portfolio/:slug',
{ path: '/edit-project/:slug', name: 'edit-project', component: editProject, props: true }, 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 : []; const routes = flavorValues.showPortfolio ? portfolioRoutes : [];

View File

@ -16,24 +16,29 @@ const newRoomEntry = () => import(/* webpackChunkName: "rooms" */ '@/pages/rooms
const editRoomEntry = () => import(/* webpackChunkName: "rooms" */ '@/pages/rooms/editRoomEntry'); const editRoomEntry = () => import(/* webpackChunkName: "rooms" */ '@/pages/rooms/editRoomEntry');
const moduleRoom = () => import(/* webpackChunkName: "rooms" */ '@/pages/module/moduleRoom'); const moduleRoom = () => import(/* webpackChunkName: "rooms" */ '@/pages/module/moduleRoom');
function matomoRoomWithSlugCallback(route) {
return `/room/${route.params.slug}/`;
}
export default [ 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: '/new-room/', name: NEW_ROOM_PAGE, component: newRoom },
{ path: '/edit-room/:id', name: 'edit-room', component: editRoom, props: true }, { path: '/edit-room/:id', name: 'edit-room', component: editRoom, props: true },
{ path: '/room/:slug', name: ROOM_PAGE, component: room, 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 }, { 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, props: true }, { path: '/room/:slug/edit/:entrySlug', name: UPDATE_ROOM_ENTRY_PAGE, component: editRoomEntry, meta: { matomoUrlCallback: matomoRoomWithSlugCallback }},
{ {
path: '/module-room/:slug', path: '/module-room/:slug',
name: MODULE_ROOM_PAGE, name: MODULE_ROOM_PAGE,
component: moduleRoom, component: moduleRoom,
props: true, props: true,
meta: { layout: 'fullScreen' }, meta: { layout: 'fullScreen', matomoUrlCallback: matomoRoomWithSlugCallback },
}, },
{ {
path: '/module-room/:slug/add', path: '/module-room/:slug/add',
name: ADD_MODULE_ROOM_ENTRY_PAGE, name: ADD_MODULE_ROOM_ENTRY_PAGE,
component: newRoomEntry, component: newRoomEntry,
props: (route) => ({ slug: route.params.slug, isModule: true }), props: (route) => ({ slug: route.params.slug, isModule: true }),
meta: { matomoUrlCallback: matomoRoomWithSlugCallback },
}, },
]; ];

View File

@ -33,10 +33,14 @@ const registerPlugins = (app: any) => {
app.use(router); app.use(router);
app.use(flavorPlugin); app.use(flavorPlugin);
if (process.env.MATOMO_HOST) { 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, { app.use(VueMatomo, {
host: process.env.MATOMO_HOST, host: process.env.MATOMO_HOST,
siteId: process.env.MATOMO_SITE_ID, siteId: process.env.MATOMO_SITE_ID,
router: router, enableHeartBeatTimer: true,
// we don't want the default vue-matomo router behaviour
router: null,
}); });
} }
}; };