|
|
@ -4,8 +4,8 @@ q-page.admin-navigation
|
|
|
|
.col-auto
|
|
|
|
.col-auto
|
|
|
|
img.admin-icon.animated.fadeInLeft(src='/_assets/icons/fluent-tree-structure.svg')
|
|
|
|
img.admin-icon.animated.fadeInLeft(src='/_assets/icons/fluent-tree-structure.svg')
|
|
|
|
.col.q-pl-md
|
|
|
|
.col.q-pl-md
|
|
|
|
.text-h5.text-primary.animated.fadeInLeft {{ $t('admin.navigation.title') }}
|
|
|
|
.text-h5.text-primary.animated.fadeInLeft {{ t('admin.navigation.title') }}
|
|
|
|
.text-subtitle1.text-grey.animated.fadeInLeft.wait-p2s {{ $t('admin.navigation.subtitle') }}
|
|
|
|
.text-subtitle1.text-grey.animated.fadeInLeft.wait-p2s {{ t('admin.navigation.subtitle') }}
|
|
|
|
.col-auto
|
|
|
|
.col-auto
|
|
|
|
q-btn.acrylic-btn.q-mr-sm(
|
|
|
|
q-btn.acrylic-btn.q-mr-sm(
|
|
|
|
icon='las la-question-circle'
|
|
|
|
icon='las la-question-circle'
|
|
|
@ -19,16 +19,16 @@ q-page.admin-navigation
|
|
|
|
icon='las la-redo-alt'
|
|
|
|
icon='las la-redo-alt'
|
|
|
|
flat
|
|
|
|
flat
|
|
|
|
color='secondary'
|
|
|
|
color='secondary'
|
|
|
|
:loading='loading > 0'
|
|
|
|
:loading='state.loading > 0'
|
|
|
|
@click='load'
|
|
|
|
@click='load'
|
|
|
|
)
|
|
|
|
)
|
|
|
|
q-btn(
|
|
|
|
q-btn(
|
|
|
|
unelevated
|
|
|
|
unelevated
|
|
|
|
icon='mdi-check'
|
|
|
|
icon='fa-solid fa-check'
|
|
|
|
:label='$t(`common.actions.apply`)'
|
|
|
|
:label='t(`common.actions.apply`)'
|
|
|
|
color='secondary'
|
|
|
|
color='secondary'
|
|
|
|
@click='save'
|
|
|
|
@click='save'
|
|
|
|
:disabled='loading > 0'
|
|
|
|
:disabled='state.loading > 0'
|
|
|
|
)
|
|
|
|
)
|
|
|
|
q-separator(inset)
|
|
|
|
q-separator(inset)
|
|
|
|
.row.q-pa-md.q-col-gutter-md
|
|
|
|
.row.q-pa-md.q-col-gutter-md
|
|
|
@ -306,36 +306,46 @@ q-page.admin-navigation
|
|
|
|
//- page-selector(mode='select', v-model='selectPageModal', :open-handler='selectPageHandle', path='home', :locale='currentLang')
|
|
|
|
//- page-selector(mode='select', v-model='selectPageModal', :open-handler='selectPageHandle', path='home', :locale='currentLang')
|
|
|
|
</template>
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
<script setup>
|
|
|
|
import _ from 'lodash'
|
|
|
|
|
|
|
|
import gql from 'graphql-tag'
|
|
|
|
import gql from 'graphql-tag'
|
|
|
|
|
|
|
|
import { find, intersectionBy, pull, unionBy } from 'lodash-es'
|
|
|
|
import { v4 as uuid } from 'uuid'
|
|
|
|
import { v4 as uuid } from 'uuid'
|
|
|
|
import { createMetaMixin } from 'quasar'
|
|
|
|
|
|
|
|
|
|
|
|
import { useI18n } from 'vue-i18n'
|
|
|
|
|
|
|
|
import { useMeta, useQuasar } from 'quasar'
|
|
|
|
|
|
|
|
import { computed, onMounted, reactive, watch, nextTick } from 'vue'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import { useAdminStore } from 'src/stores/admin'
|
|
|
|
|
|
|
|
import { useSiteStore } from 'src/stores/site'
|
|
|
|
|
|
|
|
|
|
|
|
import draggable from 'vuedraggable'
|
|
|
|
import draggable from 'vuedraggable'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// QUASAR
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const $q = useQuasar()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// STORES
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const adminStore = useAdminStore()
|
|
|
|
|
|
|
|
const siteStore = useSiteStore()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// I18N
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const { t } = useI18n()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// META
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
useMeta({
|
|
|
|
|
|
|
|
title: t('admin.navigation.title')
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// DATA
|
|
|
|
|
|
|
|
|
|
|
|
const siteConfig = { lang: 'en' }
|
|
|
|
const siteConfig = { lang: 'en' }
|
|
|
|
const siteLangs = [{ code: 'en' }]
|
|
|
|
const siteLangs = [{ code: 'en' }]
|
|
|
|
|
|
|
|
|
|
|
|
export default {
|
|
|
|
const state = reactive({
|
|
|
|
mixins: [
|
|
|
|
loading: 0,
|
|
|
|
createMetaMixin(function () {
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
|
|
title: this.$t('admin.navigation.title')
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
],
|
|
|
|
|
|
|
|
components: {
|
|
|
|
|
|
|
|
draggable
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
meta () {
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
|
|
title: this.$t('admin.navigation.title')
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
data () {
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
|
|
loading: false,
|
|
|
|
|
|
|
|
selectPageModal: false,
|
|
|
|
selectPageModal: false,
|
|
|
|
trees: [],
|
|
|
|
trees: [],
|
|
|
|
current: {},
|
|
|
|
current: {},
|
|
|
@ -347,54 +357,58 @@ export default {
|
|
|
|
},
|
|
|
|
},
|
|
|
|
allLocales: [],
|
|
|
|
allLocales: [],
|
|
|
|
copyFromLocaleCode: 'en'
|
|
|
|
copyFromLocaleCode: 'en'
|
|
|
|
}
|
|
|
|
})
|
|
|
|
},
|
|
|
|
|
|
|
|
computed: {
|
|
|
|
// COMPUTED
|
|
|
|
navTypes () {
|
|
|
|
|
|
|
|
return [
|
|
|
|
const navTypes = computed(() => ([
|
|
|
|
{ text: this.$t('navigation.navType.external'), value: 'external' },
|
|
|
|
{ text: t('navigation.navType.external'), value: 'external' },
|
|
|
|
{ text: this.$t('navigation.navType.externalblank'), value: 'externalblank' },
|
|
|
|
{ text: t('navigation.navType.externalblank'), value: 'externalblank' },
|
|
|
|
{ text: this.$t('navigation.navType.home'), value: 'home' },
|
|
|
|
{ text: t('navigation.navType.home'), value: 'home' },
|
|
|
|
{ text: this.$t('navigation.navType.page'), value: 'page' }
|
|
|
|
{ text: t('navigation.navType.page'), value: 'page' }
|
|
|
|
// { text: this.$t('navigation.navType.searchQuery'), value: 'search' }
|
|
|
|
// { text: t('navigation.navType.searchQuery'), value: 'search' }
|
|
|
|
]
|
|
|
|
]))
|
|
|
|
},
|
|
|
|
|
|
|
|
locales () {
|
|
|
|
const locales = computed(() => {
|
|
|
|
return _.intersectionBy(this.allLocales, _.unionBy(siteLangs, [{ code: 'en' }, { code: siteConfig.lang }], 'code'), 'code')
|
|
|
|
return intersectionBy(state.allLocales, unionBy(siteLangs, [{ code: 'en' }, { code: siteConfig.lang }], 'code'), 'code')
|
|
|
|
},
|
|
|
|
})
|
|
|
|
currentTree: {
|
|
|
|
|
|
|
|
|
|
|
|
const currentTree = computed({
|
|
|
|
get () {
|
|
|
|
get () {
|
|
|
|
return _.get(_.find(this.trees, ['locale', this.currentLang]), 'items', null) || []
|
|
|
|
return find(state.trees, ['locale', state.currentLang])?.items || []
|
|
|
|
},
|
|
|
|
},
|
|
|
|
set (val) {
|
|
|
|
set (val) {
|
|
|
|
const tree = _.find(this.trees, ['locale', this.currentLang])
|
|
|
|
const tree = find(state.trees, ['locale', state.currentLang])
|
|
|
|
if (tree) {
|
|
|
|
if (tree) {
|
|
|
|
tree.items = val
|
|
|
|
tree.items = val
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
this.trees = [...this.trees, {
|
|
|
|
state.trees = [...state.trees, {
|
|
|
|
locale: this.currentLang,
|
|
|
|
locale: state.currentLang,
|
|
|
|
items: val
|
|
|
|
items: val
|
|
|
|
}]
|
|
|
|
}]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
},
|
|
|
|
|
|
|
|
watch: {
|
|
|
|
// WATCHERS
|
|
|
|
currentLang (newValue, oldValue) {
|
|
|
|
|
|
|
|
this.$nextTick(() => {
|
|
|
|
watch(() => state.currentLang, (newValue, oldValue) => {
|
|
|
|
if (this.currentTree.length > 0) {
|
|
|
|
nextTick(() => {
|
|
|
|
this.current = this.currentTree[0]
|
|
|
|
if (state.currentTree.length > 0) {
|
|
|
|
|
|
|
|
state.current = state.currentTree[0]
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
this.current = {}
|
|
|
|
state.current = {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// METHODS
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async function load () {
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
methods: {
|
|
|
|
|
|
|
|
async load () {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
},
|
|
|
|
function addItem (kind) {
|
|
|
|
addItem (kind) {
|
|
|
|
|
|
|
|
let newItem = {
|
|
|
|
let newItem = {
|
|
|
|
id: uuid(),
|
|
|
|
id: uuid(),
|
|
|
|
kind,
|
|
|
|
kind,
|
|
|
@ -405,40 +419,46 @@ export default {
|
|
|
|
case 'link':
|
|
|
|
case 'link':
|
|
|
|
newItem = {
|
|
|
|
newItem = {
|
|
|
|
...newItem,
|
|
|
|
...newItem,
|
|
|
|
label: this.$t('navigation.untitled', { kind: this.$t('navigation.link') }),
|
|
|
|
label: t('navigation.untitled', { kind: t('navigation.link') }),
|
|
|
|
icon: 'mdi-chevron-right',
|
|
|
|
icon: 'mdi-chevron-right',
|
|
|
|
targetType: 'home',
|
|
|
|
targetType: 'home',
|
|
|
|
target: ''
|
|
|
|
target: ''
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break
|
|
|
|
break
|
|
|
|
case 'header':
|
|
|
|
case 'header':
|
|
|
|
newItem.label = this.$t('navigation.untitled', { kind: this.$t('navigation.header') })
|
|
|
|
newItem.label = t('navigation.untitled', { kind: t('navigation.header') })
|
|
|
|
break
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this.currentTree = [...this.currentTree, newItem]
|
|
|
|
state.currentTree = [...state.currentTree, newItem]
|
|
|
|
this.current = newItem
|
|
|
|
state.current = newItem
|
|
|
|
},
|
|
|
|
}
|
|
|
|
deleteItem (item) {
|
|
|
|
|
|
|
|
this.currentTree = _.pull(this.currentTree, item)
|
|
|
|
function deleteItem (item) {
|
|
|
|
this.current = {}
|
|
|
|
state.currentTree = pull(state.currentTree, item)
|
|
|
|
},
|
|
|
|
state.current = {}
|
|
|
|
selectItem (item) {
|
|
|
|
}
|
|
|
|
this.current = item
|
|
|
|
|
|
|
|
},
|
|
|
|
function selectItem (item) {
|
|
|
|
selectPage () {
|
|
|
|
state.current = item
|
|
|
|
this.selectPageModal = true
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
selectPageHandle ({ path, locale }) {
|
|
|
|
function selectPage () {
|
|
|
|
this.current.target = `/${locale}/${path}`
|
|
|
|
state.selectPageModal = true
|
|
|
|
},
|
|
|
|
}
|
|
|
|
copyFromLocale () {
|
|
|
|
|
|
|
|
this.copyFromLocaleDialogIsShown = false
|
|
|
|
function selectPageHandle ({ path, locale }) {
|
|
|
|
this.currentTree = [...this.currentTree, ..._.get(_.find(this.trees, ['locale', this.copyFromLocaleCode]), 'items', null) || []]
|
|
|
|
state.current.target = `/${locale}/${path}`
|
|
|
|
},
|
|
|
|
}
|
|
|
|
async save () {
|
|
|
|
|
|
|
|
|
|
|
|
function copyFromLocale () {
|
|
|
|
|
|
|
|
state.copyFromLocaleDialogIsShown = false
|
|
|
|
|
|
|
|
state.currentTree = [...state.currentTree, ...find(state.trees, ['locale', state.copyFromLocaleCode])?.items || []]
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async function save () {
|
|
|
|
this.$store.commit('loadingStart', 'admin-navigation-save')
|
|
|
|
this.$store.commit('loadingStart', 'admin-navigation-save')
|
|
|
|
try {
|
|
|
|
try {
|
|
|
|
const resp = await this.$apollo.mutate({
|
|
|
|
const resp = await APOLLO_CLIENT.mutate({
|
|
|
|
mutation: gql`
|
|
|
|
mutation: gql`
|
|
|
|
mutation ($tree: [NavigationTreeInput]!, $mode: NavigationMode!) {
|
|
|
|
mutation ($tree: [NavigationTreeInput]!, $mode: NavigationMode!) {
|
|
|
|
navigation{
|
|
|
|
navigation{
|
|
|
@ -462,34 +482,35 @@ export default {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
`,
|
|
|
|
variables: {
|
|
|
|
variables: {
|
|
|
|
tree: this.trees,
|
|
|
|
tree: state.trees,
|
|
|
|
mode: this.config.mode
|
|
|
|
mode: state.config.mode
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|
|
|
|
if (_.get(resp, 'data.navigation.updateTree.responseResult.succeeded', false) && _.get(resp, 'data.navigation.updateConfig.responseResult.succeeded', false)) {
|
|
|
|
if (resp?.data.navigation.updateTree.responseResult.succeeded && resp?.data.navigation.updateConfig.responseResult.succeeded) {
|
|
|
|
this.$store.commit('showNotification', {
|
|
|
|
this.$store.commit('showNotification', {
|
|
|
|
message: this.$t('navigation.saveSuccess'),
|
|
|
|
message: t('navigation.saveSuccess'),
|
|
|
|
style: 'success',
|
|
|
|
style: 'success',
|
|
|
|
icon: 'check'
|
|
|
|
icon: 'check'
|
|
|
|
})
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
throw new Error(_.get(resp, 'data.navigation.updateTree.responseResult.message', 'An unexpected error occurred.'))
|
|
|
|
throw new Error(resp?.data.navigation.updateTree.operation.message || 'An unexpected error occurred.')
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (err) {
|
|
|
|
} catch (err) {
|
|
|
|
this.$store.commit('pushGraphError', err)
|
|
|
|
this.$store.commit('pushGraphError', err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this.$store.commit('loadingStop', 'admin-navigation-save')
|
|
|
|
this.$store.commit('loadingStop', 'admin-navigation-save')
|
|
|
|
},
|
|
|
|
}
|
|
|
|
async refresh () {
|
|
|
|
|
|
|
|
await this.$apollo.queries.trees.refetch()
|
|
|
|
async function refresh () {
|
|
|
|
this.current = {}
|
|
|
|
load()
|
|
|
|
|
|
|
|
state.current = {}
|
|
|
|
this.$store.commit('showNotification', {
|
|
|
|
this.$store.commit('showNotification', {
|
|
|
|
message: 'Navigation has been refreshed.',
|
|
|
|
message: 'Navigation has been refreshed.',
|
|
|
|
style: 'success',
|
|
|
|
style: 'success',
|
|
|
|
icon: 'cached'
|
|
|
|
icon: 'cached'
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// apollo: {
|
|
|
|
// apollo: {
|
|
|
|
// config: {
|
|
|
|
// config: {
|
|
|
|
// query: gql`
|
|
|
|
// query: gql`
|
|
|
@ -573,11 +594,9 @@ export default {
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
}
|
|
|
|
|
|
|
|
</script>
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style lang='scss' scoped>
|
|
|
|
<style lang='scss' scoped>
|
|
|
|
|
|
|
|
|
|
|
|
.clickable {
|
|
|
|
.clickable {
|
|
|
|
cursor: pointer;
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
|
|
|
|
|
|