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,207 @@
<template>
<v-autocomplete
v-if="isNotMember()"
:items="isMemberPage ? members : learningProducts"
:item-text="isMemberPage ? 'informal_name' : 'title'"
filled
dense
id="searchbar"
rounded
:search-input.sync="search"
v-model="selected"
append-icon="icon-search"
hide-details
:placeholder="$t('general.search')"
single-line
full-width
flat
return-object
class="mx-4"
:filter="customFilter"
@focus="toDoOnFocus"
@blur="switchOverlay(false)"
@keydown="switchOverlay(true)"
@click="switchOverlay(true)"
>
<template v-slot:prepend-item v-if="prependText">
<v-list-item>
<v-list-item-title class="header-search-title pb-4">
<span class="pt-4">
{{ prependText }}
</span>
</v-list-item-title>
</v-list-item>
</template>
</v-autocomplete>
</template>
<script>
import Util from '@/util'
var Diacritics = require('diacritic')
export default {
data() {
return {
search: '',
loading: false,
selected: null,
}
},
computed: {
learningProducts() {
return this.$store.getters.learningProducts.filter((p) => {
if (p.parent_id) return false
if (p.deleted_at) return false
if (!p.published) return false
return true
})
},
isMemberPage() {
return this.$route.name.includes('manager-members')
},
members() {
return this.$store.getters['members/membersFiltered']
},
hasMembers() {
return this.members?.length > 0
},
prependText() {
if (this.loading) return this.$t('general.loading')
if (this.isMemberPage) {
if (!this.hasMembers) return `No results found`
if (this.search) return `Zoeksuggesties voor '${this.search}'`
}
if (!this.$store.getters.hasLearningProducts) {
return `No results found`
}
if (this.$store.getters.hasLearningProducts && this.search) {
return `Zoeksuggesties voor '${this.search}'`
}
return null
},
},
watch: {
async selected(item) {
if (!item || !item.slug) return
const route = this.isMemberPage
? `/manager/members/${item.slug}`
: `/manager/learning/${item.slug}`
await this.$router.push(this.localePath(route))
this.$store.commit('navigation/TOGGLE_SEARCH_OVERLAY', false)
this.search = null
this.selected = null
this.loading = false
},
},
methods: {
customFilter(item, queryText, itemText) {
const textOne = Diacritics.clean(
item[!this.isMemberPage ? 'title' : 'informal_name'].toLowerCase()
)
let textTwo = ''
if (!this.isMemberPage) {
if (item.synonyms.length > 0) {
item.synonyms.forEach((s) => (textTwo += `${s.title.toLowerCase()} `))
}
}
const searchText = queryText.toLowerCase()
return (
textOne.indexOf(Diacritics.clean(searchText)) > -1 ||
textTwo.indexOf(Diacritics.clean(searchText)) > -1
)
},
async toDoOnFocus() {
if (this.isMemberPage && !this.hasMembers) {
this.$nextTick(() => this.$nuxt.$loading.start())
this.loading = true
await this.$store.dispatch('members/pullData')
await this.$nuxt.$loading.finish()
this.loading = false
}
if (!this.isMemberPage && !this.$store.getters.hasLearningProducts) {
this.$nextTick(() => this.$nuxt.$loading.start())
this.loading = true
await this.$store.dispatch('learning/pullProducts')
await this.$nuxt.$loading.finish()
this.loading = false
}
this.$store.commit('navigation/TOGGLE_SEARCH_OVERLAY', true)
},
switchOverlay(value) {
if (value == this.$store.getters.searchOverlay) return
this.$store.commit('navigation/TOGGLE_SEARCH_OVERLAY', value)
},
isNotMember() {
const roles = this.$auth.user.roles.map(({ name }) => name)
return !Util.findCommonValuesInArray(roles, ['member'])
},
},
}
</script>
<style scoped>
#searchbar {
width: 400px !important;
color: var(--v-txt-base) !important;
}
.v-input >>> textarea {
color: var(--v-txt-base) !important;
}
#searchbar >>> .icon-search {
color: var(--v-txt-base) !important;
font-weight: 600;
}
#searchbar >>> .v-text-field--outlined fieldset {
color: rgba(255, 255, 255, 0);
}
#searchbar
>>> .v-select.v-select--is-menu-active
.v-input__icon--append
.v-icon {
transform: unset !important;
}
.v-list {
padding: 30px 90px 30px 100px !important;
border-radius: 0px;
}
@media only screen and (min-width: 1536px) {
.v-list {
padding: 30px 80px 30px 53px !important;
}
}
.v-list:focus {
outline: none !important;
}
.v-list >>> .v-list-item {
color: var(--v-tertiary-base) !important;
background: var(--v-primary-base) !important;
}
.v-list >>> .v-list-item__title {
font-size: 16px;
color: var(--v-tab-base);
}
.header-search-title {
pointer-events: none;
cursor: initial;
font-size: 24px !important;
height: 22px !important;
color: var(--v-txt-base) !important;
font-weight: 700;
border-bottom: 2px solid var(--v-search-base);
padding-bottom: 15px;
font-weight: 700;
}
</style>