- 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:
207
components/UI/SearchBar/SearchBar.vue
Normal file
207
components/UI/SearchBar/SearchBar.vue
Normal 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>
|
||||
Reference in New Issue
Block a user