Browse Source

Login update

master
Joshua Rubingh 2 months ago
parent
commit
f402b6c028
  1. 2
      .dockerignore
  2. 2
      components/app/list/item.vue
  3. 4
      components/apps/grid.vue
  4. 2
      components/contributor/list/item.vue
  5. 7
      components/contributors/inviteButton.vue
  6. 8
      components/contributors/list.vue
  7. 2
      components/logo/rug.vue
  8. 24
      components/navigation/index.vue
  9. 20
      components/navigation/languageSwitcher.vue
  10. 2
      components/researchStudies/createButton.vue
  11. 2
      components/researchStudies/deleteButton.vue
  12. 7
      components/researchStudies/list.vue
  13. 15
      components/user/profile/list/item.vue
  14. 8
      layouts/default.vue
  15. 2
      layouts/error.vue
  16. 14
      layouts/landing.vue
  17. 9
      lib/redirect.js
  18. 103
      lib/routes.js
  19. 2
      locales/en.js
  20. 55
      locales/nl.js
  21. 35
      middleware/auth.js
  22. 62
      nuxt.config.js
  23. 1
      package.json
  24. 2
      pages/index.vue
  25. 33
      pages/login.vue
  26. 0
      pages/profile/index.vue
  27. 2
      pages/researchStudies/_studyId/apps/index.vue
  28. 7
      pages/researchStudies/_studyId/apps/windows-vdi/_appId/index.vue
  29. 17
      pages/researchStudies/_studyId/apps/windows-vdi/create.vue
  30. 10
      pages/researchStudies/_studyId/contributors/_contributorId/edit.vue
  31. 0
      pages/researchStudies/_studyId/contributors/index.vue
  32. 7
      pages/researchStudies/_studyId/contributors/invite.vue
  33. 0
      pages/researchStudies/_studyId/index.vue
  34. 0
      pages/researchStudies/_studyId/settings.vue
  35. 7
      pages/researchStudies/create.vue
  36. 0
      pages/researchStudies/index.vue
  37. 16
      plugins/axios.js
  38. 52
      store/authorisation.js
  39. 16
      store/index.js
  40. 101
      yarn.lock

2
.dockerignore

@ -0,0 +1,2 @@
node_modules
npm-debug.log

2
components/app/list/item.vue

@ -1,5 +1,5 @@
<template>
<v-card link :to="{ name: `researchStudies.study.apps.${app.component.slug}`, params: {studyId: studyId, appId: app.id } }">
<v-card link :to="localePath(`/researchStudies/${studyId}/apps/${app.component.slug}/${app.id}`)">
<v-img
:src="app.avatar"
height="50"

4
components/apps/grid.vue

@ -106,12 +106,12 @@ app
},
methods: {
navigateToRow(row) {
this.$router.push({
this.$router.push(this.app.localePath({
name: 'researchStudies.study.overview',
params: {
studyId: row.id
}
});
}));
}
},
}

2
components/contributor/list/item.vue

@ -1,5 +1,5 @@
<template>
<v-list-item class="px-2" link :to="{ name: 'researchStudies.study.contributors.edit', params: { studyId: studyId, contributorId: contributor.id } }">
<v-list-item class="px-2" link :to="localePath(`/researchStudies/${studyId}/contributors/${contributor.id}/edit`)">
<v-list-item-avatar>
<v-img :src="contributor.researcher.avatar ||`https://randomuser.me/api/portraits/men/${contributor.researcher.id+6}.jpg`"></v-img>
</v-list-item-avatar>

7
components/contributors/inviteButton.vue

@ -14,12 +14,7 @@ export default {
},
methods: {
goToInviteContributor() {
this.$router.push({
name: 'researchStudies.study.contributors.invite',
params: {
studyId: this.studyId,
}
})
this.$router.push(this.localePath(`/researchStudies/${this.studyId}/contributors/invite`))
}
}
}

8
components/contributors/list.vue

@ -118,13 +118,7 @@ export default {
},
methods: {
navigateToRow(row) {
this.$router.push({
name: 'researchStudies.study.contributors.edit',
params: {
studyId: this.studyId,
contributorId: row.id
}
});
this.$router.push(this.localePath(`/researchStudies/${this.studyId}/contributors/${row.id}/edit`))
},
customFilter(_, search, item) {
search = search.toString().toLowerCase();

2
components/logo/rug.vue

@ -1,5 +1,5 @@
<template>
<router-link :to="{ name: 'landing' }">
<router-link :to="localePath('/')">
<img
height="50"
:src="require('~/assets/logo.png')" alt="RUG"

24
components/navigation.vue → components/navigation/index.vue

@ -1,13 +1,13 @@
<template>
<div>
<v-list>
<UserProfileListItem :user="user" />
<user-profile-list-item :user="getUser" />
</v-list>
<v-divider></v-divider>
<v-list>
<v-list-item link :to="{ name: 'researchStudies' }">
<v-list-item link :to="localePath('/researchStudies')">
<v-list-item-icon>
<v-icon>mdi-view-list</v-icon>
</v-list-item-icon>
@ -27,28 +27,28 @@
</v-list-item-content>
</template>
<v-list-item link :to="{ name: 'researchStudies.study.overview', params: { studyId: $route.params.studyId } }">
<v-list-item link :to="localePath(`/researchStudies/${$route.params.studyId}`)">
<v-list-item-icon>
<v-icon>mdi-file-document</v-icon>
</v-list-item-icon>
<v-list-item-title>{{ $t('link.researchStudies.study.overview') }}</v-list-item-title>
</v-list-item>
<v-list-item link :to="{ name: 'researchStudies.study.contributors', params: { studyId: $route.params.studyId } }">
<v-list-item link :to="localePath(`/researchStudies/${$route.params.studyId}/contributors`)">
<v-list-item-icon>
<v-icon>mdi-account-group</v-icon>
</v-list-item-icon>
<v-list-item-title>{{ $t('link.researchStudies.study.contributors') }}</v-list-item-title>
</v-list-item>
<v-list-item link :to="{ name: 'researchStudies.study.apps', params: { studyId: $route.params.studyId } }">
<v-list-item link :to="localePath(`/researchStudies/${$route.params.studyId}/apps`)">
<v-list-item-icon>
<v-icon>mdi-desktop-mac</v-icon>
</v-list-item-icon>
<v-list-item-title>{{ $t('link.researchStudies.study.apps') }}</v-list-item-title>
</v-list-item>
<v-list-item link :to="{ name: 'researchStudies.study.settings', params: { studyId: $route.params.studyId } }">
<v-list-item link :to="localePath(`/researchStudies/${$route.params.studyId}/settings`)">
<v-list-item-icon>
<v-icon>mdi-cog</v-icon>
</v-list-item-icon>
@ -63,18 +63,16 @@
import { mapGetters } from 'vuex';
export default {
data() {
return {
user: this.$auth.user,
}
},
computed: {
showStudyDetailNavigation() {
return this.$route.params?.studyId;
},
...mapGetters('studies', [
'getActiveStudy',
]),
'getActiveStudy',
]),
...mapGetters('authorisation', [
'getUser'
])
},
}
</script>

20
components/navigation/languageSwitcher.vue

@ -0,0 +1,20 @@
<template>
<div>
lang links:
<nuxt-link
v-for="locale in availableLocales"
:key="locale.code"
:to="switchLocalePath(locale.code)">{{ locale.name }}</nuxt-link>
</div>
</template>
<script>
export default {
computed: {
availableLocales () {
return this.$i18n.locales.filter(i => i.code !== this.$i18n.locale)
}
},
};
</script>

2
components/researchStudies/createButton.vue

@ -1,6 +1,6 @@
<template>
<v-btn
:to="{ name: 'researchStudies.create' }"
:to="localePath('/researchStudies/create')"
>
<v-icon>mdi-plus</v-icon>
{{ $t('researchStudies.createButton.button') }}

2
components/researchStudies/deleteButton.vue

@ -64,7 +64,7 @@ export default {
action: this['studies/deleteStudy'],
onResponse() {
this.dialog = false;
this.$router.push({ name: 'researchStudies' });
this.$router.push(this.localePath(`/researchStudies/`))
}
}),
}

7
components/researchStudies/list.vue

@ -97,12 +97,7 @@
},
methods: {
navigateToRow(row) {
this.$router.push({
name: 'researchStudies.study.overview',
params: {
studyId: row.id
}
});
this.$router.push(this.localePath(`/researchStudies/${row.id}`))
},
customFilter(_, search, item) {
search = search.toString().toLowerCase();

15
components/user/profile/list/item.vue

@ -1,13 +1,13 @@
<template>
<v-list-item class="px-2">
<v-list-item-avatar>
<v-img :src="user.avatar || `https://randomuser.me/api/portraits/men/${user.id}.jpg`"></v-img>
<v-img :src="getUser.avatar || `https://randomuser.me/api/portraits/men/${getUser.id}.jpg`"></v-img>
</v-list-item-avatar>
<v-list-item-content>
<v-list-item-title class="text-h6">
{{user.display_name}}
{{getUser.display_name}}
</v-list-item-title>
<v-list-item-subtitle>{{user.email_address}}</v-list-item-subtitle>
<v-list-item-subtitle>{{getUser.email_address}}</v-list-item-subtitle>
</v-list-item-content>
</v-list-item>
</template>
@ -24,5 +24,14 @@ export default {
})
}
},
computed: {
getUser() {
return this.user || {
avatar: '',
display_name: '',
email_address: ''
}
}
}
}
</script>

8
layouts/default.vue

@ -7,15 +7,15 @@
expand-on-hover
clipped
>
<Navigation />
<navigation />
</v-navigation-drawer>
<v-app-bar
app
clipped-left
>
<LogoRug />
<router-view name="header"></router-view>
<logo-rug />
<navigation-language-switcher />
</v-app-bar>
<v-main>
@ -25,4 +25,4 @@
</v-main>
</v-app>
</template>
</template>

2
layouts/error.vue

@ -6,7 +6,7 @@
<h1 v-else>
{{ otherError }}
</h1>
<NuxtLink to="/">
<NuxtLink :to="localePath('/')">
{{ $t('link.home') }}
</NuxtLink>
</v-app>

14
layouts/landing.vue

@ -5,7 +5,7 @@
app
clipped-left
>
<LogoRug />
<logo-rug />
<router-view name="header"></router-view>
</v-app-bar>
@ -17,15 +17,3 @@
</v-app>
</template>
<script>
export default {
data () {
return {
title: 'landing layout'
}
}
}
</script>

9
lib/redirect.js

@ -0,0 +1,9 @@
export const redirectToLogin = (redirect, url) => {
const authUrl = `/auth/login/?next=${url}`;
if (process.client)
location.href = authUrl
else
redirect(`/auth/login/?next=${url}`)
}

103
lib/routes.js

@ -1,103 +0,0 @@
const createRouteProcessor = (resolver) => {
const routeProcessor = (routes) => {
routes.forEach((route) => {
const chunks = {};
// normalize `component` to `components = {default: ...}`
if (route.component) {
route.components = {default: route.component}
delete route.component;
}
// help message for development
if (typeof route.components !== 'object') {
// eslint-disable-next-line no-console
console.error(`Misconfigured "component(s)" for route: ${route.name}`);
}
// traverse components
Object.keys(route.components).forEach((componentKey) => {
chunks[componentKey] = route.components[componentKey];
route.components[componentKey] = resolver(route.components[componentKey])
});
// add chunks for route
route.chunkNames = chunks;
// parse path and add type conversion for params
// normalizePathParams(route);
// recurse children
if (route.children) {
routeProcessor(route.children);
}
});
};
return routeProcessor;
};
const prepareRoutes = (parsedRoutes, resolveComponent, routeConfig) => {
for(let i=parsedRoutes.length; i>1; i--) {
parsedRoutes.pop()
}
const routeProcessor = createRouteProcessor(resolveComponent);
routeProcessor(routeConfig.routes);
routeConfig.routes.forEach((route) => {
parsedRoutes.push(route);
})
};
// // parse path and add type conversion for params
// // example:
// // path: `/studies/:studyId<Int>/`
// // becomes: `/studies/:studyId/`
// // and a conversion method is added for studyId => Int(params.studyId)
// /// en/researchStudies/:studyId<Int>/apps/',
// const normalizePathParams = route => {
// const hasLeadingSlash = route.path[0] === '/';
// const hasTrailingSlash = route.path[route.path.length -1] === '/';
// const startSliceAt = Number(hasLeadingSlash);
// const endSliceAt = hasTrailingSlash ? -1: undefined;
// const pathParts = route.path
// .slice(startSliceAt, endSliceAt)
// .split('/');
// const casts = {};
// const mappedPath = pathParts.map(part => {
// let partName = part, partTypeDefinition, prefix = ''
// if (part[0] === ':') {
// prefix = ':';
// [partName, partTypeDefinition] = part.slice(1).split('<')
// let paramType = getParamTypeCaster((partTypeDefinition || '').slice(0, -1))
// casts[partName] = paramType
// }
// return prefix + partName
// });
// route.path = (hasLeadingSlash ? '/' : '')
// + mappedPath.join('/')
// + (hasTrailingSlash ? '/' : '')
// if (Object.keys(casts).length) {
// route.props = createRoutePropsCaster(casts);
// }
// }
// const createRoutePropsCaster = (config) => (route) => {
// const entries = Object.entries(config);
// const castEntries = entries.map((name, cast) => [name, cast(route.params[name])]);
// return Object.fromEntries(castEntries);
// }
// const getParamTypeCaster = (type) => {
// if (type === 'Number') return Number;
// if (type === 'Date') return Date;
// return String;
// }
export default prepareRoutes

2
locales/en.js

@ -32,7 +32,7 @@ export default {
'study.createdAt': 'Created at',
'study.updatedAt': 'Updated at',
'study.startDate': 'Start Date',
'conributor.role': 'Role',
'contributor.role': 'Role',
'contributor.role.owner': 'OWNER',
'ui.rug.form.action.save': 'Save',
'ui.rug.form.action.cancel': 'Cancel',

55
locales/nl.js

@ -0,0 +1,55 @@
export default {
'page.landing.title': 'The place to conduct your research',
'page.landing.introduction': 'Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry\'s standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.',
'page.landing.startButton': 'Get Started',
'page.studies.create.title': 'Add new study',
'page.studies.create.privacyStatement.title': 'Privacy statement',
'page.studies.create.privacyStatement.text': 'On the other hand, we denounce with righteous indignation and dislike men who are so beguiled and demoralized by the charms of pleasure of the moment, so blinded by desire, that they cannot foresee the pain and trouble that are bound to ensue; and equal blame belongs to those who fail in their duty through weakness of will, which is the same as saying through shrinking from toil and pain. These cases are perfectly simple and easy to distinguish. In a free hour, when our power of choice is untrammeled and when nothing prevents our being able to do what we like best, every pleasure is to be welcomed and every pain avoided. But in certain circumstances and owing to the claims of duty or the obligations of business it will frequently occur that pleasures have to be repudiated and annoyances accepted. The wise man therefore always holds in these matters to this principle of selection: he rejects pleasures to secure other greater pleasures, or else he endures pains to avoid worse pains.',
'page.studies.study.contributors.contributor.invite.title': 'Invite contributor',
'page.studies.study.contributors.contributor.edit.title': 'Edit contributor',
'form.label.studyName': 'Study name',
'form.studyShortDescription': 'Study short description',
'form.projectCode': 'Project code',
'form.usesHumanSubjectData': 'Uses human subject data',
'form.studyField': 'Study field',
'form.contributorName': 'Contributor name',
'form.contributorEmail': 'Contributor email',
'form.role': 'Role',
'form.search': 'Search',
'link.home': 'Home page',
'link.researchStudies': 'Research studies',
'link.researchStudies.study.overview': 'Overview',
'link.researchStudies.study.contributors': 'Contributors',
'link.researchStudies.study.apps': 'Apps',
'link.researchStudies.study.settings': 'Settings',
'study.name': 'Name',
'study.access': 'Access',
'study.access.restricted': 'restricted',
'study.access.public': 'public',
'study.field': 'Field',
'study.faculty': 'Faculty',
'study.university': 'University',
'study.createdAt': 'Created at',
'study.updatedAt': 'Updated at',
'study.startDate': 'Start Date',
'contributor.role': 'Role',
'contributor.role.owner': 'OWNER',
'ui.rug.form.action.save': 'Save',
'ui.rug.form.action.cancel': 'Cancel',
'researchStudies.title': 'Research Studies',
'researchStudies.deleteButton.button': 'delete study',
'researchStudies.deleteButton.modal.title': 'Remove research study',
'researchStudies.deleteButton.modal.text': 'Are you sure you want to remove this research study?',
'researchStudies.deleteButton.modal.action.negative': 'No',
'researchStudies.deleteButton.modal.action.positive': 'Yes',
'researchStudies.createButton.button': 'Create',
'contributors.title': 'Contributors',
'contributors.inviteButton.button': 'Invite contributor',
'contributors.editoFrom.title': 'Edit contributor',
'contributors.editForm.cancel': 'Cancel',
'contributors.editForm.save': 'Save',
'contributors.inviteButton.modal.title': 'Remove contributor',
'contributors.inviteButton.modal.text': 'Are you sure you want to remove this contributor?',
'contributors.inviteButton.modal.negative': 'No',
'contributors.inviteButton.modal.positive': 'Yes'
}

35
middleware/auth.js

@ -0,0 +1,35 @@
import { redirectToLogin } from '@/lib/redirect';
const routeOption = (route, key, value) => {
return route.matched.some((m) => {
if (process.client) {
return Object.values(m.components).some((component) => {
return component.options && component.options[key] === value;
})
} else {
return Object.values(m.components).some((component) => Object.values(component._Ctor).some((ctor) => {
return ctor.options && ctor.options[key] === value
}))
}
});
}
/**
* You can set the "auth" key in a page-component, at the same level as "layout" or "data" (see "pages/landing.vue")
* allowed values are: [false, "guest", <any>]
* if false or "guest" the authentication is disabled for that page
* if <any> or omitted, which defaults to undefined, the authorisation/isAuthenticated is checked
* and if the user is not logged in, the user is redirected to the login page
*/
export default ({ route, app, redirect, res }) => {
if (!route.matched.length) return;
const pageAuthDisabled = routeOption(route, 'auth', false);
if (pageAuthDisabled) return;
const pageIsInGuestMode = routeOption(route, 'auth', 'guest');
if (pageIsInGuestMode) return;
const isAuthenticated = app.store.getters['authorisation/isAuthenticated'];
if (!isAuthenticated) redirectToLogin(redirect, route.matched[0].path);
}

62
nuxt.config.js

@ -1,6 +1,4 @@
import colors from 'vuetify/es5/util/colors'
import prepareRoutes from './lib/routes';
import routeConfig from './config/routes';
export default {
// Global page headers: https://go.nuxtjs.dev/config-head
@ -21,7 +19,7 @@ export default {
]
},
fetchOnServer: false,
fetchOnServer: true,
ssr: true,
@ -31,9 +29,9 @@ export default {
router: {
middleware: ['auth'],
extendRoutes(parsedRoutes, resolveComponent) {
prepareRoutes(parsedRoutes, resolveComponent, routeConfig);
},
// extendRoutes(parsedRoutes, resolveComponent) {
// prepareRoutes(parsedRoutes, resolveComponent, routeConfig);
// },
},
// Plugins to run before rendering page: https://go.nuxtjs.dev/config-plugins
@ -46,26 +44,34 @@ export default {
// Modules for dev and build (recommended): https://go.nuxtjs.dev/config-modules
buildModules: [
// https://go.nuxtjs.dev/eslint
'@nuxtjs/eslint-module',
// https://go.nuxtjs.dev/vuetify
'@nuxtjs/vuetify',
],
// Modules: https://go.nuxtjs.dev/config-modules
modules: [
// https://go.nuxtjs.dev/axios
'@nuxtjs/axios',
'@nuxtjs/proxy',
'@nuxtjs/auth-next',
'@nuxtjs/i18n',
'@nuxtjs/sentry',
],
i18n: {
locales: [
{ code: 'en', iso: 'en-US', file: 'en.js', dir: 'ltr' },
// { code: 'nl', iso: 'nl-NL', file: 'nl.js', dir: 'ltr' },
{
code: 'en',
iso: 'en-US',
file: 'en.js',
dir: 'ltr',
name: 'English',
},
{
code: 'nl',
iso: 'nl-NL',
file: 'nl.js',
dir: 'ltr',
name: 'Nederlands',
},
],
defaultLocale: 'en',
langDir: '~/locales/',
@ -73,9 +79,9 @@ export default {
detectBrowserLanguage: {
alwaysRedirect: true,
fallbackLocale: 'en',
// redirectOn: 'root',
redirectOn: 'root',
useCookie: false,
cookieCrossOrigin: false,
cookieCrossOrigin: false,
cookieDomain: null,
cookieKey: 'i18n_redirected',
cookieSecure: false
@ -105,31 +111,13 @@ export default {
},
},
auth: {
cookie: false,
localStorage: false,
strategies: {
cookie: {
endpoints: {
login: { url: '/auth/login/', method: 'get' },
user: { url: '/api/v1/researchers/me/', method: 'get' },
}
},
}
},
// Axios module configuration: https://go.nuxtjs.dev/config-axios
axios: {
// baseURL: 'https://api-vre.web.rug.nl/',
// https: true,
// proxy: true,
baseURL: process.env.API_URL,
https: process.env.API_HTTPS
},
proxy: [
// 'https://api-vre.web.rug.nl/api',
],
proxy: process.env.PROXY_URLS.split(';'),
// Vuetify module configuration: https://go.nuxtjs.dev/config-vuetify
vuetify: {
@ -146,19 +134,17 @@ export default {
success: colors.green,
warning: colors.orange,
}
}
}
}
},
// Paul: https://c60ba2eccd2e4facbcb88c599b6f5149@o1022157.ingest.sentry.io/5988257
// Elwin: https://65fdb3af442f4217bed58536be25febe@o1025289.ingest.sentry.io/5991484
// sentry: {
// dsn: "https://65fdb3af442f4217bed58536be25febe@o1025289.ingest.sentry.io/5991484", // Enter your project's DSN here
// dsn: "", // Enter your project's DSN here
// // Additional Module Options go here
// // https://sentry.nuxtjs.org/sentry/options
// config: {
// // Add native Sentry config here
// // https://docs.sentry.io/platforms/javascript/guides/vue/configuration/options/
// // Set tracesSampleRate to 1.0 to capture 100%
// // of transactions for performance monitoring.
// // We recommend adjusting this value in production

1
package.json

@ -18,7 +18,6 @@
"deploy:onserver": "source ./scripts/deploy-on-server.sh"
},
"dependencies": {
"@nuxtjs/auth-next": "5.0.0-1624817847.21691f1",
"@nuxtjs/axios": "^5.13.6",
"@nuxtjs/i18n": "^7.0.3",
"@nuxtjs/sentry": "^5.1.4",

2
pages/landing.vue → pages/index.vue

@ -17,7 +17,7 @@
</v-card-text>
<v-card-actions>
<v-btn :to="{ name: 'researchStudies'}">
<v-btn :to="localePath('/researchStudies')">
{{ $t('page.landing.startButton') }}
</v-btn>
</v-card-actions>

33
pages/login.vue

@ -1,33 +0,0 @@
<template>
<div></div>
</template>
<script>
export default {
layout: 'landing',
mounted() {
this.$axios
.get('/api/v1/researchers/me/')
.then((response) => {
this.$auth.setUser(response.data)
})
.catch(() => {
this.$auth.loginWith('cookie').catch(() => {
window.location.href = `https://api-vre.web.rug.nl/auth/login/?next=${location.origin}${this.$auth.$state.redirect}`
})
})
// this.$auth.login({
// data: {
// "username": "p.scheltema@rug.nl",
// "password": "Tralala1!"
// }
// })
// .then(() => {
// this.$auth.fetchUser().then((response) => {
// this.$auth.setUser(response.data);
// });
// });
},
}
</script>

0
pages/profile/overview.vue → pages/profile/index.vue

2
pages/studies/study/apps.vue → pages/researchStudies/_studyId/apps/index.vue

@ -1,6 +1,6 @@
<template>
<div>
<apps-list :apps="apps" :studyId="studyId" />
<apps-list :apps="apps" :study-id="studyId" />
</div>
</template>

7
pages/studies/study/apps/app/windows-vdi.vue → pages/researchStudies/_studyId/apps/windows-vdi/_appId/index.vue

@ -126,12 +126,7 @@ export default {
required: ['profileId', 'code', 'field', ],
},
onResponse(response) {
this.vm.$router.push({
name: 'researchStudies.study.overview',
params: {
studyId: response.data.id,
}
})
this.vm.$router.push(this.localePath(`/researchStudies/${response.data.id}/`))
}
}),
search: '',

17
pages/studies/study/apps/app/windows-vdi/create.vue → pages/researchStudies/_studyId/apps/windows-vdi/create.vue

@ -51,7 +51,7 @@
item-text="name"
item-value="id"
>
<template v-slot:selection="data">
<template #selection="data">
<v-chip
v-bind="data.attrs"
:input-value="data.selected"
@ -80,7 +80,7 @@
item-value="name"
multiple
>
<template v-slot:selection="data">
<template #selection="data">
<v-chip
v-bind="data.attrs"
:input-value="data.selected"
@ -94,7 +94,7 @@
{{ data.item.name }}
</v-chip>
</template>
<template v-slot:item="data">
<template #item="data">
<template v-if="typeof data.item !== 'object'">
<v-list-item-content v-text="data.item"></v-list-item-content>
</template>
@ -103,8 +103,8 @@
<img :src="data.item.avatar">
</v-list-item-avatar>
<v-list-item-content>
<v-list-item-title v-html="data.item.name"></v-list-item-title>
<v-list-item-subtitle v-html="data.item.group"></v-list-item-subtitle>
<v-list-item-title>{{data.item.name}}></v-list-item-title>
<v-list-item-subtitle>{{data.item.group}}</v-list-item-subtitle>
</v-list-item-content>
</template>
</template>
@ -165,12 +165,7 @@ export default {
required: ['profileId', 'studyId', 'contributorId', ],
},
onResponse() {
this.vm.$router.push({
name: 'researchStudies.study.apps.windows-vdi',
params: {
studyId: $route.params.studyId,
}
})
this.vm.$router.push(this.localePath(`/researchStudies/${$route.params.studyId}/apps/windows-vdi/`))
}
}),
search: '',

10
pages/studies/study/contributors/contributor/edit.vue → pages/researchStudies/_studyId/contributors/_contributorId/edit.vue

@ -6,7 +6,7 @@
</ui-rug-card-title>
<v-card-text class="flex-row mb-6">
<contributor-list-item :contributor="contributor" />
<contributor-list-item :contributor="contributor" :study-id="studyId" />
<v-select
id="formData.role"
@ -37,6 +37,7 @@ export default {
]);
const contributor = store.getters['studies/getContributorFromActiveStudy']({ contributorId })
return {
studyId,
contributor,
roles: store.getters['studies/getRoles'],
formData: {
@ -63,12 +64,7 @@ export default {
},
onResponse() {
const studyId = this.vm.$route.params.studyId;
this.vm.$router.push({
name: 'researchStudies.study.contributors',
params: {
studyId,
}
})
this.vm.$router.push(this.localePath(`/researchStudies/${studyId}/contributors`))
}
}),
}

0
pages/studies/study/contributors.vue → pages/researchStudies/_studyId/contributors/index.vue

7
pages/studies/study/contributors/contributor/invite.vue → pages/researchStudies/_studyId/contributors/invite.vue

@ -86,12 +86,7 @@ export default {
},
onResponse() {
const studyId = this.vm.$route.params.studyId;
this.vm.$router.push({
name: 'researchStudies.study.contributors',
params: {
studyId,
}
})
this.vm.$router.push(this.localePath(`/researchStudies/${studyId}/contributors`))
}
}),
}

0
pages/studies/study/overview.vue → pages/researchStudies/_studyId/index.vue

0
pages/studies/study/settings.vue → pages/researchStudies/_studyId/settings.vue

7
pages/studies/create.vue → pages/researchStudies/create.vue

@ -105,12 +105,7 @@ export default {
required: ['name', 'code', 'field', ],
},
onResponse(response) {
this.vm.$router.push({
name: 'researchStudies.study.overview',
params: {
studyId: response.data.id,
}
})
this.vm.$router.push(this.localePath(`/researchStudies/${response.data.id}/`))
}
}),
}

0
pages/studies/list.vue → pages/researchStudies/index.vue

16
plugins/axios.js

@ -1,5 +1,7 @@
import { redirectToLogin } from '@/lib/redirect';
export default function ({ $axios, i18n }) {
export default function (args) {
const { $axios, i18n, req, redirect } = args;
$axios.interceptors.request.use(config => {
const locales = i18n.locales;
@ -13,4 +15,16 @@ export default function ({ $axios, i18n }) {
return config;
});
$axios.interceptors.response.use(
response => response,
error => {
if (error.response.status === 401) {
const url = process.server ? req.url : location.pathname
redirectToLogin(redirect, url);
} else {
return Promise.reject(error);
}
}
);
}

52
store/authorisation.js

@ -10,39 +10,51 @@
const ADMIN_ROLE = 'ADMIN';
const CONTRIBUTOR_ROLE = 'CONTRIBUTOR';
// TODO get user with researcher id
const getUser = (rootState) => {
return {
...rootState.auth.user,
researcher:{id:7}
}
}
export const state = () => ({
user: undefined,
isAuthenticated: false,
})
export const getters = {
getUser(state) {
return state.user;
},
isAuthenticated(state) {
return state.isAuthenticated
},
isAll() {
return true;
},
isGuest(state, commit, rootState) {
return !getters.isUser(state, commit, rootState)
isGuest(state) {
return !getters.isAuthenticated(state)
},
isUser(state, commit, rootState) {
return Boolean(getUser(rootState))
isUser(state) {
return getters.isAuthenticated(state)
},
isOwnerOfStudy: (state, commit, rootState) => ({study}) => {
return getters.isUser(state, commit, rootState) && study.owner.id === getUser(rootState).researcher.id
isOwnerOfStudy: (state) => ({study}) => {
return getters.isUser(state) && study.owner.id === getters.getUser(state).researcher.id
},
isContributorOfStudy: (state, commit, rootState) => ({study}) => {
return getters.hasRoleInStudy(state, commit, rootState)({study, role: CONTRIBUTOR_ROLE});
isContributorOfStudy: (state) => ({study}) => {
return getters.hasRoleInStudy(state)({study, role: CONTRIBUTOR_ROLE});
},
isAdminOfStudy: (state, commit, rootState) => ({study}) => {
return getters.hasRoleInStudy(state, commit, rootState)({study, role: ADMIN_ROLE});
isAdminOfStudy: (state) => ({study}) => {
return getters.hasRoleInStudy(state)({study, role: ADMIN_ROLE});
},
hasRoleInStudy: (state, commit, rootState) => ({study, role}) => {
if (!getters.isUser(state, commit, rootState)) return false;
const researcherId = getUser(rootState).researcher.id;
hasRoleInStudy: (state) => ({study, role}) => {
if (!getters.isUser(state)) return false;
const researcherId = getters.getUser(state).researcher.id;
return study.contributors.filter(contributor => {
return contributor.role === role
&& contributor.researcher.id === researcherId
}).length
},
};
export const mutations = {
setUser(state, payload) {
state.user = payload
},
setAuthenticated(state, payload) {
state.isAuthenticated = payload
},
}

16
store/index.js

@ -0,0 +1,16 @@
export const actions = {
async nuxtServerInit({commit}, {app, req, res}) {
await this.$axios
// this is to prevent the interceptor from redirecting us to the login page
.create()
.get('/api/v1/researchers/me/')
.then((response) => {
commit('authorisation/setUser', response.data);
commit('authorisation/setAuthenticated', true);
})
.catch((e) => {
commit('authorisation/setUser', null);
commit('authorisation/setAuthenticated', false);
});
},
}

101
yarn.lock

@ -1372,22 +1372,7 @@
webpack-node-externals "^3.0.0"
webpackbar "^4.0.0"
"@nuxtjs/auth-next@5.0.0-1624817847.21691f1":
version "5.0.0-1624817847.21691f1"
resolved "https://registry.yarnpkg.com/@nuxtjs/auth-next/-/auth-next-5.0.0-1624817847.21691f1.tgz#28b92625ac5817d8083f8b42dd184a88caefbe96"
integrity sha512-PsHhLtzglMnwM2o16mgM7zQ3KwTI7AIg2ja3IFYujbs9s1w8HvnRD3byMRj1WZo0vXEBRh3u3nHxKEBIbFe1Xg==
dependencies:
"@nuxtjs/axios" "^5.13.0"
axios "^0.21.1"
body-parser "^1.19.0"
consola "^2.15.3"
cookie "^0.4.1"
defu "^3.2.2"
hasha "^5.2.2"
jwt-decode "^3.1.2"
requrl "^3.0.2"
"@nuxtjs/axios@^5.13.0", "@nuxtjs/axios@^5.13.6":
"@nuxtjs/axios@^5.13.6":
version "5.13.6"
resolved "https://registry.yarnpkg.com/@nuxtjs/axios/-/axios-5.13.6.tgz#6f4bbd98a3a7799a5d2c0726c6ad2a98aa111881"
integrity sha512-XS+pOE0xsDODs1zAIbo95A0LKlilvJi8YW0NoXYuq3/jjxGgWDxizZ6Yx0AIIjZOoGsXJOPc0/BcnSEUQ2mFBA==
@ -2581,22 +2566,6 @@ bn.js@^5.0.0, bn.js@^5.1.1:
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.0.tgz#358860674396c6997771a9d051fcc1b57d4ae002"
integrity sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw==
body-parser@^1.19.0:
version "1.19.0"
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a"
integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==
dependencies:
bytes "3.1.0"
content-type "~1.0.4"
debug "2.6.9"
depd "~1.1.2"
http-errors "1.7.2"
iconv-lite "0.4.24"
on-finished "~2.3.0"
qs "6.7.0"
raw-body "2.4.0"
type-is "~1.6.17"
boolbase@^1.0.0, boolbase@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
@ -2771,11 +2740,6 @@ bytes@3.0.0:
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048"
integrity sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=
bytes@3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6"
integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==
cacache@^12.0.2:
version "12.0.4"
resolved "https://registry.yarnpkg.com/cacache/-/cacache-12.0.4.tgz#668bcbd105aeb5f1d92fe25570ec9525c8faa40c"
@ -3392,11 +3356,6 @@ constants-browserify@^1.0.0:
resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75"
integrity sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=
content-type@~1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b"
integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==
convert-source-map@^1.3.0, convert-source-map@^1.7.0:
version "1.8.0"
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369"
@ -3933,11 +3892,6 @@ defu@^2.0.4:
resolved "https://registry.yarnpkg.com/defu/-/defu-2.0.4.tgz#09659a6e87a8fd7178be13bd43e9357ebf6d1c46"
integrity sha512-G9pEH1UUMxShy6syWk01VQSRVs3CDWtlxtZu7A+NyqjxaCA4gSlWAKDBx6QiUEKezqS8+DUlXLI14Fp05Hmpwg==
defu@^3.2.2:
version "3.2.2"
resolved "https://registry.yarnpkg.com/defu/-/defu-3.2.2.tgz#be20f4cc49b9805d54ee6b610658d53894942e97"
integrity sha512-8UWj5lNv7HD+kB0e9w77Z7TdQlbUYDVWqITLHNqFIn6khrNHv5WQo38Dcm1f6HeNyZf0U7UbPf6WeZDSdCzGDQ==
defu@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/defu/-/defu-4.0.1.tgz#9d7d7a48f9295f08285d153dcff174c89b9bcb22"
@ -5390,7 +5344,7 @@ hash.js@^1.0.0, hash.js@^1.0.3:
inherits "^2.0.3"
minimalistic-assert "^1.0.1"
hasha@^5.0.0, hasha@^5.2.2:
hasha@^5.0.0:
version "5.2.2"
resolved "https://registry.yarnpkg.com/hasha/-/hasha-5.2.2.tgz#a48477989b3b327aea3c04f53096d816d97522a1"
integrity sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==
@ -5505,17 +5459,6 @@ http-cache-semantics@^4.0.0:
resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390"
integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==
http-errors@1.7.2:
version "1.7.2"
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f"
integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==
dependencies:
depd "~1.1.2"
inherits "2.0.3"
setprototypeof "1.1.1"
statuses ">= 1.5.0 < 2"
toidentifier "1.0.0"
http-errors@~1.7.2:
version "1.7.3"
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06"
@ -6336,11 +6279,6 @@ jsonfile@^6.0.1:
optionalDependencies:
graceful-fs "^4.1.6"
jwt-decode@^3.1.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/jwt-decode/-/jwt-decode-3.1.2.tgz#3fb319f3675a2df0c2895c8f5e9fa4b67b04ed59"
integrity sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A==
keyv@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9"
@ -6742,11 +6680,6 @@ mdn-data@2.0.4:
resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.4.tgz#699b3c38ac6f1d728091a64650b65d388502fd5b"
integrity sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==
media-typer@0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=
mem@^8.0.0, mem@^8.1.1:
version "8.1.1"
resolved "https://registry.yarnpkg.com/mem/-/mem-8.1.1.tgz#cf118b357c65ab7b7e0817bdf00c8062297c0122"
@ -8691,11 +8624,6 @@ q@^1.1.2:
resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7"
integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=
qs@6.7.0:
version "6.7.0"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc"
integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==
qs@^6.9.4:
version "6.10.1"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.1.tgz#4931482fa8d647a5aab799c5271d2133b981fb6a"
@ -8761,16 +8689,6 @@ range-parser@^1.2.1, range-parser@~1.2.1:
resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031"
integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==
raw-body@2.4.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332"
integrity sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==
dependencies:
bytes "3.1.0"
http-errors "1.7.2"
iconv-lite "0.4.24"
unpipe "1.0.0"
rc9@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/rc9/-/rc9-1.2.0.tgz#ef098181fdde714efc4c426383d6e46c14b1254a"
@ -9042,11 +8960,6 @@ requires-port@^1.0.0:
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=
requrl@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/requrl/-/requrl-3.0.2.tgz#d376104193b02a2d874dde68454c2db2dfeb0fac"
integrity sha512-f3gjR6d8MhOpn46PP+DSJywbmxi95fxQm3coXBFwognjFLla9X6tr8BdNyaIKNOEkaRbRcm0/zYAqN19N1oyhg==
reselect@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.0.0.tgz#f2529830e5d3d0e021408b246a206ef4ea4437f7"
@ -10236,14 +10149,6 @@ type-fest@^0.8.0, type-fest@^0.8.1:
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d"
integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==
type-is@~1.6.17:
version "1.6.18"
resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131"
integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==
dependencies:
media-typer "0.3.0"
mime-types "~2.1.24"
typedarray-to-buffer@^3.1.5:
version "3.1.5"
resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080"
@ -10360,7 +10265,7 @@ universalify@^2.0.0:
resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717"
integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==
unpipe@1.0.0, unpipe@~1.0.0:
unpipe@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=

Loading…
Cancel
Save