Files
nuxt-frontend/pages/manager/learning/index.vue
Joris Slagter 791aebc346
Some checks failed
continuous-integration/drone/push Build is failing
Initial Nuxt frontend import
- 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
2025-12-02 17:48:48 +01:00

329 lines
9.0 KiB
Vue

<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>