- 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
This commit is contained in:
685
components/Members/ContactsMembers.vue
Normal file
685
components/Members/ContactsMembers.vue
Normal file
@@ -0,0 +1,685 @@
|
||||
<template>
|
||||
<accordion-card title="Contactpersonen">
|
||||
<v-data-table
|
||||
:headers="headers"
|
||||
:items="contacts"
|
||||
:options="options"
|
||||
hide-default-footer
|
||||
item-key="contact"
|
||||
flat
|
||||
v-if="hasContacts"
|
||||
>
|
||||
<template v-slot:item.contact_person="{ item }">
|
||||
{{ determineNameFullByContactPersonItem(item) }}
|
||||
</template>
|
||||
|
||||
<template v-slot:item.ledencontrole="{ item }">
|
||||
<v-icon
|
||||
:color="!item.approved_at ? 'accent' : 'success'"
|
||||
>
|
||||
{{
|
||||
!item.approved_at
|
||||
? 'mdi-alert-circle-outline'
|
||||
: 'icon-checkmark'
|
||||
}}
|
||||
</v-icon>
|
||||
</template>
|
||||
|
||||
<template v-slot:item.actions="{ item }">
|
||||
<v-btn
|
||||
class="mx-4 white--text view"
|
||||
style="height: 100%"
|
||||
:color="$vuetify.theme.dark ? 'info' : 'txt'"
|
||||
rounded
|
||||
depressed
|
||||
small
|
||||
@click="viewItem(item)"
|
||||
v-if="$store.getters.hasCharges"
|
||||
>{{ $t('general.view') }}</v-btn
|
||||
>
|
||||
|
||||
<v-menu
|
||||
offset-y
|
||||
v-if="
|
||||
editMode && $store.getters['members/isSuperAdminAdminOrDelegated']
|
||||
"
|
||||
>
|
||||
<template v-slot:activator="{ on }">
|
||||
<v-hover v-slot:default="{ hover }">
|
||||
<v-btn
|
||||
:color="hover ? 'info' : ''"
|
||||
:outlined="hover"
|
||||
depressed
|
||||
fab
|
||||
small
|
||||
v-on="on"
|
||||
>
|
||||
<v-icon>icon-options</v-icon>
|
||||
</v-btn>
|
||||
</v-hover>
|
||||
</template>
|
||||
<v-list width="200">
|
||||
<v-list-item @click="editItem(item)">
|
||||
<v-list-item-icon class="mr-1">
|
||||
<v-icon small>icon-edit</v-icon>
|
||||
</v-list-item-icon>
|
||||
|
||||
<v-list-item-content>
|
||||
<v-list-item-subtitle>
|
||||
{{ $t('general.edit') | capitalize }}
|
||||
</v-list-item-subtitle>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
|
||||
<v-dialog max-width="740" persistent v-model="dialogDelete">
|
||||
<template v-slot:activator="{ on }">
|
||||
<v-list-item v-on="on">
|
||||
<v-list-item-icon class="mr-1">
|
||||
<v-icon small>icon-remove</v-icon>
|
||||
</v-list-item-icon>
|
||||
|
||||
<v-list-item-content>
|
||||
<v-list-item-subtitle>
|
||||
{{ $t('general.delete') | capitalize }}
|
||||
</v-list-item-subtitle>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
</template>
|
||||
<v-card class="primary pa-10" flat>
|
||||
<v-card-title class="headline">{{
|
||||
$t('learning.delete_contactsmembers_confirmation')
|
||||
}}</v-card-title>
|
||||
<v-card-actions>
|
||||
<div class="ma-4">
|
||||
<v-btn
|
||||
@click="deleteItem(item)"
|
||||
class="mx-2"
|
||||
color="accent"
|
||||
depressed
|
||||
rounded
|
||||
>{{ $t('general.delete') }}</v-btn
|
||||
>
|
||||
<v-btn
|
||||
@click="dialogDelete = false"
|
||||
class="mx-2"
|
||||
color="info"
|
||||
depressed
|
||||
rounded
|
||||
>{{ $t('general.cancel') }}</v-btn
|
||||
>
|
||||
</div>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
</template>
|
||||
</v-data-table>
|
||||
|
||||
<v-dialog
|
||||
v-model="dialogContacts"
|
||||
:persistent="editModeComputed"
|
||||
max-width="75%"
|
||||
v-if="$store.getters['members/isSuperAdminAdminOrDelegated']"
|
||||
>
|
||||
<template v-slot:activator="{ on, attrs }">
|
||||
<v-btn
|
||||
v-if="editMode"
|
||||
class="my-10 cta-secondary"
|
||||
block
|
||||
depressed
|
||||
min-height="60px"
|
||||
:disabled="isCreateMode"
|
||||
v-on="on"
|
||||
@click="contactEditMode = true"
|
||||
>
|
||||
<v-icon x-small class="mx-4">icon-add</v-icon>Nieuw contactperson
|
||||
toevoegen
|
||||
</v-btn>
|
||||
</template>
|
||||
<v-card ref="card">
|
||||
<v-card-title>
|
||||
<span class="headline">{{ formTitle }}</span>
|
||||
</v-card-title>
|
||||
|
||||
<v-card-text>
|
||||
<v-container>
|
||||
<v-row>
|
||||
<v-col cols="12" sm="12" md="6">
|
||||
<v-subheader class="txt--text font-weight-black"
|
||||
>Rol</v-subheader
|
||||
>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="12" md="6">
|
||||
<v-select
|
||||
:items="roles"
|
||||
v-model="editedItem.function"
|
||||
:outlined="editModeComputed"
|
||||
:solo="!editModeComputed"
|
||||
:disabled="!editModeComputed"
|
||||
:flat="!editModeComputed"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row>
|
||||
<v-col cols="12" sm="12" md="6">
|
||||
<v-subheader class="txt--text font-weight-black"
|
||||
>Locatie</v-subheader
|
||||
>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="12" md="6">
|
||||
<!-- :items="formatAddresses(addresses)" -->
|
||||
<v-select
|
||||
:items="addresses"
|
||||
item-text="indicating"
|
||||
item-value="id"
|
||||
v-model="editedItem.address_id"
|
||||
:outlined="editModeComputed"
|
||||
:solo="!editModeComputed"
|
||||
:disabled="!editModeComputed"
|
||||
:flat="!editModeComputed"
|
||||
>
|
||||
<template v-slot:item="{ item, attrs, on }">
|
||||
{{
|
||||
`
|
||||
${item.indicating || ''}
|
||||
${item.house_number + ',' || ''}
|
||||
${item.postal + ',' || ''}
|
||||
${item.city + ',' || ''}
|
||||
${item.country || ''}
|
||||
`
|
||||
}}
|
||||
</template>
|
||||
</v-select>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row>
|
||||
<v-col cols="12" sm="12" md="6">
|
||||
<v-subheader class="txt--text font-weight-black"
|
||||
>Aanhef</v-subheader
|
||||
>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="12" md="6">
|
||||
<v-select
|
||||
:items="salutations"
|
||||
v-model="editedItem.salutation"
|
||||
:outlined="editModeComputed"
|
||||
:solo="!editModeComputed"
|
||||
:disabled="!editModeComputed"
|
||||
:flat="!editModeComputed"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row>
|
||||
<v-col cols="12" sm="12" md="6">
|
||||
<v-subheader class="txt--text font-weight-black"
|
||||
>Voorletters</v-subheader
|
||||
>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="12" md="6">
|
||||
<v-text-field
|
||||
:outlined="editModeComputed"
|
||||
:solo="!editModeComputed"
|
||||
:disabled="!editModeComputed"
|
||||
:flat="!editModeComputed"
|
||||
v-model="editedItem.initials"
|
||||
></v-text-field>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row>
|
||||
<v-col cols="12" sm="12" md="6">
|
||||
<v-subheader class="txt--text font-weight-black"
|
||||
>Voornaam</v-subheader
|
||||
>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="12" md="6">
|
||||
<v-text-field
|
||||
:outlined="editModeComputed"
|
||||
:solo="!editModeComputed"
|
||||
:disabled="!editModeComputed"
|
||||
:flat="!editModeComputed"
|
||||
v-model="editedItem.firstname"
|
||||
:rules="rules.firstname"
|
||||
required
|
||||
error
|
||||
></v-text-field>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row>
|
||||
<v-col cols="12" sm="12" md="6">
|
||||
<v-subheader class="txt--text font-weight-black"
|
||||
>Tussenvoegsel</v-subheader
|
||||
>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="12" md="6">
|
||||
<v-text-field
|
||||
:outlined="editModeComputed"
|
||||
:solo="!editModeComputed"
|
||||
:disabled="!editModeComputed"
|
||||
:flat="!editModeComputed"
|
||||
v-model="editedItem.middlename"
|
||||
></v-text-field>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row>
|
||||
<v-col cols="12" sm="12" md="6">
|
||||
<v-subheader class="txt--text font-weight-black"
|
||||
>Achternaam</v-subheader
|
||||
>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="12" md="6">
|
||||
<v-text-field
|
||||
:outlined="editModeComputed"
|
||||
:solo="!editModeComputed"
|
||||
:disabled="!editModeComputed"
|
||||
:flat="!editModeComputed"
|
||||
v-model="editedItem.lastname"
|
||||
:rules="rules.lastname"
|
||||
required
|
||||
error
|
||||
></v-text-field>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row>
|
||||
<v-col cols="12" sm="12" md="6">
|
||||
<v-subheader class="txt--text font-weight-black"
|
||||
>Functie</v-subheader
|
||||
>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="12" md="6">
|
||||
<v-text-field
|
||||
:outlined="editModeComputed"
|
||||
:solo="!editModeComputed"
|
||||
:disabled="!editModeComputed"
|
||||
:flat="!editModeComputed"
|
||||
v-model="editedItem.job_title"
|
||||
></v-text-field>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row>
|
||||
<v-col cols="12" sm="12" md="6">
|
||||
<v-subheader class="txt--text font-weight-black"
|
||||
>Mailadres</v-subheader
|
||||
>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="12" md="6">
|
||||
<v-text-field
|
||||
:outlined="editModeComputed"
|
||||
:solo="!editModeComputed"
|
||||
:disabled="!editModeComputed"
|
||||
:flat="!editModeComputed"
|
||||
v-model="editedItem.email"
|
||||
type="email"
|
||||
required
|
||||
error
|
||||
:rules="[rules.email.required]"
|
||||
></v-text-field>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row>
|
||||
<v-col cols="12" sm="12" md="6">
|
||||
<v-subheader class="txt--text font-weight-black"
|
||||
>Mailadres 2</v-subheader
|
||||
>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="12" md="6">
|
||||
<v-text-field
|
||||
:outlined="editModeComputed"
|
||||
:solo="!editModeComputed"
|
||||
:disabled="!editModeComputed"
|
||||
:flat="!editModeComputed"
|
||||
v-model="editedItem.email2"
|
||||
type="email"
|
||||
></v-text-field>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row>
|
||||
<v-col cols="12" sm="12" md="6">
|
||||
<v-subheader class="txt--text font-weight-black"
|
||||
>Mailadres 3</v-subheader
|
||||
>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="12" md="6">
|
||||
<v-text-field
|
||||
:outlined="editModeComputed"
|
||||
:solo="!editModeComputed"
|
||||
:disabled="!editModeComputed"
|
||||
:flat="!editModeComputed"
|
||||
v-model="editedItem.email3"
|
||||
type="email"
|
||||
></v-text-field>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row>
|
||||
<v-col cols="12" sm="12" md="6">
|
||||
<v-subheader class="txt--text font-weight-black"
|
||||
>Telefoonnummer</v-subheader
|
||||
>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="12" md="6">
|
||||
<v-text-field
|
||||
:outlined="editModeComputed"
|
||||
:solo="!editModeComputed"
|
||||
:disabled="!editModeComputed"
|
||||
:flat="!editModeComputed"
|
||||
v-model="editedItem.phone"
|
||||
></v-text-field>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row>
|
||||
<v-col cols="12" sm="12" md="6">
|
||||
<v-subheader class="txt--text font-weight-black"
|
||||
>Mobiel</v-subheader
|
||||
>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="12" md="6">
|
||||
<v-text-field
|
||||
:outlined="editModeComputed"
|
||||
:solo="!editModeComputed"
|
||||
:disabled="!editModeComputed"
|
||||
:flat="!editModeComputed"
|
||||
v-model="editedItem.mobile"
|
||||
></v-text-field>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</v-card-text>
|
||||
|
||||
<v-divider />
|
||||
<v-card-actions v-if="editModeComputed">
|
||||
<v-btn
|
||||
class="ma-2 white--text"
|
||||
color="info"
|
||||
depressed
|
||||
rounded
|
||||
:disabled="loading"
|
||||
@click="save"
|
||||
v-if="
|
||||
$store.getters.isAdmin ||
|
||||
$store.getters.isOperator ||
|
||||
isUserDelegated
|
||||
"
|
||||
>{{ $t('general.save') }}</v-btn
|
||||
>
|
||||
<v-spacer />
|
||||
<v-btn
|
||||
class="ma-2 white--text"
|
||||
color="txt"
|
||||
depressed
|
||||
text
|
||||
:disabled="loading"
|
||||
@click="close"
|
||||
>{{ $t('general.cancel') }}</v-btn
|
||||
>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
|
||||
<p v-if="isCreateMode" class="text-center">
|
||||
Bij het aanmaken van een nieuw lid kiest u eerst voor 'Tussentijds
|
||||
Opslaan' om deze functie te activeren.
|
||||
</p>
|
||||
</accordion-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import accordionCard from '@/components/UI/AccordionCard/AccordionCard'
|
||||
import Name from '@/util/Name'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
accordionCard,
|
||||
},
|
||||
props: {
|
||||
editMode: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
isCreateMode: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
options: {
|
||||
itemsPerPage: -1,
|
||||
},
|
||||
headers: [
|
||||
{ text: 'rol', value: 'function' },
|
||||
{ text: 'contactpersoon', value: 'contact_person' },
|
||||
{ text: 'ledencontrole', value: 'ledencontrole', sortable: false },
|
||||
{ text: '', value: 'actions' },
|
||||
],
|
||||
roles: [
|
||||
'Bestuurder',
|
||||
'Klankbordgroeplid cc',
|
||||
'Klankbordgroeplid cp',
|
||||
'Algemene vergadering cp',
|
||||
'Algemene vergadering cc',
|
||||
'Vertegenwoordiger',
|
||||
],
|
||||
salutations: ['Mevrouw', 'De heer'],
|
||||
contactEditMode: false,
|
||||
dialogContacts: false,
|
||||
dialogDelete: false,
|
||||
editedIndex: -1,
|
||||
editedItem: {},
|
||||
defaultItem: {},
|
||||
loading: false,
|
||||
rules: {
|
||||
firstname: [(v) => !!v || 'De voornaam is verplicht.'],
|
||||
lastname: [(v) => !!v || 'De achternaam is verplicht.'],
|
||||
email: {
|
||||
required: (value) => !!value || 'Het mailadres is verplicht.',
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
local() {
|
||||
return this.$store.state.members.local
|
||||
},
|
||||
addresses() {
|
||||
if (!this.local || !Array.isArray(this.local.addresses)) return []
|
||||
return this.local.addresses
|
||||
},
|
||||
hasAddresses() {
|
||||
if (!this.addresses) return false
|
||||
return this.addresses.length > 0
|
||||
},
|
||||
contacts() {
|
||||
if (!this.local || !Array.isArray(this.local.contacts)) return []
|
||||
return this.local.contacts
|
||||
},
|
||||
hasContacts() {
|
||||
if (!this.contacts) return false
|
||||
return this.contacts.length > 0
|
||||
},
|
||||
formTitle() {
|
||||
return this.editedIndex === -1 ? 'new' : 'edit'
|
||||
},
|
||||
isCreatedModeContact() {
|
||||
return this.editedIndex === -1
|
||||
},
|
||||
editModeComputed() {
|
||||
return this.contactEditMode && this.editMode
|
||||
},
|
||||
isUserDelegated() {
|
||||
return this.local.user_id === this.$store.getters.loggedInUser.id
|
||||
},
|
||||
},
|
||||
|
||||
watch: {
|
||||
dialogContacts(val) {
|
||||
val || this.close()
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
close() {
|
||||
this.dialogContacts = false
|
||||
this.$nextTick(() => {
|
||||
this.editedItem = Object.assign({}, this.defaultItem)
|
||||
this.editedIndex = -1
|
||||
this.$refs.card.$el.scrollIntoView(true)
|
||||
})
|
||||
},
|
||||
|
||||
viewItem(item) {
|
||||
this.contactEditMode = false
|
||||
this.editedIndex = this.contacts.indexOf(item)
|
||||
this.editedItem = Object.assign({}, item)
|
||||
this.$forceUpdate()
|
||||
this.dialogContacts = true
|
||||
},
|
||||
|
||||
editItem(item) {
|
||||
if (!this.editMode) return
|
||||
this.setItemToEdit(item)
|
||||
this.dialogContacts = true
|
||||
},
|
||||
|
||||
setItemToEdit(item) {
|
||||
this.contactEditMode = true
|
||||
this.editedIndex = this.contacts.indexOf(item)
|
||||
this.editedItem = Object.assign({}, item)
|
||||
this.$forceUpdate()
|
||||
},
|
||||
|
||||
async save() {
|
||||
this.$nextTick(() => {
|
||||
this.$refs.card.$el.scrollIntoView(true)
|
||||
})
|
||||
this.loading = true
|
||||
this.$nextTick(() => this.$nuxt.$loading.start())
|
||||
this.dialogContacts = false
|
||||
if (this.isCreatedModeContact) {
|
||||
try {
|
||||
await this.$store.dispatch('members/storeContact', this.editedItem)
|
||||
this.loading = false
|
||||
} catch (error) {
|
||||
console.log('save -> error', error)
|
||||
this.loading = false
|
||||
}
|
||||
} else {
|
||||
// Edit mode
|
||||
try {
|
||||
await this.$store.dispatch('members/storeContact', this.editedItem)
|
||||
this.loading = false
|
||||
this.dialogContacts = false
|
||||
} catch (error) {
|
||||
console.log('save -> error', error)
|
||||
this.loading = false
|
||||
}
|
||||
}
|
||||
|
||||
this.close()
|
||||
this.$nuxt.$loading.finish()
|
||||
},
|
||||
|
||||
async deleteItem(item) {
|
||||
if (!item.id) {
|
||||
this.$notifier.showMessage({
|
||||
content: `No contact to delete selected`,
|
||||
color: 'error',
|
||||
icon: 'icon-message',
|
||||
})
|
||||
}
|
||||
|
||||
this.$nextTick(() => this.$nuxt.$loading.start())
|
||||
|
||||
try {
|
||||
await this.$store.dispatch('members/deleteContact', item)
|
||||
|
||||
this.dialogDelete = false
|
||||
this.dialogContacts = false
|
||||
this.$nuxt.$loading.finish()
|
||||
} catch (error) {
|
||||
console.log('deleteItem -> error', error)
|
||||
this.$nuxt.$loading.finish()
|
||||
}
|
||||
},
|
||||
|
||||
determineNameFullByContactPersonItem(item) {
|
||||
return Name.fromContactPersonItem(item).nameFull
|
||||
},
|
||||
|
||||
formatAddresses(addresses) {
|
||||
return addresses.map((x) => this.formatAddress(x)).filter((x) => x)
|
||||
},
|
||||
|
||||
formatAddress(address) {
|
||||
const addressParts = [
|
||||
address.indicating || '',
|
||||
address.house_number || '',
|
||||
address.postal || '',
|
||||
address.city || '',
|
||||
address.country || '',
|
||||
]
|
||||
|
||||
return addressParts
|
||||
.filter((x) => x)
|
||||
.map((x) => x.trim())
|
||||
.join(', ')
|
||||
.trimRight()
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.v-card >>> table,
|
||||
.v-card >>> .v-data-table-header tr,
|
||||
.v-card >>> .v-data-footer {
|
||||
background-color: var(--v-primary-base);
|
||||
}
|
||||
.v-card >>> .v-data-table-header th span {
|
||||
color: var(--v-tertiary-base);
|
||||
}
|
||||
.v-card >>> .text-start,
|
||||
.v-card >>> .v-icon,
|
||||
.v-dialog__content >>> .v-subheader,
|
||||
.v-dialog__content >>> i {
|
||||
color: var(--v-txt-base);
|
||||
font-weight: bold;
|
||||
}
|
||||
.v-card >>> .v-subheader {
|
||||
padding: 0px !important;
|
||||
}
|
||||
.v-dialog__content >>> .v-dialog {
|
||||
border: 4px solid var(--v-secAccent-base);
|
||||
max-height: 68% !important;
|
||||
/* box-shadow: inset 0px 0px 2px 2px var(--v-secAccent-base),
|
||||
inset 6px 6px 14px -14px var(--v-secAccent-base) !important; */
|
||||
}
|
||||
.v-dialog .v-card {
|
||||
background: var(--v-secondary-base);
|
||||
}
|
||||
|
||||
.v-dialog__content >>> .v-input__slot {
|
||||
background: var(--v-primary-base);
|
||||
border: 1px solid var(--v-lines-base);
|
||||
}
|
||||
.v-menu__content >>> .v-date-picker-table {
|
||||
height: 285px !important;
|
||||
}
|
||||
table tr button.view {
|
||||
opacity: 0;
|
||||
}
|
||||
table tr:hover button.view {
|
||||
opacity: 1;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user