- 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:
483
pages/manager/members/_member.vue
Normal file
483
pages/manager/members/_member.vue
Normal file
@@ -0,0 +1,483 @@
|
||||
<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>
|
||||
254
pages/manager/members/branches/index.vue
Normal file
254
pages/manager/members/branches/index.vue
Normal file
@@ -0,0 +1,254 @@
|
||||
<template>
|
||||
<v-row>
|
||||
<v-col>
|
||||
<div class="d-flex justify-space-between">
|
||||
<h2 class="ml-6 txt--text">Branches ({{ items.length }})</h2>
|
||||
<current-date-time showIcon class="mr-6" />
|
||||
</div>
|
||||
|
||||
<accordion-card :title="$t('general.list') | capitalize">
|
||||
<v-row v-for="(item, index) in items" :key="item.title + index">
|
||||
<v-col cols="12" sm="3" md="3">
|
||||
<v-subheader class="ml-10 txt--text font-weight-black"
|
||||
>{{ index + 1 }}.
|
||||
</v-subheader>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="6">
|
||||
<v-text-field
|
||||
outlined
|
||||
:name="item.title + index"
|
||||
:value="item.title"
|
||||
@change="setDynamicTitle($event, index)"
|
||||
append-icon="icon-close"
|
||||
@click:append="askForDelete(item)"
|
||||
@click:append-outer="update(item.id, index)"
|
||||
:append-outer-icon="
|
||||
hasItemChanges(index) ? 'mdi-content-save' : ''
|
||||
"
|
||||
:error="hasItemChanges(index)"
|
||||
>
|
||||
</v-text-field>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="3" md="3"> </v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row>
|
||||
<v-col cols="12" sm="12" md="3">
|
||||
<v-subheader class="ml-10 txt--text font-weight-black">
|
||||
Nieuw
|
||||
</v-subheader>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="12" md="6">
|
||||
<v-text-field v-model="newItem" outlined> </v-text-field>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="12" md="3"> </v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row>
|
||||
<v-col cols="12" sm="12" md="3"> </v-col>
|
||||
<v-col cols="12" sm="12" md="6">
|
||||
<v-btn
|
||||
class="cta-secondary"
|
||||
min-height="60px"
|
||||
block
|
||||
depressed
|
||||
@click="addBranch"
|
||||
:disabled="!newItem"
|
||||
>
|
||||
<v-icon x-small class="mx-4">icon-add</v-icon>
|
||||
|
||||
{{ $t('general.add') | capitalize }}</v-btn
|
||||
>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="12" md="3"> </v-col>
|
||||
</v-row>
|
||||
</accordion-card>
|
||||
|
||||
<v-dialog v-model="dialog" persistent max-width="740">
|
||||
<v-card class="primary pa-10" flat>
|
||||
<v-card-title class="headline">
|
||||
{{
|
||||
$t('learning.filters.delete_item_confirmation', {
|
||||
itemName: itemSelected.title,
|
||||
})
|
||||
}}
|
||||
</v-card-title>
|
||||
<v-card-actions>
|
||||
<div class="ma-4">
|
||||
<v-btn
|
||||
color="accent"
|
||||
class="mx-2"
|
||||
@click="deleteItem(itemSelected.id)"
|
||||
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-col>
|
||||
|
||||
<v-footer fixed style="z-index: 4" height="90" color="primary">
|
||||
<v-btn text nuxt :to="localePath('/manager/learning/filters')">
|
||||
<v-icon>icon-arrow-left</v-icon>
|
||||
</v-btn>
|
||||
</v-footer>
|
||||
</v-row>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import accordionCard from '@/components/UI/AccordionCard/AccordionCard'
|
||||
import currentDateTime from '@/components/CurrentDateTime/CurrentDateTime'
|
||||
import PageHeader from '~/components/UI/PageHeader/PageHeader'
|
||||
|
||||
import Vue from 'vue'
|
||||
|
||||
export default {
|
||||
layout: `${process.env.CUSTOMER}Admin`,
|
||||
middleware: 'allowSuperAdminOrAdmin',
|
||||
components: {
|
||||
accordionCard,
|
||||
PageHeader,
|
||||
currentDateTime,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
newItem: '',
|
||||
branches: [],
|
||||
items: [],
|
||||
itemsRemote: [],
|
||||
itemSelected: {},
|
||||
dialog: false,
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
await this.getBranches()
|
||||
},
|
||||
|
||||
computed: {},
|
||||
methods: {
|
||||
async getBranches() {
|
||||
const response = await this.$axios.get('/members/branches')
|
||||
this.items = [...response.data]
|
||||
this.itemsRemote = [...response.data]
|
||||
},
|
||||
close() {
|
||||
this.dialog = false
|
||||
},
|
||||
save() {
|
||||
this.close()
|
||||
},
|
||||
hasItemChanges(index) {
|
||||
return this.items[index].title !== this.itemsRemote[index].title
|
||||
},
|
||||
setDynamicTitle(title, index) {
|
||||
this.items[index] = Object.assign({}, { ...this.items[index], title })
|
||||
Vue.set(this.items, index, this.items[index])
|
||||
},
|
||||
|
||||
async addBranch() {
|
||||
const data = { title: this.newItem }
|
||||
|
||||
this.$nextTick(() => this.$nuxt.$loading.start())
|
||||
|
||||
try {
|
||||
await this.$axios.post(`/members/branches`, data)
|
||||
await this.getBranches()
|
||||
this.newItem = ''
|
||||
this.$nuxt.$loading.finish()
|
||||
} catch (error) {
|
||||
this.$nuxt.$loading.finish()
|
||||
this.$notifier.showMessage({
|
||||
content: `Error creating the filter item: ` + error.message,
|
||||
color: 'error',
|
||||
icon: 'icon-message',
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
async update(id, index) {
|
||||
if (
|
||||
id === null ||
|
||||
id === undefined ||
|
||||
index === null ||
|
||||
index === undefined ||
|
||||
!this.items[index]
|
||||
)
|
||||
return
|
||||
|
||||
this.$nextTick(() => this.$nuxt.$loading.start())
|
||||
|
||||
try {
|
||||
await this.$axios.post(`/members/branches`, this.items[index])
|
||||
|
||||
await this.getBranches()
|
||||
this.$nuxt.$loading.finish()
|
||||
this.$notifier.showMessage({
|
||||
content: `Branch updated successfully`,
|
||||
color: 'success',
|
||||
icon: 'icon-message',
|
||||
})
|
||||
} catch (error) {
|
||||
this.$nuxt.$loading.finish()
|
||||
this.$notifier.showMessage({
|
||||
content: `Error updating the Branch: ` + error.message,
|
||||
color: 'error',
|
||||
icon: 'icon-message',
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
askForDelete(item) {
|
||||
this.itemSelected = item
|
||||
this.dialog = true
|
||||
},
|
||||
|
||||
async deleteItem(itemId) {
|
||||
if (!itemId) {
|
||||
this.$notifier.showMessage({
|
||||
content: `No item to delete selected`,
|
||||
color: 'error',
|
||||
icon: 'icon-message',
|
||||
})
|
||||
}
|
||||
|
||||
this.$nextTick(() => this.$nuxt.$loading.start())
|
||||
|
||||
try {
|
||||
await this.$axios.delete(`/members/branches/${itemId}`)
|
||||
|
||||
this.dialog = false
|
||||
this.itemSelected = {}
|
||||
|
||||
await this.getBranches()
|
||||
|
||||
this.$nuxt.$loading.finish()
|
||||
|
||||
this.$notifier.showMessage({
|
||||
content: `Branch deleted`,
|
||||
color: 'success',
|
||||
icon: 'mdi-delete',
|
||||
})
|
||||
} catch (error) {
|
||||
this.dialog = false
|
||||
this.$nuxt.$loading.finish()
|
||||
|
||||
this.$notifier.showMessage({
|
||||
content: `Error trying to delete the selected Branch`,
|
||||
color: 'error',
|
||||
icon: 'mdi-delete',
|
||||
})
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
137
pages/manager/members/control.vue
Normal file
137
pages/manager/members/control.vue
Normal file
@@ -0,0 +1,137 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="pa-6">
|
||||
<page-header class="d-flex justify-space-between">
|
||||
<div class="subtitle-1">
|
||||
<span class="mr-4 display-1">
|
||||
Ledencontrole
|
||||
<small class="font-weight-light">( {{ changes.length }})</small>
|
||||
</span>
|
||||
</div>
|
||||
</page-header>
|
||||
</div>
|
||||
|
||||
<members-changes :items="changes" :users="users" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import PageHeader from '~/components/UI/PageHeader/PageHeader'
|
||||
import MembersChanges from '~/components/Members/MembersChanges'
|
||||
|
||||
export default {
|
||||
layout: `${process.env.CUSTOMER}Admin`,
|
||||
middleware: 'allowSuperAdminOrAdmin',
|
||||
components: {
|
||||
PageHeader,
|
||||
MembersChanges,
|
||||
},
|
||||
|
||||
async asyncData({ $axios, store }) {
|
||||
try {
|
||||
if (store.getters.isSuperAdminOrAdmin) {
|
||||
await store.dispatch('members/pullData')
|
||||
const response = await $axios.get('/admin/users/getList')
|
||||
return { users: response.data }
|
||||
}
|
||||
return { users: [] }
|
||||
} catch (error) {
|
||||
console.log('Data -> error', error)
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
users: [],
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
allMembers() {
|
||||
return this.$store.getters['members/membersFiltered']
|
||||
},
|
||||
|
||||
changes() {
|
||||
const tmp = []
|
||||
|
||||
// cycle members
|
||||
this.allMembers.forEach((member) => {
|
||||
member['changes'] = []
|
||||
let found = null
|
||||
|
||||
// cycle addresses, contacts, summaries
|
||||
// const properties = [
|
||||
// 'addresses',
|
||||
// 'contacts',
|
||||
// 'contributions',
|
||||
// 'summaries',
|
||||
// ]
|
||||
|
||||
// Doesn't work due to a vuex error
|
||||
// properties.forEach((property) => {
|
||||
// if (member[property].length) {
|
||||
// member[property].some((item) => {
|
||||
// if (!item.approved_at) {
|
||||
// if (!member[property].includes(property)) {
|
||||
// member[property].push(property)
|
||||
// }
|
||||
// found = true
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
// })
|
||||
|
||||
if (member.addresses.length) {
|
||||
member.addresses.some((a) => {
|
||||
if (!a.approved_at) {
|
||||
if (!member['changes'].includes('Addresses')) {
|
||||
member['changes'].push('Addresses')
|
||||
}
|
||||
found = true
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (member.contacts.length) {
|
||||
member.contacts.some((c) => {
|
||||
if (!c.approved_at) {
|
||||
if (!member['changes'].includes('Contacts')) {
|
||||
member['changes'].push('Contacts')
|
||||
}
|
||||
found = true
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (member.contributions.length) {
|
||||
member.contributions.some((c) => {
|
||||
if (!c.approved_at) {
|
||||
if (!member['changes'].includes('Contributions')) {
|
||||
member['changes'].push('Contributions')
|
||||
}
|
||||
found = true
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (member.summaries.length) {
|
||||
member.summaries.some((s) => {
|
||||
if (!s.approved_at) {
|
||||
if (!member['changes'].includes('Werknemers')) {
|
||||
member['changes'].push('Werknemers')
|
||||
}
|
||||
found = true
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (found) {
|
||||
tmp.push(member)
|
||||
}
|
||||
})
|
||||
|
||||
return tmp
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
236
pages/manager/members/index.vue
Normal file
236
pages/manager/members/index.vue
Normal file
@@ -0,0 +1,236 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="pa-6">
|
||||
<page-header class="d-flex justify-space-between">
|
||||
<div class="subtitle-1">
|
||||
<span
|
||||
:class="{ 'display-1': selector === 'allMembers' }"
|
||||
:style="{ cursor: 'pointer' }"
|
||||
@click="selector = 'allMembers'"
|
||||
class="mr-4"
|
||||
>
|
||||
Leden
|
||||
<small class="font-weight-light">({{ allMembers.length }})</small>
|
||||
</span>
|
||||
|
||||
<span
|
||||
:class="{ 'display-1': selector === 'active' }"
|
||||
:style="{ cursor: 'pointer' }"
|
||||
@click="selector = 'active'"
|
||||
class="mx-4"
|
||||
v-if="$store.getters.isAdmin || $store.getters.isOperator"
|
||||
>
|
||||
Actieve leden
|
||||
<small class="font-weight-light">({{ active.length }})</small>
|
||||
</span>
|
||||
|
||||
<span
|
||||
:class="{ 'display-1': selector === 'inactive' }"
|
||||
:style="{ cursor: 'pointer' }"
|
||||
@click="selector = 'inactive'"
|
||||
class="mx-4"
|
||||
v-if="$store.getters.isAdmin || $store.getters.isOperator"
|
||||
>
|
||||
Inactieve leden
|
||||
<small class="font-weight-light">({{ inactive.length }})</small>
|
||||
</span>
|
||||
|
||||
<span
|
||||
:class="{ 'display-1': selector === 'deleted' }"
|
||||
:style="{ cursor: 'pointer' }"
|
||||
@click="selector = 'deleted'"
|
||||
class="mx-4"
|
||||
v-if="$store.getters.isAdmin || $store.getters.isOperator"
|
||||
>
|
||||
Verwijderd
|
||||
<small class="font-weight-light">({{ deleted.length }})</small>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<!-- here checks -->
|
||||
</div>
|
||||
</page-header>
|
||||
</div>
|
||||
|
||||
<members-table :members="membersComputed" />
|
||||
|
||||
<v-footer
|
||||
fixed
|
||||
style="z-index: 4"
|
||||
height="90"
|
||||
color="primary"
|
||||
v-if="$store.getters.isSuperAdminOrAdmin"
|
||||
>
|
||||
<v-btn text nuxt :to="localePath('/manager')">
|
||||
<v-icon>icon-arrow-left</v-icon>
|
||||
</v-btn>
|
||||
<div class="mx-10">
|
||||
<v-btn
|
||||
color="accent"
|
||||
depressed
|
||||
rounded
|
||||
to="/manager/members/new"
|
||||
class="ml-10"
|
||||
>
|
||||
<v-icon left size="8">icon-add</v-icon>
|
||||
<small>Lid toevoegen</small>
|
||||
</v-btn>
|
||||
</div>
|
||||
|
||||
<v-spacer />
|
||||
<v-btn class="ma-2" tile text small @click="exportCsv">
|
||||
<v-icon class="mx-2" x-small>icon-export</v-icon>
|
||||
{{ $t('general.export_csv') | capitalize }}
|
||||
</v-btn>
|
||||
</v-footer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import csv from 'csv'
|
||||
import {DateTime} from 'luxon'
|
||||
import dayjs from 'dayjs'
|
||||
import download from 'downloadjs'
|
||||
import MembersTable from '~/components/Members/MembersTable'
|
||||
import PageHeader from '~/components/UI/PageHeader/PageHeader'
|
||||
|
||||
const I18N_CSV_WRAPPER_KEY = 'csv.members'
|
||||
const I18N_CSV_HEADER_KEYS = [
|
||||
'id',
|
||||
'type',
|
||||
'informal_name',
|
||||
'formal_name',
|
||||
'start_membership',
|
||||
'main_branch',
|
||||
'sub_branches',
|
||||
]
|
||||
const CSV_VALUE_NONE = '-'
|
||||
|
||||
export default {
|
||||
layout: `${process.env.CUSTOMER}Admin`,
|
||||
// middleware: 'adminsOrOperatorsOrMemberEditors',
|
||||
|
||||
components: {
|
||||
PageHeader,
|
||||
MembersTable,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
selector: 'allMembers',
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
exportCsv() {
|
||||
let inputData = [
|
||||
I18N_CSV_HEADER_KEYS.map((key) => {
|
||||
return this.$t(`${I18N_CSV_WRAPPER_KEY}.${key}`)
|
||||
}),
|
||||
]
|
||||
|
||||
this.allMembers.forEach((member) => {
|
||||
let subBranchTitles = member.sub_branches.map(branch => branch.title)
|
||||
subBranchTitles.sort()
|
||||
|
||||
inputData.push([
|
||||
member.id,
|
||||
member.type ?? CSV_VALUE_NONE,
|
||||
member.informal_name ?? CSV_VALUE_NONE,
|
||||
member.formal_name ?? CSV_VALUE_NONE,
|
||||
this.formatCsvDate(
|
||||
member.start_membership,
|
||||
'yyyy-LL-dd HH:mm:ss',
|
||||
) ?? CSV_VALUE_NONE,
|
||||
member.main_branch ?? CSV_VALUE_NONE,
|
||||
subBranchTitles.length
|
||||
? subBranchTitles.join(', ')
|
||||
: CSV_VALUE_NONE,
|
||||
])
|
||||
})
|
||||
|
||||
inputData.sort((rowLeft, rowRight) => rowRight.id - rowLeft.id);
|
||||
|
||||
csv.stringify(inputData, { quoted: true }, (err, output) => {
|
||||
if (err) {
|
||||
// TODO: handle
|
||||
} else {
|
||||
download(
|
||||
output,
|
||||
this.$t(`${I18N_CSV_WRAPPER_KEY}.filename`),
|
||||
'text/csv'
|
||||
)
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
formatCsvDate: (input, inputFormat) => {
|
||||
if (input) {
|
||||
return DateTime.fromFormat(input, inputFormat).toFormat('yyyy-LL-dd')
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
allMembers() {
|
||||
return this.$store.getters['members/membersFiltered']
|
||||
},
|
||||
published() {
|
||||
return this.allMembers.filter((member) => !member.deleted_at)
|
||||
},
|
||||
active() {
|
||||
return this.published.filter(
|
||||
(member) =>
|
||||
!member.end_membership || dayjs().isBefore(member.end_membership)
|
||||
)
|
||||
},
|
||||
inactive() {
|
||||
return this.published.filter(
|
||||
(member) =>
|
||||
member.end_membership && !dayjs().isBefore(member.end_membership)
|
||||
)
|
||||
},
|
||||
deleted() {
|
||||
return this.allMembers.filter((member) => member.deleted_at)
|
||||
},
|
||||
membersComputed() {
|
||||
if (
|
||||
!['allMembers', 'active', 'inactive', 'deleted'].includes(this.selector)
|
||||
) {
|
||||
return []
|
||||
}
|
||||
return this[this.selector]
|
||||
},
|
||||
},
|
||||
|
||||
async asyncData({ $axios, store }) {
|
||||
try {
|
||||
await store.dispatch('members/pullBranches')
|
||||
|
||||
if (!store.getters['members/hasMembers']) {
|
||||
await store.dispatch('members/pullData')
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('Data -> error', error)
|
||||
}
|
||||
},
|
||||
|
||||
// #warning Laravel Echo has been manually disabled until broadcasting issue is fixed.
|
||||
// async mounted() {
|
||||
// this.$echo.channel('updates').listen(`.members-updated`, async (e) => {
|
||||
// await this.$store.dispatch('members/pullData')
|
||||
//
|
||||
// this.$notifier.showMessage({
|
||||
// content: 'Members updated',
|
||||
// color: 'success',
|
||||
// icon: 'icon-message',
|
||||
// })
|
||||
// })
|
||||
// },
|
||||
|
||||
async beforeRouteLeave(to, from, next) {
|
||||
await this.$store.commit('members/RESET_MEMBERS')
|
||||
next()
|
||||
},
|
||||
}
|
||||
</script>
|
||||
341
pages/manager/members/managementinfo.vue
Normal file
341
pages/manager/members/managementinfo.vue
Normal file
@@ -0,0 +1,341 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="pa-6">
|
||||
<page-header class="d-flex justify-space-between">
|
||||
<div class="subtitle-1">
|
||||
<span
|
||||
:class="{ 'display-1': selector === 'published' }"
|
||||
:style="{ cursor: 'pointer' }"
|
||||
@click="selector = 'published'"
|
||||
class="mr-4"
|
||||
>Managementinformatie</span>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<!-- here checks -->
|
||||
</div>
|
||||
</page-header>
|
||||
</div>
|
||||
<!-- kaal -->
|
||||
<div class="management-block-container">
|
||||
<div class="row header-top">
|
||||
<div class="col-md-2 titel">#</div>
|
||||
<div class="col-md-3 titel">lid</div>
|
||||
<div class="col-md-3 titel">rapportages</div>
|
||||
</div>
|
||||
<div class="management-block" v-for="member in members" :key="member.formal_name" >
|
||||
<div class="header row">
|
||||
<div class="col-md-2 titel">{{ member.id }}</div>
|
||||
<div class="col-md-3 titel">{{ member.formal_name }}</div>
|
||||
<div class="col-md-3 titel">{{ member.management_links.length }}</div>
|
||||
</div>
|
||||
|
||||
<div class="body row">
|
||||
<div class="link col-md-12" v-for="link in member.management_links" :key="link.url">
|
||||
<div class="flex space-between h-50 space-between">
|
||||
<div>
|
||||
<strong class="titel">Rapportage</strong>
|
||||
<span class="titel">{{member.formal_name}} (#{{member.id}})</span>
|
||||
</div>
|
||||
<div>
|
||||
<button class="delete" @click="deleteLink(link.id)">
|
||||
<span class="icon-close"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form row">
|
||||
<div class="col-md-4 flex flex-col">
|
||||
<label>
|
||||
<b class="titel">Titel</b>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
class="link"
|
||||
:value="link.title"
|
||||
@change="setField($event, link.id, 'title')"
|
||||
/>
|
||||
|
||||
<!-- <button class="demo" @click="openWindow(link.url)">
|
||||
<span class="icon-download"></span>
|
||||
</button>-->
|
||||
</div>
|
||||
<div class="col-md-4 flex flex-col">
|
||||
<label>
|
||||
<b class="url">Koppeling url</b>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
class="link"
|
||||
:value="link.url"
|
||||
@change="setField($event, link.id, 'url')"
|
||||
/>
|
||||
</div>
|
||||
<!-- <input type="text" :value="link.target" @change="setField($event, link.id, 'target')" /><br />-->
|
||||
<div class="col-md-4">
|
||||
<div class="flex flex-col p-0">
|
||||
<label>
|
||||
<b class="display">Weergave</b>
|
||||
</label>
|
||||
<div class="flex flex-row p-0">
|
||||
<div class="col-md-6 p-0">
|
||||
<label>
|
||||
<input
|
||||
class="radio"
|
||||
type="radio"
|
||||
value="self"
|
||||
@change="setField($event, link.id, 'target')"
|
||||
:checked="link.target === 'self'"
|
||||
/>
|
||||
IFrame
|
||||
</label>
|
||||
</div>
|
||||
<div class="col-md-6 p-0">
|
||||
<label>
|
||||
<input
|
||||
class="radio"
|
||||
type="radio"
|
||||
value="blank"
|
||||
@change="setField($event, link.id, 'target')"
|
||||
:checked="link.target === 'blank'"
|
||||
/>
|
||||
Externe link
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button @click="addLink(member.id)">
|
||||
<span class="icon-add"></span>
|
||||
<b class="toevoegen">Nog een rapportage koppeling toevoegen</b>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<v-footer
|
||||
fixed
|
||||
style="z-index: 4"
|
||||
height="90"
|
||||
color="primary"
|
||||
v-if="$store.getters.isAdmin || $store.getters.isOperator"
|
||||
>
|
||||
<v-btn text nuxt :to="localePath('/manager')">
|
||||
<v-icon>icon-arrow-left</v-icon>
|
||||
</v-btn>
|
||||
<div class="mx-10"></div>
|
||||
|
||||
<v-spacer />
|
||||
</v-footer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
<script>
|
||||
import Util from '@/util'
|
||||
import PageHeader from '~/components/UI/PageHeader/PageHeader'
|
||||
|
||||
export default {
|
||||
layout: `${process.env.CUSTOMER}Admin`,
|
||||
middleware: 'allowSuperAdminOrAdmin',
|
||||
|
||||
components: {
|
||||
PageHeader,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
selector: 'published',
|
||||
showAddButton: false,
|
||||
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
members() {
|
||||
return this.$store.getters['members/membersFiltered']
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
async addLink(memberId) {
|
||||
this.$nextTick(() => this.$nuxt.$loading.start())
|
||||
try {
|
||||
await this.$store.dispatch('members/addManagementLink', memberId)
|
||||
this.$nuxt.$loading.finish()
|
||||
} catch (error) {
|
||||
console.log('addLink -> error', error)
|
||||
this.$nuxt.$loading.finish()
|
||||
}
|
||||
},
|
||||
async setField(e, linkId, target) {
|
||||
let data = { value: e.target.value, link_id: linkId, field: target }
|
||||
try {
|
||||
await this.$store.dispatch('members/changeManagementLink', data)
|
||||
} catch (error) {
|
||||
console.log('addLink -> error', error)
|
||||
}
|
||||
},
|
||||
async deleteLink(linkId) {
|
||||
try {
|
||||
await this.$store.dispatch('members/deleteManagementLink', linkId)
|
||||
} catch (error) {
|
||||
console.log('deleteLink -> error', error)
|
||||
}
|
||||
},
|
||||
openWindow(url) {
|
||||
window.open(url, '_blank').focus()
|
||||
},
|
||||
|
||||
openBlock(){
|
||||
this.showAddButton = !this.showAddButton;
|
||||
}
|
||||
},
|
||||
|
||||
async asyncData({ $axios, store }) {
|
||||
try {
|
||||
if (!store.getters['members/hasMembers']) {
|
||||
await store.dispatch('members/pullData')
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('Data -> error', error)
|
||||
}
|
||||
},
|
||||
|
||||
async mounted() {},
|
||||
|
||||
async beforeRouteLeave(to, from, next) {
|
||||
next()
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.management-block-container {
|
||||
background-color: var(--v-primary-base);
|
||||
border-radius: 5px;
|
||||
padding: 20px 40px;
|
||||
.flex {
|
||||
display: flex;
|
||||
}
|
||||
.flex-col {
|
||||
flex-direction: column;
|
||||
}
|
||||
.flex-row {
|
||||
flex-direction: row;
|
||||
}
|
||||
.p-0 {
|
||||
padding: 0;
|
||||
}
|
||||
.h-50 {
|
||||
height: 50px;
|
||||
}
|
||||
.space-between {
|
||||
justify-content: space-between;
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
span.icon-add {
|
||||
position: relative;
|
||||
top: 1px;
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
.titel,
|
||||
.url,
|
||||
.display,
|
||||
.toevoegen,
|
||||
.icon-add{
|
||||
color: var(--v-txt-base);
|
||||
}
|
||||
}
|
||||
|
||||
.header-top {
|
||||
font-size: 14px;
|
||||
color: var(--v-txt-base);
|
||||
font-weight: bold;
|
||||
margin-bottom: 15px;
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.header {
|
||||
padding: 10px;
|
||||
border: 1px solid var(--v-lines-base);
|
||||
}
|
||||
|
||||
.body {
|
||||
// border-bottom: 1px solid #eef7f9;
|
||||
// border-left: 1px solid #eef7f9;
|
||||
// border-right: 1px solid #eef7f9;
|
||||
|
||||
border: 1px solid var(--v-lines-base);
|
||||
border-top: unset;
|
||||
padding: 25px 10px;
|
||||
margin-bottom: 35px;
|
||||
}
|
||||
|
||||
.form {
|
||||
margin-top: 15px;
|
||||
margin-bottom: 35px;
|
||||
}
|
||||
|
||||
.form div {
|
||||
padding-top: 10px;
|
||||
}
|
||||
.form div {
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.form {
|
||||
background-color: var(--v-secondary-base);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.form input {
|
||||
width: calc(100% - 30px);
|
||||
background-color: var(--v-primary-base);
|
||||
color: var(--v-txt-base);
|
||||
border-radius: 5px;
|
||||
padding: 4px;
|
||||
border: 1px solid var(--v-lines-base);
|
||||
}
|
||||
|
||||
.form input.link {
|
||||
width: calc(100% - 46px);
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.form label {
|
||||
display: block;
|
||||
line-height: 2.5;
|
||||
}
|
||||
|
||||
button {
|
||||
width: 100%;
|
||||
margin: 15px auto 30px auto;
|
||||
padding-top: 20px;
|
||||
padding-bottom: 20px;
|
||||
text-align: center;
|
||||
border: 2px dashed var(--v-lines-base);
|
||||
}
|
||||
|
||||
.delete {
|
||||
right: -40px;
|
||||
top: 8px;
|
||||
border: 0px solid transparent;
|
||||
width: auto;
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
.demo {
|
||||
border: 0px solid transparent;
|
||||
width: auto;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.form input.radio {
|
||||
width: 30px;
|
||||
display: inline-block;
|
||||
}
|
||||
</style>
|
||||
200
pages/manager/members/report.vue
Normal file
200
pages/manager/members/report.vue
Normal file
@@ -0,0 +1,200 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="pa-6">
|
||||
<page-header class="d-flex justify-space-between">
|
||||
<div class="subtitle-1">
|
||||
<span class="mr-4 display-1"> Rapportage </span>
|
||||
</div>
|
||||
</page-header>
|
||||
</div>
|
||||
<v-row>
|
||||
<v-col>
|
||||
<v-data-table
|
||||
:headers="headers"
|
||||
:items="managementLinks"
|
||||
:search="search"
|
||||
item-key="informal_name"
|
||||
hide-default-footer
|
||||
:pagination="settings"
|
||||
>
|
||||
<template v-slot:item.title="{ item }">
|
||||
<a
|
||||
v-if="item.target === 'blank'"
|
||||
:href="item.url"
|
||||
@click="setActiveLink(item)"
|
||||
target="_BLANK"
|
||||
class="ma-4 dot"
|
||||
:class="{ 'accent--text activeTab': activeLink === item }"
|
||||
>{{ item.title || item.id }}</a
|
||||
>
|
||||
<a
|
||||
v-else
|
||||
@click="setActiveLink(item)"
|
||||
class="ma-4 dot"
|
||||
:class="{ 'accent--text activeTab': activeLink === item }"
|
||||
>{{ item.title || item.id }}</a
|
||||
>
|
||||
</template>
|
||||
|
||||
<template v-slot:item.updated_at="{ item }">
|
||||
{{ formatDate(item.updated_at) }}
|
||||
</template>
|
||||
</v-data-table>
|
||||
|
||||
<div
|
||||
class="frame"
|
||||
v-if="activeLink.url && activeLink.target === 'self'"
|
||||
>
|
||||
<iframe
|
||||
:src="activeLink.url"
|
||||
width="100%"
|
||||
frameBorder="0"
|
||||
height="800px"
|
||||
/>
|
||||
</div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-footer fixed style="z-index: 4" height="90" color="primary">
|
||||
<v-btn text nuxt :to="localePath('/manager')">
|
||||
<v-icon>icon-arrow-left</v-icon>
|
||||
</v-btn>
|
||||
<div class="mx-10"></div>
|
||||
<v-spacer />
|
||||
</v-footer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
<script>
|
||||
import dayjs from 'dayjs'
|
||||
import PageHeader from '~/components/UI/PageHeader/PageHeader'
|
||||
|
||||
export default {
|
||||
layout: `${process.env.CUSTOMER}Admin`,
|
||||
middleware: 'adminsOrOperatorsOrMemberEditors',
|
||||
|
||||
components: {
|
||||
PageHeader,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
activeLink: {
|
||||
id: 0,
|
||||
url: '',
|
||||
title: '',
|
||||
target: '',
|
||||
},
|
||||
dialog: false,
|
||||
search: '',
|
||||
headers: [
|
||||
{ text: 'titel', value: 'title' },
|
||||
{
|
||||
text: 'leden',
|
||||
align: 'start',
|
||||
sortable: true,
|
||||
value: 'informal_name',
|
||||
},
|
||||
{ text: 'bijgewerkt', value: 'updated_at' },
|
||||
// { text: 'links', value: 'management_links', sortable: false },
|
||||
],
|
||||
settings: {
|
||||
itemsPerPage: 5,
|
||||
},
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
members() {
|
||||
return this.$store.getters['members/membersFiltered']
|
||||
},
|
||||
|
||||
managementLinks() {
|
||||
const links = []
|
||||
|
||||
this.members.forEach((member) => {
|
||||
member.management_links.forEach((link) => {
|
||||
links.push({ ...link, informal_name: member.informal_name })
|
||||
})
|
||||
})
|
||||
|
||||
return links
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
setActiveLink(link) {
|
||||
this.activeLink = link
|
||||
this.dialog = true
|
||||
},
|
||||
formatDate(date) {
|
||||
return dayjs(date).format('D MMM YYYY, HH:mm').toLowerCase()
|
||||
},
|
||||
},
|
||||
|
||||
async asyncData({ $axios, store }) {
|
||||
try {
|
||||
if (!store.getters['members/hasMembers']) {
|
||||
await store.dispatch('members/pullData')
|
||||
this.activeLink = this.members[0]
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('Data -> error', error)
|
||||
}
|
||||
},
|
||||
}
|
||||
</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;
|
||||
color: #333333;
|
||||
font-weight: bold;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
text-decoration: none;
|
||||
}
|
||||
.dot::before {
|
||||
content: '\A';
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
border-radius: 50%;
|
||||
background: #e54e0f;
|
||||
display: block;
|
||||
position: relative;
|
||||
top: 35px;
|
||||
left: 50%;
|
||||
opacity: 0;
|
||||
}
|
||||
.dot:hover::before {
|
||||
opacity: 0.5 !important;
|
||||
}
|
||||
.dot.activeTab::before {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
|
||||
.frame {
|
||||
margin-top: 35px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user