Initial Nuxt frontend import
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
This commit is contained in:
Joris Slagter
2025-12-02 17:48:48 +01:00
parent 0f691e83e3
commit 791aebc346
290 changed files with 113801 additions and 0 deletions

View File

@@ -0,0 +1,310 @@
<template>
<div>
<v-data-table
:headers="headers"
:items-per-page="10"
:items="members"
class="pa-4 secondary"
>
<template v-slot:item.informal_name="{ item }">
<v-img
:alt="item.informal_name"
lazy-src="/images/mijnggz-placeholder-members.png"
:src="computeImage(item)"
contain
height="60px"
width="110px"
/>
{{ item.informal_name }}
</template>
<template v-slot:item.main_branch="{ item }">
{{ getBranchTitleById(item.branch_id) }}
</template>
<template v-slot:item.start_membership="{ item }">
{{ formatDate(item.start_membership || item.created_at) }}
</template>
<template v-slot:item.updated_at="{ item }">
{{ formatDate(item.updated_at) }}
</template>
<template v-slot:item.changes="{ item }">
<v-icon
v-if="item.revision"
:color="item.revision.hasChanges ? 'accent' : 'success'"
>{{
item.revision.hasChanges
? 'mdi-alert-circle-outline'
: 'icon-checkmark'
}}</v-icon
>
</template>
<template v-slot:item.actions="{ item }">
<div class="view-container">
<v-btn
class="mx-4 white--text"
style="height: 100%"
:color="$vuetify.theme.dark ? 'info' : 'txt'"
rounded
depressed
nuxt
small
:to="localePath(`/manager/members/${item.slug}`)"
>{{ $t('general.view') }}</v-btn
>
<v-menu
offset-y
v-if="canEditMember"
>
<template v-slot:activator="{ on }">
<v-hover v-slot:default="{ hover }">
<v-btn
:color="hover ? 'info' : ''"
:outlined="hover"
depressed
fab
small
v-on="on"
class="menu-btn"
>
<v-icon>icon-options</v-icon>
</v-btn>
</v-hover>
</template>
<v-list width="200">
<v-list-item
:to="localePath(`/manager/members/${item.slug}?edit`)"
nuxt
>
<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="dialog">
<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.product_overview.delete_confirmation', {
productName: item.informal_name,
})
}}
</v-card-title>
<v-card-actions>
<div class="ma-4">
<v-btn
@click="deleteMember(item.id)"
class="mx-2"
color="accent"
depressed
rounded
>{{ $t('general.delete') }}</v-btn
>
<v-btn
@click="close"
class="mx-2"
color="info"
depressed
rounded
>{{ $t('general.cancel') }}</v-btn
>
</div>
</v-card-actions>
</v-card>
</v-dialog>
</v-list>
</v-menu>
</div>
</template>
</v-data-table>
</div>
</template>
<script>
import dayjs from 'dayjs'
export default {
props: {
members: {
type: Array,
required: true,
},
},
data() {
return {
dialog: false,
page: 1,
headers: [
{
text: '#',
value: 'id',
},
{
text: this.$t('members.table.headers.member'),
value: 'informal_name',
// sortable: false,
},
{
text: this.$t('members.table.headers.type'),
value: 'type',
},
{
text: this.$t('members.table.headers.industry'),
value: 'main_branch',
},
{
text: this.$t('members.table.headers.location'),
value: 'info_city',
},
{
text: this.$t('members.table.headers.since'),
value: 'start_membership',
},
{
text: this.$t('members.table.headers.updated'),
value: 'updated_at',
},
{
text: this.$t('members.table.headers.check'),
value: 'changes',
},
{
text: this.$t('members.table.headers.action'),
value: 'actions',
},
],
}
},
watch: {
dialog(val) {
val || this.close()
},
},
computed: {
canEditMember() {
return this.$store.getters['members/isSuperAdminAdminOrDelegated']
},
},
methods: {
close() {
this.dialog = false
},
async deleteMember(memberId) {
if (!memberId) return
this.dialog = false
await this.$store.dispatch('members/deleteMember', memberId)
this.$router.push(this.localePath('/manager/members'))
},
formatDate(date) {
return dayjs(date).format('D MMM YYYY').toLowerCase()
},
computeImage(item) {
if (item.logo.thumb) return item.logo.thumb
return '/images/mijnggz-placeholder-members.png'
},
getBranchTitleById(id) {
const branch = this.$store.getters['members/getBranchById'](id);
return branch ? branch.title : '';
},
},
}
</script>
<style scoped>
.view-container{
display:flex;
}
table tr a.v-btn {
opacity: 0;
}
table tr:hover a.v-btn {
opacity: 1;
}
.v-list-item:hover >>> .v-list-item__icon i,
.v-list-item:hover >>> .v-list-item__content .v-list-item__subtitle {
color: var(--v-info-base) !important;
}
.v-data-table >>> .v-data-table-header th:last-child,
.v-data-table >>> .v-data-table-header th:nth-child(3) {
position: relative;
}
.v-data-table >>> .v-data-table-header th:last-child::before,
.v-data-table >>> .v-data-table-header th:nth-child(3)::before {
content: '';
position: absolute;
left: -1px;
height: 20px;
top: 50%;
transform: translateY(-50%);
border-left: 1px solid rgba(0, 0, 0, 0.12) !important;
}
.v-data-table >>> .v-data-table-header tr th {
border-bottom: none !important;
border-top: 1px solid rgba(0, 0, 0, 0.12);
}
.v-data-table td .menu-btn {
background-color: unset !important;
}
.v-data-table >>> td {
color: var(--v-txt-base);
}
.v-data-table >>> th {
white-space: nowrap;
}
.v-data-table >>> td:first-child {
padding-right: 0px !important;
}
.v-data-table >>> td:first-child .v-image {
margin-right: -18px;
}
.v-data-table >>> td:last-child,
.v-data-table >>> td:nth-child(3) {
position: relative;
}
.v-data-table >>> td:last-child::before,
.v-data-table >>> td:nth-child(3)::before {
content: '';
position: absolute;
left: -1px;
height: 60px;
top: 50%;
transform: translateY(-50%);
border-left: 1px solid rgba(0, 0, 0, 0.12) !important;
}
.v-data-table >>> .v-data-table__divider {
border-right: none !important;
}
</style>