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,272 @@
<template>
<v-row>
<v-col>
<div class="d-flex justify-space-between">
<h2 class="ml-6 txt--text">{{ title }}</h2>
<current-date-time showIcon class="mr-6" />
</div>
<accordion-card disableAccordion>
<v-row>
<v-col cols="12" sm="12" md="3">
<v-subheader class="ml-10 txt--text font-weight-black">
Kwaliteitsstandaard
</v-subheader>
</v-col>
<v-col cols="12" sm="12" md="6">
<v-text-field v-model="item.title" 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-subheader class="ml-10 txt--text font-weight-black">
Externe link
</v-subheader>
</v-col>
<v-col cols="12" sm="12" md="6">
<v-text-field
v-model="item.link"
outlined
prepend-inner-icon="icon-link"
>
</v-text-field>
</v-col>
<v-col cols="12" sm="12" md="3"> Voeg link toe</v-col>
</v-row>
</accordion-card>
</v-col>
<v-footer fixed style="z-index: 4" height="90" color="primary">
<v-btn text nuxt :to="localePath('/manager/learning/quality-standards')">
<v-icon>icon-arrow-left</v-icon>
</v-btn>
<v-btn
v-cloak
class="ma-2 white--text"
color="txt"
depressed
rounded
@click="save(true)"
v-if="canSave && !isNew"
>
Opslaan</v-btn
>
<v-btn
v-cloak
class="ma-2 white--text"
color="accent"
depressed
rounded
@click="save(false)"
v-if="canSave"
>
Opslaan en sluiten</v-btn
>
<v-spacer />
<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" 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: item.title,
})
}}
</v-card-title>
<v-card-actions>
<div class="ma-4">
<v-btn
color="accent"
class="mx-2"
@click="deleteItem(item.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-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'
export default {
layout: `${process.env.CUSTOMER}Admin`,
middleware: 'adminsOrOperators',
components: {
accordionCard,
PageHeader,
currentDateTime,
},
data() {
return {
remoteItem: {},
item: { title: null },
dialog: false,
}
},
mounted() {
if (this.isSlugNumber) this.getFilter(this.slug)
// if (!this.isNew) this.$router.push('/manager')
},
computed: {
slug() {
return this.$route.params.filter
},
isSlugNumber() {
return !isNaN(this.slug.split(['-'], 1))
},
isNew() {
return this.slug.toLowerCase() === 'new'
},
title() {
if (!this.item || !this.item.title) return 'Nieuw'
return this.item.title || ''
},
canSave() {
if (this.isNew && this.itemFilled) return true
if (this.itemFilled && this.itemHasChanges) return true
return false
},
itemFilled() {
if (!this.item || !this.item.title) return false
return true
},
itemHasChanges() {
return JSON.stringify(this.remoteItem) !== JSON.stringify(this.item)
},
},
methods: {
async getFilter(url) {
if (!this.isSlugNumber) return
this.$nextTick(() => this.$nuxt.$loading.start())
try {
const { data } = await this.$axios.get(`/filter-items/${url}`)
this.remoteItem = { ...data }
this.item = { ...data }
this.$nuxt.$loading.finish()
} catch (error) {
this.$nuxt.$loading.finish()
this.$notifier.showMessage({
content: `Error getting the filter: ` + error.message,
color: 'error',
icon: 'icon-message',
})
this.$router.push('/manager/learning/quality-standards/')
}
},
close() {
this.dialog = false
},
async save(stay = false) {
if (this.dialog) this.close()
this.$nextTick(() => this.$nuxt.$loading.start())
const QUALITY_STANDARD_ID = 14
const data = {
...this.item,
filter_id: this.item.filter_id || QUALITY_STANDARD_ID,
}
try {
await this.$axios.post(`/filter-items`, data)
this.remoteItem = { ...data }
this.$nuxt.$loading.finish()
if (!stay) this.$router.push('/manager/learning/quality-standards/')
} catch (error) {
this.$nuxt.$loading.finish()
this.$notifier.showMessage({
content: `Error creating the filter item: ` + error.message,
color: 'error',
icon: 'icon-message',
})
}
},
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(`/filter-items/${itemId}`)
this.dialog = false
this.$nuxt.$loading.finish()
this.$notifier.showMessage({
content: `Filter Item deleted`,
color: 'success',
icon: 'mdi-delete',
})
this.$router.push('/manager/learning/quality-standards/')
} catch (error) {
this.dialog = false
this.$nuxt.$loading.finish()
this.$notifier.showMessage({
content: `Error trying to delete the selected Filter Item`,
color: 'error',
icon: 'mdi-delete',
})
this.$router.push('/manager/learning/quality-standards/')
}
},
},
}
</script>
<style lang="scss" scoped>
.v-dialog >>> .v-cardcard__title {
word-break: break-word;
}
</style>

View File

@@ -0,0 +1,202 @@
<template>
<div>
<page-header class="d-flex justify-space-between">
<span>
{{ $t('learning.quality_standards') }}
</span>
<v-btn
depressed
:to="localePath('/manager/learning/quality-standards/new')"
color="accent"
rounded
>
<v-icon left>mdi-plus</v-icon>
<small>{{ $t('general.add') }}</small>
</v-btn>
</page-header>
<v-data-table
:headers="headers"
:items="filter.items"
:items-per-page="10"
class="pa-4 secondary"
>
<!-- Translates dynamically headers -->
<template v-for="h in headers" v-slot:[`header.${h.value}`]="{ header }">
{{ $t(h.text) }}
</template>
<template v-slot:item.name="{ item }">
<strong class="txt--text" v-text="item.name"></strong>
</template>
<template v-slot:item.actions="{ item }">
<v-menu offset-y>
<template v-slot:activator="{ on }">
<v-hover v-slot:default="{ hover }">
<v-btn
v-on="on"
depressed
:outlined="hover"
small
fab
:color="hover ? 'info' : ''"
>
<v-icon>mdi-dots-horizontal</v-icon>
</v-btn>
</v-hover>
</template>
<v-list width="200">
<v-list-item
nuxt
:to="localePath(`/manager/learning/quality-standards/${item.id}`)"
>
<v-list-item-icon class="mr-1">
<v-icon small>mdi-pencil</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-list-item>
<v-list-item-icon class="mr-1">
<v-icon small>icon-link</v-icon>
</v-list-item-icon>
<v-list-item-content>
<v-list-item-subtitle>
Link naar informatie
</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.title,
})
}}
</v-card-title>
<v-card-actions>
<div class="ma-4">
<v-btn
@click="deleteItem(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>
</template>
</v-data-table>
</div>
</template>
<script>
import PageHeader from '~/components/UI/PageHeader/PageHeader'
import accordionCard from '@/components/UI/AccordionCard/AccordionCard'
export default {
layout: `${process.env.CUSTOMER}Admin`,
components: {
PageHeader,
accordionCard,
},
data() {
return {
headers: [
{ text: 'naam', sortable: false, value: 'title' },
{ text: '', value: 'actions' },
],
filter: {},
dialog: false,
}
},
async asyncData({ $axios }) {
try {
const { data } = await $axios.get(`/filters`)
const qualityStandards = data.find((f) => f.title === 'quality_standards')
return { filter: { ...qualityStandards } }
} catch (error) {
console.log(error)
}
},
methods: {
close() {
this.dialog = false
},
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(`/filter-items/${itemId}`)
this.dialog = false
this.filter.items = this.filter.items.filter((i) => i.id !== itemId)
this.$nuxt.$loading.finish()
this.$notifier.showMessage({
content: `Filter Item 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 Filter Item`,
color: 'error',
icon: 'mdi-delete',
})
}
},
},
}
</script>