Browse Source

A lot of small tweaks....

master
Joshua Rubingh 6 months ago
parent
commit
42817b7033
  1. 1
      .gitignore
  2. BIN
      assets/avatar.png
  3. 46
      components/app/list/item.vue
  4. 7
      components/contributor/list/item.vue
  5. 4
      components/contributors/list.vue
  6. 2
      components/user/profile/list/item.vue
  7. 1
      locales/en.js
  8. 6
      pages/profile/edit.vue
  9. 62
      pages/researchStudies/_studyId/apps/index.vue
  10. 172
      pages/researchStudies/_studyId/apps/windows-vdi/_appId/index.vue
  11. 0
      pages/researchStudies/_studyId/apps/windows10-vdi/create.vue
  12. 153
      pages/researchStudies/_studyId/apps/windows10-vdi/index.vue
  13. 4
      pages/researchStudies/_studyId/index.vue
  14. 121
      store/apps.js
  15. 4
      store/researchers.js

1
.gitignore vendored

@ -88,3 +88,4 @@ sw.* @@ -88,3 +88,4 @@ sw.*
# Vim swap files
*.swp
.vscode/launch.json

BIN
assets/avatar.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

46
components/app/list/item.vue

@ -1,12 +1,27 @@ @@ -1,12 +1,27 @@
<template>
<v-card link :to="localePath(`/researchStudies/${studyId}/apps/${app.component.slug}/${app.id}`)">
<v-img
:src="app.avatar"
height="50"
width="50"
/>
<v-card-title>{{app.name}}</v-card-title>
<v-card-subtitle v-text="app.description"></v-card-subtitle>
<!-- This is STUPID!!! Why do I have to make 2 complete HTML blocks with the same code, just to DISABLE a a-href..... -->
<v-card
v-if="app.available"
link
:to="localePath(`/researchStudies/${studyId}/apps/${app.slug}/`)"
>
<v-avatar>
<v-img :src="app.src" :alt="`${app.name}`" height="100" width="100" />
</v-avatar>
<div style="padding-left: 5rem">
<v-card-title>{{ app.name }}</v-card-title>
<v-card-subtitle v-text="app.description"></v-card-subtitle>
</div>
</v-card>
<v-card v-else>
<v-avatar>
<v-img :src="app.src" :alt="`${app.name}`" height="100" width="100" />
</v-avatar>
<div style="padding-left: 5rem">
<v-card-title>{{ app.name }}</v-card-title>
<v-card-subtitle v-text="app.description"></v-card-subtitle>
</div>
</v-card>
</template>
@ -15,14 +30,21 @@ export default { @@ -15,14 +30,21 @@ export default {
props: {
studyId: {
type: [String, Number],
default: 0
default: 0,
},
app: {
type: Object,
default() {
return {}
}
}
}
},
},
},
}
</script>
<style scoped>
.v-avatar {
float: left;
margin: 1rem;
}
</style>

7
components/contributor/list/item.vue

@ -10,12 +10,7 @@ @@ -10,12 +10,7 @@
>
<v-list-item-avatar>
<v-img
:src="
contributor.researcher.avatar ||
`https://randomuser.me/api/portraits/men/${
contributor.researcher.id + 6
}.jpg`
"
:src="contributor.researcher.avatar || require('~/assets/avatar.png')"
></v-img>
</v-list-item-avatar>
<v-list-item-content>

4
components/contributors/list.vue

@ -23,9 +23,7 @@ @@ -23,9 +23,7 @@
<template #item.image="{ item }">
<v-avatar>
<v-img
:src="`https://randomuser.me/api/portraits/men/${
item.researcher.id + 6
}.jpg`"
:src="item.researcher.avatar || require('~/assets/avatar.png')"
:alt="`${item.researcher.display_name}`"
/>
</v-avatar>

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

@ -1,7 +1,7 @@ @@ -1,7 +1,7 @@
<template>
<v-list-item class="px-2" @click="editProfile">
<v-list-item-avatar>
<v-img :src="getUser.avatar"></v-img>
<v-img :src="getUser.avatar || require('~/assets/avatar.png')"></v-img>
</v-list-item-avatar>
<v-list-item-content>
<v-list-item-title class="text-h6">

1
locales/en.js

@ -14,6 +14,7 @@ export default { @@ -14,6 +14,7 @@ export default {
'page.researchStudies.studyId.contributors.index.invite': 'Invite a researcher to this study',
'page.researchStudies.studyId.settings.title': 'Study settings',
'page.researchStudies.create.title': 'Create a new study',
'page.researchStudies.studyId.apps.title': 'Apps for study {study_name}',
'page.profile.edit.title': 'Edit me',
'page.profile.edit.header': 'Edit me',
'form.label.firstName': 'First name',

6
pages/profile/edit.vue

@ -59,11 +59,11 @@ @@ -59,11 +59,11 @@
:label="$t('form.label.profileAvatar')"
></v-text-field>
<v-avatar v-if="hasImage" size="100">
<v-avatar size="300">
<img
class="img-preview"
:alt="formData.display_name"
:src="formData.avatar"
:src="formData.avatar || require('~/assets/avatar.png')"
/>
</v-avatar>
@ -123,7 +123,7 @@ export default { @@ -123,7 +123,7 @@ export default {
data() {
const profile = this.$store.getters['researchers/getMe']
return {
hasImage: profile.avatar !== '',
hasImage: profile.avatar !== null && profile.avatar !== '',
formData: {
display_name: profile.display_name,
first_name: profile.first_name,

62
pages/researchStudies/_studyId/apps/index.vue

@ -1,11 +1,44 @@ @@ -1,11 +1,44 @@
<template>
<div>
<apps-list :apps="apps" :study-id="studyId" />
</div>
<v-card>
<ui-rug-card-title> {{ study.name }} ({{ study.code }}) </ui-rug-card-title>
<v-card-text class="d-flex flex-row mb-6">
<apps-list :apps="apps" :study-id="studyId" />
</v-card-text>
</v-card>
</template>
<script>
/*
export default {
async asyncData({ store, route }) {
const studyId = route.params.studyId
await Promise.all([
store.dispatch('studies/getStudy', { studyId }),
store.dispatch('studies/getStudyFields'),
store.dispatch('apps/getApps'),
])
return {
study: store.getters['studies/getActiveStudy'],
studyFields: store.getters['studies/getStudyFields'],
apps: store.getters['apps/getAppsForStudy'](studyId),
studyId,
}
},
head() {
const $t = this.$t.bind(this)
return {
title: $t('page.researchStudies.studyId.index.title', {
study_name: this.study.name,
}),
}
},
}
*/
export default {
/*
async asyncData({ store, route }) {
const studyId = route.params.studyId;
await store.dispatch('apps/getApps');
@ -19,4 +52,27 @@ export default { @@ -19,4 +52,27 @@ export default {
// };
},
}
*/
async asyncData({ store, route }) {
const studyId = route.params.studyId
await Promise.all([
store.dispatch('studies/getStudy', { studyId }),
store.dispatch('apps/getAvailableAppTypes', { studyId }),
])
return {
study: store.getters['studies/getActiveStudy'],
apps: store.getters['apps/getAvailableAppTypes'],
studyId,
}
},
head() {
const $t = this.$t.bind(this)
return {
title: $t('page.researchStudies.studyId.apps.title', {
study_name: this.study.name,
}),
}
},
}
</script>

172
pages/researchStudies/_studyId/apps/windows-vdi/_appId/index.vue

@ -1,172 +0,0 @@ @@ -1,172 +0,0 @@
<template>
<v-card>
<v-card-title>
Virtual machines
<v-spacer></v-spacer>
<!-- <v-data-table
multi-sort
hide-default-footer
:headers="headers"
:items="profiles"
:search="search"
:custom-filter="customFilter"
@click:row="navigateToRow"
/> -->
<!-- <windows-vdi-edit-button :studyId="studyId" :virtualMachineId="item.id" />
<windows-vdi-delete-button :virtualMachineId="item.id" /> -->
data-table: current vm's (name, os, ram, storage, researcher)
edit button
add vm page in edit modus
delete button
delete action => re-render
button: add vm
page:
pick-profile-input:
data-table: vm profiles choice
returns profile-id
dropdown: researcher
button: create workspace
<v-text-field
v-model="search"
append-icon="mdi-magnify"
label="Search"
single-line
hide-details
/>
</v-card-title>
<v-radio-group v-model="formData.profileId">
<v-data-table
multi-sort
hide-default-footer
:headers="headers"
:items="profiles"
:search="search"
:custom-filter="customFilter"
@click:row="navigateToRow"
>
<template #item.name="{ item }">
{{item.name}}
</template>
<template #item.os="{ item }">
{{item.os.name}}
</template>
<template #item.ram="{ item }">
{{item.memory_amount}} x {{item.memory_type.name}}
</template>
<template #item.storage="{ item }">
{{item.storage_amount}} x {{item.storage_type.name}}
</template>
<template #item.actions="{ item }">
<v-radio
:key="item.id"
:value="item.id"
/>
</template>
</v-data-table>
</v-radio-group>
</v-card>
</template>
<script>
import { mapActions } from 'vuex';
import Form from '@/lib/form';
export default {
async asyncData({ store, route }) {
const studyId = route.params.studyId;
await store.dispatch('virtualmachines/getProfiles');
return {
studyId,
profiles: store.getters['virtualmachines/getProfiles'],
}
},
data ({ route }) {
return {
formData: {
name: '',
studyId: null, // route.params.studyId,
profileId: null,
// TODO bespreken, is allemaal nu deel van het profile ik denk dat dit weg kan
provider: null, // [vrw, openstack]
operating_systemId: null,
base_memory_type: null,
base_storage_type: null,
},
form: new Form({
vm: this,
action: this['studies/createStudy'],
schema: {
type: 'object',
properties: {
profileId: {
type: 'integer',
},
description: {
type: 'string',
maxLength: 2048,
},
code: {
type: 'string',
maxLength: 50,
},
human_subject: {
type: 'boolean',
},
field: {
type: 'integer'
}
},
required: ['profileId', 'code', 'field', ],
},
onResponse(response) {
this.vm.$router.push(this.localePath(`/researchStudies/${response.data.id}/`))
}
}),
search: '',
headers: [
{
text: 'Name',
value: 'name',
},
{
text: 'Operating System',
value: 'os',
},
{
text: 'RAM',
value: 'ram'
},
{
text: 'Storage',
value: 'storage'
},
{
text: 'Kies configuratie',
value: 'actions'
}
],
}
},
methods: {
navigateToRow() {},
customFilter(_, search, item) {
search = search.toString().toLowerCase();
return (
item.name.toLowerCase().includes(search)
|| item.name.toLowerCase().includes(search)
|| item.os.name.toLowerCase().includes(search)
|| item.memory_amount.toString().includes(search)
|| item.storage_amount.toString().includes(search)
)
},
...mapActions(['studies/createStudy']),
}
}
</script>

0
pages/researchStudies/_studyId/apps/windows-vdi/create.vue → pages/researchStudies/_studyId/apps/windows10-vdi/create.vue

153
pages/researchStudies/_studyId/apps/windows10-vdi/index.vue

@ -0,0 +1,153 @@ @@ -0,0 +1,153 @@
<template>
<v-card>
<ui-rug-card-title>
{{ study.name }} ({{ study.code }})
<v-spacer></v-spacer>
<v-text-field
v-model="search"
append-icon="mdi-magnify"
:label="$t('form.search')"
single-line
hide-details
></v-text-field>
</ui-rug-card-title>
<v-radio-group v-model="formData.profileId">
<v-data-table
multi-sort
hide-default-footer
:headers="headers"
:items="profiles"
:search="search"
:custom-filter="customFilter"
@click:row="navigateToRow"
>
<template #item.title="{ item }">
{{ item.title }}
</template>
<template #item.os="{ item }">
{{ item.os }}
</template>
<template #item.memory="{ item }">
{{ item.title }}
</template>
<template #item.disk="{ item }">
{{ item.disk }}
</template>
<template #item.actions="{ item }">
<v-radio :key="item.id" :value="item.id" />
</template>
</v-data-table>
</v-radio-group>
</v-card>
</template>
<script>
/* import { mapActions } from 'vuex' */
import Form from '@/lib/form'
export default {
async asyncData({ store, route }) {
const studyId = route.params.studyId
await Promise.all([
store.dispatch('studies/getStudy', { studyId }),
store.dispatch({
type: 'apps/getAppTypeProfiles',
studyId: studyId.toString(),
appType: 'windows10-vdi',
}),
])
return {
studyId,
study: store.getters['studies/getActiveStudy'],
profiles: store.getters['apps/getAppTypeProfiles'],
}
},
data({ route }) {
return {
formData: {
title: '',
studyId: null, // route.params.studyId,
profileId: null,
// TODO bespreken, is allemaal nu deel van het profile ik denk dat dit weg kan
provider: null, // [vrw, openstack]
operating_systemId: null,
base_memory_type: null,
base_storage_type: null,
},
form: new Form({
vm: this,
action: this['studies/createStudy'],
schema: {
type: 'object',
properties: {
profileId: {
type: 'integer',
},
description: {
type: 'string',
maxLength: 2048,
},
code: {
type: 'string',
maxLength: 50,
},
human_subject: {
type: 'boolean',
},
field: {
type: 'integer',
},
},
required: ['profileId', 'code', 'field'],
},
onResponse(response) {
this.vm.$router.push(
this.localePath(`/researchStudies/${response.data.id}/`)
)
},
}),
search: '',
headers: [
{
text: 'Name',
value: 'name',
},
{
text: 'Operating System',
value: 'os',
},
{
text: 'RAM',
value: 'ram',
},
{
text: 'Storage',
value: 'storage',
},
{
text: 'Kies configuratie',
value: 'actions',
},
],
}
},
methods: {
navigateToRow() {},
customFilter(_, search, item) {
search = search.toString().toLowerCase()
return (
item.title.toLowerCase().includes(search) ||
item.title.toLowerCase().includes(search) ||
item.os.toLowerCase().includes(search) ||
item.memory.toString().includes(search) ||
item.disk.toString().includes(search)
)
},
/*
...mapActions(['studies/createStudy']),
*/
},
}
</script>

4
pages/researchStudies/_studyId/index.vue

@ -61,12 +61,12 @@ export default { @@ -61,12 +61,12 @@ export default {
await Promise.all([
store.dispatch('studies/getStudy', { studyId }),
store.dispatch('studies/getStudyFields'),
store.dispatch('apps/getApps'),
store.dispatch('apps/getAvailableAppTypes', { studyId }),
])
return {
study: store.getters['studies/getActiveStudy'],
studyFields: store.getters['studies/getStudyFields'],
apps: store.getters['apps/getAppsForStudy'](studyId),
apps: store.getters['apps/getAvailableAppTypes'],
studyId,
}
},

121
store/apps.js

@ -1,12 +1,123 @@ @@ -1,12 +1,123 @@
import { actionCreator } from '@/lib/store';
export const state = () => ({
apps: [],
/*
appTypeMap: {
WINDOWS_10_WORKSPACE: {
component: 'windows-10-workspace',
slug: 'windows-vdi',
},
},
*/
})
export const actions = {
getAvailableAppTypes: actionCreator({
name: 'getAvailableAppTypes',
method({ studyId }) {
return this.$axios.get(`/api/v1/studies/${studyId}/app-types/`)
},
}),
getStudyApps: actionCreator({
name: 'getStudyApps',
method({ studyId, appType }) {
return this.$axios.get(`/api/v1/studies/${studyId}/app-types/${appType}/`)
},
}),
getAppTypeProfiles: actionCreator({
name: 'getAppTypeProfiles',
method(data) {
return this.$axios.get(`/api/v1/studies/${data.studyId}/app-types/${data.appType}/profiles/`)
}
}),
/*
getAppsTest: actionCreator({
name: 'getAppsTest',
method() {
return Promise.resolve({
data: [
{
id: 1,
name: 'Windows 10 workspace',
description: 'Windows VDI',
avatar: 'https://banner2.cleanpng.com/20180328/skq/kisspng-logo-windows-8-windows-7-microsoft-8-5abc1c77a59fe7.0872489215222774956784.jpg',
type: 'WINDOWS_10_WORKSPACE',
}
]
})
}
}),
*/
}
export const mutations = {
getAvailableAppTypesSuccess(state, { result }) {
state.apps = result.map((app) => {
return {
id: app.id,
src: app.avatar,
name: app.title,
description: app.description,
types: app.has_apps,
available: app.has_apps > 0,
slug: app.slug
}
})
},
getStudyAppsSuccess(state, { result }) {
state.apps = result.map((app) => {
return {
id: app.id,
src: app.avatar,
name: app.display_name,
login: app.login_url
}
})
},
getAppTypeProfilesSuccess(state, { result }) {
state.app_profiles = result.map((profile) => {
return {
id: profile.id,
name: profile.title,
os: profile.os,
disk: profile.disk,
memory: profile.memory
}
})
}
}
export const getters = {
getAvailableAppTypes(state) {
return state.apps;
},
getAppsForStudy: state => studyId => {
// studyId = parseInt(studyId, 10);
// return state.apps.filter(app => app.study.id === studyId)
return []
},
getAppTypeProfiles(state) {
console.log(state.app_profiles)
return state.app_profiles;
},
}
/*
export const state = () => ({
appTypeMap: {
WINDOWS_10_WORKSPACE: {
component: 'windows-10-workspace',
slug: 'windows-vdi',
},
},
apps: [],
})
@ -44,9 +155,7 @@ export const getters = { @@ -44,9 +155,7 @@ export const getters = {
},
}
export const mappers = {
addComponentToApp: (state) => (app) => {
app.component = state.appTypeMap[app.type];
return app;
}
}
export const mappers = {Profiles
}
}
*/

4
store/researchers.js

@ -25,10 +25,6 @@ export const actions = { @@ -25,10 +25,6 @@ export const actions = {
export const mutations = {
getMeSuccess(state, { result }) {
state.profile = result;
/* TODO: How to add the API url for full image url ?? */
if (this.$axios.defaults.baseURL) {
state.profile.avatar = this.$axios.defaults.baseURL.replace(/\/$/g, '') + state.profile.avatar;
}
},
updateMeSuccess(state, { result }) {

Loading…
Cancel
Save