Some checks failed
continuous-integration/drone/push Build is failing
- Complete GGZ Ecademy Nuxt.js user portal - Learning products browser and management - Member management interface - User authentication and roles - Multi-language support (NL/EN) - Vuex store for state management - Component-based architecture
484 lines
13 KiB
Vue
484 lines
13 KiB
Vue
<template>
|
|
<v-row>
|
|
<v-col>
|
|
<div class="d-flex justify-space-between">
|
|
<h2 class="ma-4" v-if="isNew">Nieuw</h2>
|
|
<span v-else>
|
|
<h2 class="ma-4">{{ local.informal_name || '' }}</h2>
|
|
<h3 class="ma-4">{{ local.formal_name || '' }}</h3>
|
|
</span>
|
|
<span class="justify-center d-flex flex-column" v-if="local.revision">
|
|
<small>
|
|
<v-icon color="success" class="mr-2">icon-checkmark</v-icon>
|
|
bijgewerkt op
|
|
<strong>{{ formatDate(local.revision.updated_at) }}</strong> door
|
|
<strong>{{ local.revision.user.fullName }}</strong>
|
|
</small>
|
|
|
|
<small v-if="local.revision.accepted_at">
|
|
<v-icon color="success">icon-checkmark</v-icon>
|
|
<v-icon color="success" class="mr-2">icon-checkmark</v-icon>
|
|
gecontroleerd op
|
|
<strong>{{ formatDate(local.revision.accepted_at) }}</strong> door
|
|
<strong>{{ local.revision.revisor.fullName }}</strong>
|
|
</small>
|
|
</span>
|
|
</div>
|
|
|
|
<div class="d-flex justify-space-between">
|
|
<div class="d-flex">
|
|
<small
|
|
v-for="(tab, index) in tabs"
|
|
:key="index"
|
|
@click="selectedTab = tab"
|
|
class="ma-4 dot"
|
|
:class="{ 'accent--text activeTab': selectedTab === tab }"
|
|
>{{ $t(`members.tabs.${tab}`) }}</small
|
|
>
|
|
</div>
|
|
<!-- here checks -->
|
|
</div>
|
|
|
|
<basic-members
|
|
v-if="isTabSelected('basic')"
|
|
:editMode="canEditSuperAdminsAndAdmins"
|
|
:isCreateMode="isNew"
|
|
:users="users"
|
|
/>
|
|
|
|
<address-members
|
|
v-if="isTabSelected('address')"
|
|
:editMode="canEditSuperAdminsAndAdmins"
|
|
:isCreateMode="isNew"
|
|
/>
|
|
<contacts-members
|
|
v-if="isTabSelected('contacts')"
|
|
:editMode="canEditSuperAdminsAdminsAndDelegated"
|
|
:isCreateMode="isNew"
|
|
/>
|
|
|
|
<contribution-members
|
|
v-if="isTabSelected('contribution')"
|
|
:editMode="canEditSuperAdminsAndAdmins"
|
|
:isCreateMode="isNew"
|
|
/>
|
|
|
|
<employees-members
|
|
v-if="isTabSelected('employees')"
|
|
:editMode="canEditSuperAdminsAdminsAndDelegated"
|
|
:isCreateMode="isNew"
|
|
/>
|
|
<page-members
|
|
v-if="isTabSelected('member_page')"
|
|
:editMode="canEditSuperAdminsAdminsAndDelegated"
|
|
/>
|
|
</v-col>
|
|
|
|
<v-footer
|
|
fixed
|
|
style="z-index: 4"
|
|
height="90"
|
|
color="primary"
|
|
v-if="$store.getters['members/isSuperAdminAdminOrDelegated']"
|
|
>
|
|
<v-btn title text small :to="localePath('/manager/members')">
|
|
<v-icon>icon-arrow-left</v-icon>
|
|
</v-btn>
|
|
<div class="mx-10">
|
|
<v-btn
|
|
class="ma-2 white--text"
|
|
color="accent"
|
|
depressed
|
|
v-if="
|
|
$store.getters['members/isSuperAdminAdminOrDelegated'] &&
|
|
!$store.getters.isOnlyMemberEditor &&
|
|
!isNew &&
|
|
!isEditMode
|
|
"
|
|
rounded
|
|
@click="switchToEdit"
|
|
>
|
|
<!-- :to="localePath(`${$nuxt.$route.path}?edit`)"
|
|
nuxt -->
|
|
{{ $t('general.edit') }}</v-btn
|
|
>
|
|
|
|
<v-btn
|
|
class="ma-2 white--text"
|
|
:color="$vuetify.theme.dark ? 'secondary' : 'txt'"
|
|
depressed
|
|
@click="save()"
|
|
v-if="
|
|
canSave && ($store.getters.isSuperAdmin || $store.getters.isAdmin)
|
|
"
|
|
rounded
|
|
>Tussentijds opslaan</v-btn
|
|
>
|
|
|
|
<!-- Accept Revision - only super admins or admins -->
|
|
<template v-if="$store.getters.isSuperAdmin || $store.getters.isAdmin">
|
|
<v-btn
|
|
v-cloak
|
|
class="ma-2 white--text"
|
|
color="accent"
|
|
depressed
|
|
rounded
|
|
v-if="
|
|
!isNew &&
|
|
$store.getters['members/revision'] &&
|
|
local.revision.hasChanges &&
|
|
isEditMode
|
|
"
|
|
@click="save(true)"
|
|
>Opslaan en indienen</v-btn
|
|
>
|
|
</template>
|
|
<!-- Store revision - only delegated users -->
|
|
<template v-else>
|
|
<v-btn
|
|
v-cloak
|
|
class="ma-2 white--text"
|
|
color="accent"
|
|
depressed
|
|
v-if="canSave && !isNew"
|
|
rounded
|
|
@click="storeRevision()"
|
|
>Opslaan en indienen</v-btn
|
|
>
|
|
</template>
|
|
</div>
|
|
|
|
<v-btn
|
|
class="ma-2"
|
|
tile
|
|
text
|
|
v-if="canSave && !isNew"
|
|
color="accent"
|
|
depressed
|
|
>
|
|
<v-icon class="mx-2" size="26">mdi-alert-circle-outline</v-icon>
|
|
wijziging
|
|
</v-btn>
|
|
|
|
<v-spacer />
|
|
|
|
<!-- <v-btn class="ma-2" tile text small>
|
|
<v-icon class="mx-2" small>icon-sharepoint</v-icon>
|
|
Documenten
|
|
</v-btn> -->
|
|
|
|
<!-- <v-dialog v-model="dialog" persistent max-width="740">
|
|
<template v-slot:activator="{ on }">
|
|
<v-btn
|
|
class="ma-2"
|
|
tile
|
|
text
|
|
small
|
|
v-if="!isNew && canEdit"
|
|
v-on="on"
|
|
>
|
|
<v-icon class="mx-2">icon-remove</v-icon>
|
|
</v-btn>
|
|
</template>
|
|
<v-card class="primary pa-10" flat>
|
|
<v-card-title class="headline">
|
|
{{
|
|
$t('learning.product_overview.delete_confirmation', {
|
|
productName: local.title,
|
|
})
|
|
}}
|
|
</v-card-title>
|
|
<v-card-actions>
|
|
<div class="ma-4">
|
|
<v-btn
|
|
color="accent"
|
|
class="mx-2"
|
|
@click="deleteMember()"
|
|
rounded
|
|
depressed
|
|
>{{ $t('general.delete') }}</v-btn
|
|
>
|
|
<v-btn
|
|
class="mx-2"
|
|
color="info"
|
|
@click="close"
|
|
rounded
|
|
depressed
|
|
>{{ $t('general.cancel') }}</v-btn
|
|
>
|
|
</div>
|
|
</v-card-actions>
|
|
</v-card>
|
|
</v-dialog> -->
|
|
</v-footer>
|
|
</v-row>
|
|
</template>
|
|
|
|
<script>
|
|
import dayjs from 'dayjs'
|
|
import PageHeader from '~/components/UI/PageHeader/PageHeader'
|
|
import basicMembers from '@/components/Members/BasicMembers'
|
|
import addressMembers from '@/components/Members/AddressMembers'
|
|
import contactsMembers from '@/components/Members/ContactsMembers'
|
|
import employeesMembers from '@/components/Members/EmployeesMembers'
|
|
import pageMembers from '@/components/Members/PageMembers'
|
|
import moreMembers from '@/components/Members/MoreMembers'
|
|
import contributionMembers from '~/components/Members/ContributionMembers.vue'
|
|
|
|
export default {
|
|
layout: `${process.env.CUSTOMER}Admin`,
|
|
components: {
|
|
PageHeader,
|
|
basicMembers,
|
|
addressMembers,
|
|
contactsMembers,
|
|
employeesMembers,
|
|
pageMembers,
|
|
moreMembers,
|
|
contributionMembers,
|
|
},
|
|
data() {
|
|
return {
|
|
tabs: [
|
|
'all',
|
|
'basic',
|
|
'address',
|
|
'contacts',
|
|
'contribution',
|
|
'employees',
|
|
'member_page',
|
|
],
|
|
selectedTab: 'all',
|
|
dialog: false,
|
|
users: [],
|
|
}
|
|
},
|
|
|
|
async asyncData({ $axios, store }) {
|
|
try {
|
|
await store.dispatch('members/pullBranches')
|
|
await store.dispatch('members/pullTypes')
|
|
|
|
if (store.getters.isAdmin || store.getters.isOperator) {
|
|
const response = await $axios.get('/admin/users/getList')
|
|
return { users: response.data }
|
|
}
|
|
return { users: [] }
|
|
} catch (error) {
|
|
console.log('asyncData -> error', error)
|
|
}
|
|
},
|
|
|
|
async mounted() {
|
|
if (!this.isValidSlug) {
|
|
this.$nuxt.error({ statusCode: 404, message: 'URL niet geldig' })
|
|
// this.$router.push('/manager')
|
|
}
|
|
if (this.isSlugNumber) {
|
|
await this.$store.dispatch('members/getAndSetMember', this.slug)
|
|
}
|
|
if (this.isNew && !this.$store.getters.isSuperAdminOrAdmin) {
|
|
this.$nuxt.error({ statusCode: 401, message: 'Je mag dit niet doen' })
|
|
// this.$router.push('/manager')
|
|
}
|
|
|
|
// #warning Laravel Echo has been manually disabled until broadcasting issue is fixed.
|
|
// this.$echo.channel('updates').listen(`.members-updated`, async (e) => {
|
|
// if (this.isSlugNumber) {
|
|
// await this.$store.dispatch('members/getAndSetMember', this.slug)
|
|
// }
|
|
//
|
|
// this.$notifier.showMessage({
|
|
// content: 'Member updated',
|
|
// color: 'success',
|
|
// icon: 'icon-message',
|
|
// })
|
|
// })
|
|
},
|
|
|
|
computed: {
|
|
canEditSuperAdminsAdminsAndDelegated() {
|
|
return (
|
|
(this.isNew || this.isEditMode) &&
|
|
this.$store.getters['members/isSuperAdminAdminOrDelegated']
|
|
)
|
|
},
|
|
canEditSuperAdminsAndAdmins() {
|
|
return (
|
|
(this.isNew || this.isEditMode) &&
|
|
(this.$store.getters.isSuperAdmin || this.$store.getters.isAdmin)
|
|
)
|
|
},
|
|
canSave() {
|
|
if (this.isNew && this.isMemberValidated) return true
|
|
// if (this.isNew) return true
|
|
return (
|
|
this.isSlugNumber &&
|
|
this.isDownloaded &&
|
|
this.$store.getters['members/hasChanges'] &&
|
|
this.isMemberValidated
|
|
)
|
|
},
|
|
isMemberValidated() {
|
|
return this.$store.getters['members/isMemberValidated']
|
|
},
|
|
slug() {
|
|
return this.$route.params.member
|
|
},
|
|
isNew() {
|
|
return this.slug.toLowerCase() === 'new'
|
|
},
|
|
isEditMode() {
|
|
return this.$route.query.edit === null
|
|
},
|
|
remote() {
|
|
return this.$store.state.members.remote
|
|
},
|
|
local() {
|
|
return this.$store.state.members.local
|
|
},
|
|
title() {
|
|
if (this.isNew) return `<h2 class="ma-4">Nieuw</h2>`
|
|
return `${this.local.informal_name || ''} <h2 class="ma-4">${
|
|
this.local.formal_name || ''
|
|
}`
|
|
},
|
|
isSlugNumber() {
|
|
return !isNaN(this.slug.split(['-'], 1))
|
|
},
|
|
isValidSlug() {
|
|
return this.isNew || this.isSlugNumber
|
|
},
|
|
isDownloaded() {
|
|
return Object.keys(this.remote).length > 0
|
|
},
|
|
},
|
|
|
|
methods: {
|
|
formatDate(date) {
|
|
return dayjs(date).format('D MMM YYYY hh:mm').toLowerCase()
|
|
},
|
|
|
|
close() {
|
|
this.dialog = false
|
|
},
|
|
isTabSelected(tab) {
|
|
return this.selectedTab === tab || this.selectedTab === 'all'
|
|
},
|
|
|
|
async save(revision = false) {
|
|
this.$nextTick(() => this.$nuxt.$loading.start())
|
|
|
|
try {
|
|
const response = await this.$store.dispatch('members/store', revision)
|
|
this.$nuxt.$loading.finish()
|
|
this.$router.push(this.localePath('/manager/members'))
|
|
$nuxt.$notifier.showMessage({
|
|
content: `Member stored`,
|
|
color: 'success',
|
|
icon: 'mdi-check',
|
|
})
|
|
} catch (error) {
|
|
this.$nuxt.$loading.finish()
|
|
console.log('save -> error', error)
|
|
this.$notifier.showMessage({
|
|
content: `${
|
|
this.slug.toLowerCase() === 'new' ? 'Creating' : 'Editing'
|
|
} page: ${error.response ? error.response.data.message : error}.`,
|
|
...(error.response && {
|
|
errors: error.response.data.errors,
|
|
}),
|
|
color: 'error',
|
|
icon: 'mdi-alert',
|
|
})
|
|
}
|
|
},
|
|
|
|
switchToEdit() {
|
|
this.$router.push(this.localePath(`${$nuxt.$route.path}?edit`))
|
|
},
|
|
|
|
async storeRevision() {
|
|
try {
|
|
await this.$store.dispatch('members/storeRevision')
|
|
this.$nuxt.$loading.finish()
|
|
this.$router.push(this.localePath('/manager/members'))
|
|
$nuxt.$notifier.showMessage({
|
|
content: `Revision stored`,
|
|
color: 'success',
|
|
icon: 'mdi-check',
|
|
})
|
|
} catch (error) {
|
|
this.$nuxt.$loading.finish()
|
|
console.log('storeRevision -> error', error)
|
|
this.$notifier.showMessage({
|
|
content: `${error.response ? error.response.data.message : error}.`,
|
|
...(error.response && {
|
|
errors: error.response.data.errors,
|
|
}),
|
|
color: 'error',
|
|
icon: 'mdi-alert',
|
|
})
|
|
}
|
|
},
|
|
|
|
async deleteMember() {
|
|
this.dialog = false
|
|
await this.$store.dispatch('members/deleteMember')
|
|
this.$router.push(this.localePath('/manager/members'))
|
|
},
|
|
},
|
|
|
|
async beforeRouteLeave(to, from, next) {
|
|
await this.$store.dispatch('members/resetMember')
|
|
next()
|
|
},
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
.v-card >>> .v-btn__content,
|
|
.v-select .v-select__selection--comma,
|
|
.v-chip__content,
|
|
.v-list-item:not(.v-list-item--active):not(.v-list-item--disabled),
|
|
.v-select.v-select--chips:not(.v-text-field--single-line).v-text-field--enclosed
|
|
.v-select__selections,
|
|
.v-list-item .v-list-item__title,
|
|
.v-list-item .v-list-item__subtitle,
|
|
.v-list-item span,
|
|
.v-input--is-disabled input,
|
|
.v-input--is-disable,
|
|
.dot,
|
|
h2,
|
|
p {
|
|
/* color: var(--v-txt-base); */
|
|
}
|
|
.v-card >>> .v-chip--disabled {
|
|
opacity: 1 !important;
|
|
}
|
|
.v-card >>> .v-chip {
|
|
padding-left: 0 !important;
|
|
}
|
|
.dot {
|
|
cursor: pointer;
|
|
}
|
|
.dot::before {
|
|
content: '\A';
|
|
width: 5px;
|
|
height: 5px;
|
|
border-radius: 50%;
|
|
background: #e54e0f;
|
|
display: block;
|
|
position: relative;
|
|
top: 30px;
|
|
left: 50%;
|
|
opacity: 0;
|
|
}
|
|
.dot:hover::before {
|
|
opacity: 0.5 !important;
|
|
}
|
|
.dot.activeTab::before {
|
|
opacity: 1 !important;
|
|
}
|
|
</style>
|