- 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:
328
pages/manager/learning/index.vue
Normal file
328
pages/manager/learning/index.vue
Normal file
@@ -0,0 +1,328 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="pa-lg-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-lg-4"
|
||||
>
|
||||
{{ $t('learning.products') }}
|
||||
<small class="font-weight-light">({{ published.length }})</small>
|
||||
</span>
|
||||
|
||||
<span
|
||||
:class="{ 'display-1': selector === 'drafts' }"
|
||||
:style="{ cursor: 'pointer' }"
|
||||
@click="selector = 'drafts'"
|
||||
class="mx-lg-4"
|
||||
v-if="$store.getters.isAdmin || $store.getters.isOperator"
|
||||
>
|
||||
{{ $t('learning.drafts') }}
|
||||
<small class="font-weight-light">({{ drafts.length }})</small>
|
||||
</span>
|
||||
<span
|
||||
:class="{ 'display-1': selector === 'deleted' }"
|
||||
:style="{ cursor: 'pointer' }"
|
||||
@click="selector = 'deleted'"
|
||||
class="mx-lg-4"
|
||||
v-if="$store.getters.isAdmin || $store.getters.isOperator"
|
||||
>
|
||||
{{ $t('general.deleted') }}
|
||||
<small class="font-weight-light">({{ deleted.length }})</small>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<settings />
|
||||
|
||||
<v-btn
|
||||
color="accent"
|
||||
depressed
|
||||
rounded
|
||||
to="/manager/learning/new"
|
||||
v-if="$store.getters.isAdmin || $store.getters.isOperator"
|
||||
>
|
||||
<v-icon left x-small>icon-add</v-icon>
|
||||
<small>{{ $t('general.add') }}</small>
|
||||
</v-btn>
|
||||
</div>
|
||||
</page-header>
|
||||
|
||||
<div class="d-flex justify-space-between">
|
||||
<filters />
|
||||
<filters-selected />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<courses-table
|
||||
:deleted="deleted"
|
||||
:drafts="drafts"
|
||||
:published="published"
|
||||
:selector="selector"
|
||||
@reload-learning-products="getProducts()"
|
||||
/>
|
||||
|
||||
<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/learning')">
|
||||
<v-icon>icon-arrow-left</v-icon>
|
||||
</v-btn>
|
||||
<div class="mx-10">
|
||||
<v-btn
|
||||
color="accent"
|
||||
depressed
|
||||
rounded
|
||||
to="/manager/learning/new"
|
||||
class="ml-10"
|
||||
>
|
||||
<v-icon left size="8">icon-add</v-icon>
|
||||
<small>{{ $t('general.add') }}</small>
|
||||
</v-btn>
|
||||
</div>
|
||||
|
||||
<v-spacer />
|
||||
|
||||
<!-- <v-btn
|
||||
class="ma-2"
|
||||
tile
|
||||
text
|
||||
small
|
||||
color="success"
|
||||
nuxt
|
||||
target="_blank"
|
||||
:to="`${this.localePath(
|
||||
'/manager/learning'
|
||||
)}?filters=${sharedUrlSuffix}`"
|
||||
id="url_to_share"
|
||||
v-show="showCopiedButton"
|
||||
>
|
||||
<v-icon class="mx-2" small>icon-selectionbox-checked</v-icon>
|
||||
URL gekopieerd
|
||||
</v-btn>
|
||||
|
||||
<v-tooltip top v-if="!showCopiedButton">
|
||||
<template v-slot:activator="{ on, attrs }">
|
||||
<v-btn
|
||||
class="ma-2"
|
||||
tile
|
||||
text
|
||||
small
|
||||
@click="copyUrl"
|
||||
v-bind="attrs"
|
||||
v-on="on"
|
||||
>
|
||||
<v-icon class="mx-2" small>icon-share</v-icon>
|
||||
Delen Lijst
|
||||
</v-btn>
|
||||
</template>
|
||||
<span> {{ $t('general.tooltip_share') }}</span>
|
||||
</v-tooltip> -->
|
||||
|
||||
<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>
|
||||
<settings class="ml-10" />
|
||||
</v-footer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Util from '@/util'
|
||||
import PageHeader from '~/components/UI/PageHeader/PageHeader'
|
||||
import Settings from '~/components/Learning/Settings'
|
||||
import Filters from '~/components/Learning/Filters'
|
||||
import FiltersSelected from '~/components/Learning/FiltersSelected'
|
||||
import CoursesTable from '~/components/Learning/CoursesTable'
|
||||
|
||||
export default {
|
||||
layout: `${process.env.CUSTOMER}Admin`,
|
||||
middleware: ['denyToOnlyMembers'],
|
||||
components: {
|
||||
PageHeader,
|
||||
Settings,
|
||||
Filters,
|
||||
CoursesTable,
|
||||
FiltersSelected,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
selector: 'published',
|
||||
showCopiedButton: false,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
learningProducts() {
|
||||
return this.$store.getters.learningProducts
|
||||
},
|
||||
hasProducts() {
|
||||
return this.$store.getters.hasLearningProducts
|
||||
},
|
||||
|
||||
published() {
|
||||
return this.learningProductsFiltered.filter(
|
||||
(product) => product.published && !product.deleted_at
|
||||
)
|
||||
},
|
||||
drafts() {
|
||||
return this.learningProductsFiltered.filter(
|
||||
(product) => !product.deleted_at && !product.published
|
||||
)
|
||||
},
|
||||
deleted() {
|
||||
return this.learningProductsFiltered.filter(
|
||||
(product) => product.deleted_at
|
||||
)
|
||||
},
|
||||
filtersSelected() {
|
||||
return this.$store.getters.filtersSelected
|
||||
},
|
||||
learningProductsFiltered() {
|
||||
if (!this.filtersSelected.length > 0) return this.learningProducts
|
||||
|
||||
return this.learningProducts.filter((product) => {
|
||||
return Util.findItemsWithExactValuesInArray(
|
||||
this.filtersSelected,
|
||||
product.filterItemsSelected
|
||||
)
|
||||
})
|
||||
},
|
||||
|
||||
// sharedUrlSuffix() {
|
||||
// let filtersSelectedIdsStringyfied = ''
|
||||
// this.filtersSelected.map(
|
||||
// (id) => (filtersSelectedIdsStringyfied += `${id},`)
|
||||
// )
|
||||
// return filtersSelectedIdsStringyfied
|
||||
// },
|
||||
// sharedUrlParsed() {
|
||||
// // if is a shared Url
|
||||
// if (!this.$route.query.filters) return null
|
||||
|
||||
// // parse content, convert every element to number
|
||||
// let filterItemIds = this.$route.query.filters.split(',').map(Number)
|
||||
|
||||
// // validate it and return
|
||||
// return filterItemIds.filter(
|
||||
// (el) => !isNaN(el) && this.$store.getters.filtersItemsMap.has(el)
|
||||
// )
|
||||
// },
|
||||
},
|
||||
|
||||
async asyncData({ $axios, store }) {
|
||||
try {
|
||||
if (!store.getters.hasLearningProducts) {
|
||||
await store.dispatch('learning/pullProducts')
|
||||
}
|
||||
|
||||
const response = await $axios.get('/filters')
|
||||
const filters = response.data
|
||||
await store.commit('learning/SORT_FILTERS', filters)
|
||||
} catch (error) {
|
||||
console.log('Data -> error', error)
|
||||
}
|
||||
},
|
||||
|
||||
async mounted() {
|
||||
if (this.sharedUrlParsed) {
|
||||
this.$store.commit('learning/SELECT_FILTERS', this.sharedUrlParsed)
|
||||
}
|
||||
|
||||
// If filters sorting preferences saved in localstorage
|
||||
if (localStorage.getItem('learning_products_filters')) {
|
||||
// Get them
|
||||
const filtersOrder = JSON.parse(
|
||||
localStorage.getItem('learning_products_filters')
|
||||
)
|
||||
|
||||
// Get filters from store
|
||||
const filters = [...this.$store.state.learning.filters]
|
||||
|
||||
// Sort by preferences
|
||||
const filtersSorted = filters.sort(
|
||||
(a, b) => filtersOrder.indexOf(a.id) - filtersOrder.indexOf(b.id)
|
||||
)
|
||||
|
||||
// send to store, sorted
|
||||
await this.$store.commit('learning/SORT_FILTERS', filtersSorted)
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
async getProducts() {
|
||||
try {
|
||||
const response = await this.$axios.get('/learning-products')
|
||||
await this.$store.commit('learning/STORE_PRODUCTS', response.data)
|
||||
} catch (error) {
|
||||
console.log('getProducts -> error', error)
|
||||
}
|
||||
},
|
||||
async copyUrl() {
|
||||
if (this.showCopiedButton) return
|
||||
|
||||
// Create <a> link with v-show false and Copy from there to have the full path?
|
||||
const url = document.getElementById('url_to_share').href
|
||||
|
||||
try {
|
||||
await navigator.clipboard.writeText(url)
|
||||
|
||||
this.showCopiedButton = true
|
||||
setTimeout(() => (this.showCopiedButton = false), 5000)
|
||||
} catch (error) {
|
||||
console.log('copyUrl -> error', error)
|
||||
}
|
||||
},
|
||||
|
||||
exportCsv() {
|
||||
const data = [...this.learningProductsFiltered]
|
||||
|
||||
const headersNeeded = [
|
||||
'title',
|
||||
'code',
|
||||
'partner',
|
||||
'owner',
|
||||
'status',
|
||||
'lead_time',
|
||||
'product_type',
|
||||
'theme',
|
||||
'course',
|
||||
]
|
||||
|
||||
const dataFiltered = this.$store.getters[
|
||||
'utils/filterArrayObjsByArrayOfProperties'
|
||||
](data, headersNeeded)
|
||||
|
||||
let dataCsv =
|
||||
this.$store.getters['utils/arrayOfObjectToCsv'](dataFiltered)
|
||||
|
||||
// Translate every entry in 'headersNeeded'
|
||||
let headersTranslated =
|
||||
headersNeeded.map((h) => this.$t(`csv.learning.${h}`)).join(',') + '\n'
|
||||
|
||||
// Remove 1st row (string)
|
||||
dataCsv = dataCsv.split('\n').slice(1).join('\n')
|
||||
|
||||
// Add as 1st row the one translated
|
||||
dataCsv = headersTranslated + dataCsv
|
||||
|
||||
// Simulate click to download file
|
||||
const universalBOM = '\uFEFF'
|
||||
const blob = new Blob([universalBOM + dataCsv], { type: 'text/csv' })
|
||||
const link = document.createElement('a')
|
||||
link.href = URL.createObjectURL(blob)
|
||||
link.setAttribute('hidden', '')
|
||||
link.setAttribute('download', 'producten.csv')
|
||||
link.click()
|
||||
URL.revokeObjectURL(link.href)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user