feat(admin): migrate nav + auth to vue 3 composition, convert lodash to lodash-es

pull/5698/head
Nicolas Giard 3 years ago
parent 8fb29cfc21
commit 4675348c43
No known key found for this signature in database
GPG Key ID: 85061B8F9D55B7C8

@ -69,6 +69,7 @@
"js-cookie": "3.0.1",
"jwt-decode": "3.1.2",
"lodash": "4.17.21",
"lodash-es": "4.17.21",
"luxon": "2.4.0",
"pinia": "2.0.14",
"pug": "3.0.2",

@ -95,7 +95,7 @@ q-layout.admin(view='hHh Lpr lff')
q-item-section(avatar)
q-icon(name='img:/_assets/icons/fluent-tree-structure.svg')
q-item-section {{ t('admin.navigation.title') }}
q-item(:to='`/_admin/` + adminStore.currentSiteId + `/rendering`', v-ripple, active-class='bg-primary text-white')
q-item(:to='`/_admin/` + adminStore.currentSiteId + `/rendering`', v-ripple, active-class='bg-primary text-white', disabled)
q-item-section(avatar)
q-icon(name='img:/_assets/icons/fluent-rich-text-converter.svg')
q-item-section {{ t('admin.rendering.title') }}

@ -116,7 +116,7 @@ q-page.admin-api
<script setup>
import gql from 'graphql-tag'
import cloneDeep from 'lodash/cloneDeep'
import { cloneDeep } from 'lodash-es'
import { useI18n } from 'vue-i18n'
import { useMeta, useQuasar } from 'quasar'
import { computed, onMounted, reactive, watch } from 'vue'

@ -4,8 +4,8 @@ q-page.admin-mail
.col-auto
img.admin-icon.animated.fadeInLeft(src='/_assets/icons/fluent-security-lock.svg')
.col.q-pl-md
.text-h5.text-primary.animated.fadeInLeft {{ $t('admin.auth.title') }}
.text-subtitle1.text-grey.animated.fadeInLeft.wait-p2s {{ $t('admin.auth.subtitle') }}
.text-h5.text-primary.animated.fadeInLeft {{ t('admin.auth.title') }}
.text-subtitle1.text-grey.animated.fadeInLeft.wait-p2s {{ t('admin.auth.subtitle') }}
.col-auto
q-btn.q-mr-sm.acrylic-btn(
icon='las la-question-circle'
@ -17,11 +17,11 @@ q-page.admin-mail
)
q-btn(
unelevated
icon='mdi-check'
:label='$t(`common.actions.apply`)'
icon='fa-solid fa-check'
:label='t(`common.actions.apply`)'
color='secondary'
@click='save'
:loading='loading'
:loading='state.loading > 0'
)
q-separator(inset)
.row.q-pa-md.q-col-gutter-md
@ -33,11 +33,11 @@ q-page.admin-mail
dark
)
q-item(
v-for='str of activeStrategies'
v-for='str of state.activeStrategies'
:key='str.key'
active-class='bg-primary text-white'
:active='selectedStrategy === str.key'
@click='selectedStrategy = str.key'
:active='state.selectedStrategy === str.key'
@click='state.selectedStrategy = str.key'
clickable
)
q-item-section(side)
@ -52,7 +52,7 @@ q-page.admin-mail
q-btn.q-mt-sm.full-width(
color='primary'
icon='las la-plus'
:label='$t(`admin.auth.addStrategy`)'
:label='t(`admin.auth.addStrategy`)'
)
q-menu(auto-close)
q-list(style='min-width: 350px;')
@ -261,275 +261,294 @@ q-page.admin-mail
//- .body-2 HTTP-POST
</template>
<script>
import _ from 'lodash'
<script setup>
import gql from 'graphql-tag'
import { find, reject } from 'lodash-es'
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'
export default {
mixins: [
createMetaMixin(function () {
return {
title: this.$t('admin.auth.title')
}
})
],
components: {
draggable
},
filters: {
startCase (val) { return _.startCase(val) }
},
data () {
return {
groups: [],
strategies: [],
activeStrategies: [
{
key: 'local',
strategy: {
key: 'local',
title: 'Username-Password Authentication',
description: '',
useForm: true,
icon: '/_assets/icons/ultraviolet-data-protection.svg',
website: ''
},
config: [],
isEnabled: true,
displayName: 'Local Database',
selfRegistration: false,
domainWhitelist: '',
autoEnrollGroups: []
},
{
key: 'google',
strategy: {
key: 'google',
title: 'Google',
description: '',
useForm: true,
icon: '/_assets/icons/ultraviolet-google.svg',
website: ''
},
config: [],
isEnabled: true,
displayName: 'Google',
selfRegistration: false,
domainWhitelist: '',
autoEnrollGroups: []
},
{
key: 'slack',
strategy: {
key: 'slack',
title: 'Slack',
description: '',
useForm: true,
icon: '/_assets/icons/ultraviolet-slack.svg',
website: ''
},
config: [],
isEnabled: false,
displayName: 'Slack',
selfRegistration: false,
domainWhitelist: '',
autoEnrollGroups: []
}
],
selectedStrategy: '',
host: '',
// QUASAR
const $q = useQuasar()
// STORES
const adminStore = useAdminStore()
const siteStore = useSiteStore()
// I18N
const { t } = useI18n()
// META
useMeta({
title: t('admin.auth.title')
})
// DATA
const state = reactive({
loading: 0,
groups: [],
strategies: [],
activeStrategies: [
{
key: 'local',
strategy: {
strategy: {}
}
}
},
watch: {
selectedStrategy (newValue, oldValue) {
this.strategy = _.find(this.activeStrategies, ['key', newValue]) || {}
},
activeStrategies (newValue, oldValue) {
this.selectedStrategy = 'local'
}
},
methods: {
async refresh () {
await this.$apollo.queries.strategies.refetch()
await this.$apollo.queries.activeStrategies.refetch()
this.$store.commit('showNotification', {
message: this.$t('admin.auth.refreshSuccess'),
style: 'success',
icon: 'cached'
})
key: 'local',
title: 'Username-Password Authentication',
description: '',
useForm: true,
icon: '/_assets/icons/ultraviolet-data-protection.svg',
website: ''
},
config: [],
isEnabled: true,
displayName: 'Local Database',
selfRegistration: false,
domainWhitelist: '',
autoEnrollGroups: []
},
addStrategy (str) {
const newStr = {
key: uuid(),
strategy: str,
config: str.props.map(c => ({
key: c.key,
value: {
...c,
value: c.default
}
})),
order: this.activeStrategies.length,
isEnabled: true,
displayName: str.title,
selfRegistration: false,
domainWhitelist: [],
autoEnrollGroups: []
}
this.activeStrategies = [...this.activeStrategies, newStr]
this.$nextTick(() => {
this.selectedStrategy = newStr.key
})
},
deleteStrategy () {
this.activeStrategies = _.reject(this.activeStrategies, ['key', this.strategy.key])
{
key: 'google',
strategy: {
key: 'google',
title: 'Google',
description: '',
useForm: true,
icon: '/_assets/icons/ultraviolet-google.svg',
website: ''
},
config: [],
isEnabled: true,
displayName: 'Google',
selfRegistration: false,
domainWhitelist: '',
autoEnrollGroups: []
},
async save () {
this.$store.commit('loadingStart', 'admin-auth-savestrategies')
try {
const resp = await this.$apollo.mutate({
mutation: gql`
mutation($strategies: [AuthenticationStrategyInput]!) {
authentication {
updateStrategies(strategies: $strategies) {
responseResult {
succeeded
errorCode
slug
message
}
}
}
}
`,
variables: {
strategies: this.activeStrategies.map((str, idx) => ({
key: str.key,
strategyKey: str.strategy.key,
displayName: str.displayName,
order: idx,
isEnabled: str.isEnabled,
config: str.config.map(cfg => ({ ...cfg, value: JSON.stringify({ v: cfg.value.value }) })),
selfRegistration: str.selfRegistration,
domainWhitelist: str.domainWhitelist,
autoEnrollGroups: str.autoEnrollGroups
}))
}
})
if (_.get(resp, 'data.authentication.updateStrategies.responseResult.succeeded', false)) {
this.$store.commit('showNotification', {
message: this.$t('admin.auth.saveSuccess'),
style: 'success',
icon: 'check'
})
} else {
throw new Error(_.get(resp, 'data.authentication.updateStrategies.responseResult.message', this.$t('common.error.unexpected')))
}
} catch (err) {
this.$store.commit('pushGraphError', err)
}
this.$store.commit('loadingStop', 'admin-auth-savestrategies')
{
key: 'slack',
strategy: {
key: 'slack',
title: 'Slack',
description: '',
useForm: true,
icon: '/_assets/icons/ultraviolet-slack.svg',
website: ''
},
config: [],
isEnabled: false,
displayName: 'Slack',
selfRegistration: false,
domainWhitelist: '',
autoEnrollGroups: []
}
},
apollo: {
strategies: {
query: gql`
query {
authentication {
strategies {
key
title
description
isAvailable
useForm
logo
website
props {
key
value
}
}
}
}
`,
skip: true,
fetchPolicy: 'network-only',
update: (data) => _.get(data, 'authentication.strategies', []).map(str => ({
...str,
isDisabled: !str.isAvailable || str.key === 'local',
props: _.sortBy(str.props.map(cfg => ({
key: cfg.key,
...JSON.parse(cfg.value)
})), [t => t.order])
})),
watchLoading (isLoading) {
this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-auth-strategies-refresh')
],
selectedStrategy: '',
host: '',
strategy: {
strategy: {}
}
})
// WATCHERS
watch(() => state.selectedStrategy, (newValue, oldValue) => {
state.strategy = find(state.activeStrategies, ['key', newValue]) || {}
})
watch(() => state.activeStrategies, (newValue, oldValue) => {
state.selectedStrategy = 'local'
})
// METHODS
async function refresh () {
await this.$apollo.queries.strategies.refetch()
await this.$apollo.queries.activeStrategies.refetch()
this.$store.commit('showNotification', {
message: this.$t('admin.auth.refreshSuccess'),
style: 'success',
icon: 'cached'
})
}
function addStrategy (str) {
const newStr = {
key: uuid(),
strategy: str,
config: str.props.map(c => ({
key: c.key,
value: {
...c,
value: c.default
}
},
activeStrategies: {
query: gql`
query {
})),
order: state.activeStrategies.length,
isEnabled: true,
displayName: str.title,
selfRegistration: false,
domainWhitelist: [],
autoEnrollGroups: []
}
state.activeStrategies = [...state.activeStrategies, newStr]
nextTick(() => {
state.selectedStrategy = newStr.key
})
}
function deleteStrategy () {
state.activeStrategies = reject(state.activeStrategies, ['key', state.strategy.key])
}
async function save () {
state.loading++
try {
const resp = await APOLLO_CLIENT.mutate({
mutation: gql`
mutation($strategies: [AuthenticationStrategyInput]!) {
authentication {
activeStrategies {
key
strategy {
key
title
description
useForm
logo
website
updateStrategies(strategies: $strategies) {
responseResult {
succeeded
errorCode
slug
message
}
config {
key
value
}
order
isEnabled
displayName
selfRegistration
domainWhitelist
autoEnrollGroups
}
}
}
`,
skip: true,
fetchPolicy: 'network-only',
update: (data) => _.sortBy(_.get(data, 'authentication.activeStrategies', []).map(str => ({
...str,
config: _.sortBy(str.config.map(cfg => ({
...cfg,
value: JSON.parse(cfg.value)
})), [t => t.value.order])
})), ['order']),
watchLoading (isLoading) {
this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-auth-activestrategies-refresh')
}
},
groups: {
query: gql`{ test }`,
fetchPolicy: 'network-only',
update: (data) => data.groups.list,
watchLoading (isLoading) {
this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-auth-groups-refresh')
}
},
host: {
query: gql`{ test }`,
fetchPolicy: 'network-only',
update: (data) => _.cloneDeep(data.site.config.host),
watchLoading (isLoading) {
this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-auth-host-refresh')
variables: {
strategies: this.activeStrategies.map((str, idx) => ({
key: str.key,
strategyKey: str.strategy.key,
displayName: str.displayName,
order: idx,
isEnabled: str.isEnabled,
config: str.config.map(cfg => ({ ...cfg, value: JSON.stringify({ v: cfg.value.value }) })),
selfRegistration: str.selfRegistration,
domainWhitelist: str.domainWhitelist,
autoEnrollGroups: str.autoEnrollGroups
}))
}
})
if (resp?.data?.authentication?.updateStrategies?.operation.succeeded) {
$q.notify({
type: 'positive',
message: t('admin.auth.saveSuccess')
})
} else {
throw new Error(resp?.data?.authentication?.updateStrategies?.operation?.message || t('common.error.unexpected'))
}
} catch (err) {
$q.notify({
type: 'negative',
message: 'Failed to save site theme config',
caption: err.message
})
}
state.loading--
}
// apollo: {
// strategies: {
// query: gql`
// query {
// authentication {
// strategies {
// key
// title
// description
// isAvailable
// useForm
// logo
// website
// props {
// key
// value
// }
// }
// }
// }
// `,
// skip: true,
// fetchPolicy: 'network-only',
// update: (data) => _.get(data, 'authentication.strategies', []).map(str => ({
// ...str,
// isDisabled: !str.isAvailable || str.key === 'local',
// props: _.sortBy(str.props.map(cfg => ({
// key: cfg.key,
// ...JSON.parse(cfg.value)
// })), [t => t.order])
// })),
// watchLoading (isLoading) {
// this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-auth-strategies-refresh')
// }
// },
// activeStrategies: {
// query: gql`
// query {
// authentication {
// activeStrategies {
// key
// strategy {
// key
// title
// description
// useForm
// logo
// website
// }
// config {
// key
// value
// }
// order
// isEnabled
// displayName
// selfRegistration
// domainWhitelist
// autoEnrollGroups
// }
// }
// }
// `,
// skip: true,
// fetchPolicy: 'network-only',
// update: (data) => _.sortBy(_.get(data, 'authentication.activeStrategies', []).map(str => ({
// ...str,
// config: _.sortBy(str.config.map(cfg => ({
// ...cfg,
// value: JSON.parse(cfg.value)
// })), [t => t.value.order])
// })), ['order']),
// watchLoading (isLoading) {
// this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-auth-activestrategies-refresh')
// }
// },
// groups: {
// query: gql`{ test }`,
// fetchPolicy: 'network-only',
// update: (data) => data.groups.list,
// watchLoading (isLoading) {
// this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-auth-groups-refresh')
// }
// },
// host: {
// query: gql`{ test }`,
// fetchPolicy: 'network-only',
// update: (data) => _.cloneDeep(data.site.config.host),
// watchLoading (isLoading) {
// this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-auth-host-refresh')
// }
// }
</script>

@ -99,7 +99,7 @@ q-page.admin-extensions
<script setup>
import gql from 'graphql-tag'
import cloneDeep from 'lodash/cloneDeep'
import { cloneDeep } from 'lodash-es'
import { useI18n } from 'vue-i18n'
import { useMeta, useQuasar } from 'quasar'
import { computed, onMounted, reactive, watch } from 'vue'

@ -82,7 +82,7 @@ q-page.admin-flags
<script setup>
import gql from 'graphql-tag'
import { defineAsyncComponent, onMounted, reactive, ref, watch } from 'vue'
import _transform from 'lodash/transform'
import { transform } from 'lodash-es'
import { useMeta, useQuasar } from 'quasar'
import { useI18n } from 'vue-i18n'

@ -391,7 +391,7 @@ q-page.admin-general
<script setup>
import gql from 'graphql-tag'
import cloneDeep from 'lodash/cloneDeep'
import { cloneDeep } from 'lodash-es'
import { useI18n } from 'vue-i18n'
import { useMeta, useQuasar } from 'quasar'
import { onMounted, reactive, watch } from 'vue'

@ -93,7 +93,7 @@ q-page.admin-groups
<script setup>
import gql from 'graphql-tag'
import cloneDeep from 'lodash/cloneDeep'
import { cloneDeep } from 'lodash-es'
import { useI18n } from 'vue-i18n'
import { useMeta, useQuasar } from 'quasar'
import { computed, onBeforeUnmount, onMounted, reactive, watch } from 'vue'

@ -143,9 +143,7 @@ q-page.admin-locale
<script setup>
import gql from 'graphql-tag'
import filter from 'lodash/filter'
import _get from 'lodash/get'
import cloneDeep from 'lodash/cloneDeep'
import { cloneDeep, filter } from 'lodash-es'
import LocaleInstallDialog from '../components/LocaleInstallDialog.vue'
@ -278,7 +276,7 @@ async function download (lc) {
locale: lc.code
}
})
const resp = _get(respRaw, 'data.localization.downloadLocale.responseResult', {})
const resp = respRaw?.data?.localization?.downloadLocale?.responseResult || {}
if (resp.succeeded) {
lc.isDownloading = false
lc.isInstalled = true
@ -331,7 +329,7 @@ async function save () {
namespaces: state.namespaces
}
})
const resp = _get(respRaw, 'data.localization.updateLocale.responseResult', {})
const resp = respRaw?.data?.localization?.updateLocale?.responseResult || {}
if (resp.succeeded) {
// Change UI language
this.$i18n.locale = state.selectedLocale

@ -176,8 +176,7 @@ q-page.admin-login
</template>
<script setup>
import { get } from 'vuex-pathify'
import cloneDeep from 'lodash/cloneDeep'
import { cloneDeep } from 'lodash-es'
import gql from 'graphql-tag'
import draggable from 'vuedraggable'

@ -298,9 +298,7 @@ q-page.admin-mail
</template>
<script setup>
import toSafeInteger from 'lodash/toSafeInteger'
import _get from 'lodash/get'
import cloneDeep from 'lodash/cloneDeep'
import { cloneDeep, toSafeInteger } from 'lodash-es'
import gql from 'graphql-tag'
import { useI18n } from 'vue-i18n'
@ -477,7 +475,7 @@ async function sendTest () {
sendMailTest(
recipientEmail: $recipientEmail
) {
status {
operation {
succeeded
slug
message
@ -489,8 +487,8 @@ async function sendTest () {
recipientEmail: state.testEmail
}
})
if (!_get(resp, 'data.sendMailTest.status.succeeded', false)) {
throw new Error(_get(resp, 'data.sendMailTest.status.message', 'An unexpected error occurred.'))
if (!resp?.data?.sendMailTest?.operation?.succeeded) {
throw new Error(resp?.data?.sendMailTest?.operation?.message || 'An unexpected error occurred.')
}
state.testEmail = ''

@ -4,8 +4,8 @@ q-page.admin-navigation
.col-auto
img.admin-icon.animated.fadeInLeft(src='/_assets/icons/fluent-tree-structure.svg')
.col.q-pl-md
.text-h5.text-primary.animated.fadeInLeft {{ $t('admin.navigation.title') }}
.text-subtitle1.text-grey.animated.fadeInLeft.wait-p2s {{ $t('admin.navigation.subtitle') }}
.text-h5.text-primary.animated.fadeInLeft {{ t('admin.navigation.title') }}
.text-subtitle1.text-grey.animated.fadeInLeft.wait-p2s {{ t('admin.navigation.subtitle') }}
.col-auto
q-btn.acrylic-btn.q-mr-sm(
icon='las la-question-circle'
@ -19,16 +19,16 @@ q-page.admin-navigation
icon='las la-redo-alt'
flat
color='secondary'
:loading='loading > 0'
:loading='state.loading > 0'
@click='load'
)
q-btn(
unelevated
icon='mdi-check'
:label='$t(`common.actions.apply`)'
icon='fa-solid fa-check'
:label='t(`common.actions.apply`)'
color='secondary'
@click='save'
:disabled='loading > 0'
:disabled='state.loading > 0'
)
q-separator(inset)
.row.q-pa-md.q-col-gutter-md
@ -306,278 +306,297 @@ q-page.admin-navigation
//- page-selector(mode='select', v-model='selectPageModal', :open-handler='selectPageHandle', path='home', :locale='currentLang')
</template>
<script>
import _ from 'lodash'
<script setup>
import gql from 'graphql-tag'
import { find, intersectionBy, pull, unionBy } from 'lodash-es'
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'
// 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 siteLangs = [{ code: 'en' }]
export default {
mixins: [
createMetaMixin(function () {
return {
title: this.$t('admin.navigation.title')
}
})
],
components: {
draggable
},
meta () {
return {
title: this.$t('admin.navigation.title')
}
const state = reactive({
loading: 0,
selectPageModal: false,
trees: [],
current: {},
currentLang: siteConfig.lang,
groups: [],
copyFromLocaleDialogIsShown: false,
config: {
mode: 'NONE'
},
data () {
return {
loading: false,
selectPageModal: false,
trees: [],
current: {},
currentLang: siteConfig.lang,
groups: [],
copyFromLocaleDialogIsShown: false,
config: {
mode: 'NONE'
},
allLocales: [],
copyFromLocaleCode: 'en'
}
allLocales: [],
copyFromLocaleCode: 'en'
})
// COMPUTED
const navTypes = computed(() => ([
{ text: t('navigation.navType.external'), value: 'external' },
{ text: t('navigation.navType.externalblank'), value: 'externalblank' },
{ text: t('navigation.navType.home'), value: 'home' },
{ text: t('navigation.navType.page'), value: 'page' }
// { text: t('navigation.navType.searchQuery'), value: 'search' }
]))
const locales = computed(() => {
return intersectionBy(state.allLocales, unionBy(siteLangs, [{ code: 'en' }, { code: siteConfig.lang }], 'code'), 'code')
})
const currentTree = computed({
get () {
return find(state.trees, ['locale', state.currentLang])?.items || []
},
computed: {
navTypes () {
return [
{ text: this.$t('navigation.navType.external'), value: 'external' },
{ text: this.$t('navigation.navType.externalblank'), value: 'externalblank' },
{ text: this.$t('navigation.navType.home'), value: 'home' },
{ text: this.$t('navigation.navType.page'), value: 'page' }
// { text: this.$t('navigation.navType.searchQuery'), value: 'search' }
]
},
locales () {
return _.intersectionBy(this.allLocales, _.unionBy(siteLangs, [{ code: 'en' }, { code: siteConfig.lang }], 'code'), 'code')
},
currentTree: {
get () {
return _.get(_.find(this.trees, ['locale', this.currentLang]), 'items', null) || []
},
set (val) {
const tree = _.find(this.trees, ['locale', this.currentLang])
if (tree) {
tree.items = val
} else {
this.trees = [...this.trees, {
locale: this.currentLang,
items: val
}]
}
}
set (val) {
const tree = find(state.trees, ['locale', state.currentLang])
if (tree) {
tree.items = val
} else {
state.trees = [...state.trees, {
locale: state.currentLang,
items: val
}]
}
},
watch: {
currentLang (newValue, oldValue) {
this.$nextTick(() => {
if (this.currentTree.length > 0) {
this.current = this.currentTree[0]
} else {
this.current = {}
}
})
}
})
// WATCHERS
watch(() => state.currentLang, (newValue, oldValue) => {
nextTick(() => {
if (state.currentTree.length > 0) {
state.current = state.currentTree[0]
} else {
state.current = {}
}
},
methods: {
async load () {
},
addItem (kind) {
let newItem = {
id: uuid(),
kind,
visibilityMode: 'all',
visibilityGroups: []
}
switch (kind) {
case 'link':
newItem = {
...newItem,
label: this.$t('navigation.untitled', { kind: this.$t('navigation.link') }),
icon: 'mdi-chevron-right',
targetType: 'home',
target: ''
}
break
case 'header':
newItem.label = this.$t('navigation.untitled', { kind: this.$t('navigation.header') })
break
})
})
// METHODS
async function load () {
}
function addItem (kind) {
let newItem = {
id: uuid(),
kind,
visibilityMode: 'all',
visibilityGroups: []
}
switch (kind) {
case 'link':
newItem = {
...newItem,
label: t('navigation.untitled', { kind: t('navigation.link') }),
icon: 'mdi-chevron-right',
targetType: 'home',
target: ''
}
this.currentTree = [...this.currentTree, newItem]
this.current = newItem
},
deleteItem (item) {
this.currentTree = _.pull(this.currentTree, item)
this.current = {}
},
selectItem (item) {
this.current = item
},
selectPage () {
this.selectPageModal = true
},
selectPageHandle ({ path, locale }) {
this.current.target = `/${locale}/${path}`
},
copyFromLocale () {
this.copyFromLocaleDialogIsShown = false
this.currentTree = [...this.currentTree, ..._.get(_.find(this.trees, ['locale', this.copyFromLocaleCode]), 'items', null) || []]
},
async save () {
this.$store.commit('loadingStart', 'admin-navigation-save')
try {
const resp = await this.$apollo.mutate({
mutation: gql`
mutation ($tree: [NavigationTreeInput]!, $mode: NavigationMode!) {
navigation{
updateTree(tree: $tree) {
responseResult {
succeeded
errorCode
slug
message
}
},
updateConfig(mode: $mode) {
responseResult {
succeeded
errorCode
slug
message
}
}
break
case 'header':
newItem.label = t('navigation.untitled', { kind: t('navigation.header') })
break
}
state.currentTree = [...state.currentTree, newItem]
state.current = newItem
}
function deleteItem (item) {
state.currentTree = pull(state.currentTree, item)
state.current = {}
}
function selectItem (item) {
state.current = item
}
function selectPage () {
state.selectPageModal = true
}
function selectPageHandle ({ path, locale }) {
state.current.target = `/${locale}/${path}`
}
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')
try {
const resp = await APOLLO_CLIENT.mutate({
mutation: gql`
mutation ($tree: [NavigationTreeInput]!, $mode: NavigationMode!) {
navigation{
updateTree(tree: $tree) {
responseResult {
succeeded
errorCode
slug
message
}
},
updateConfig(mode: $mode) {
responseResult {
succeeded
errorCode
slug
message
}
}
`,
variables: {
tree: this.trees,
mode: this.config.mode
}
})
if (_.get(resp, 'data.navigation.updateTree.responseResult.succeeded', false) && _.get(resp, 'data.navigation.updateConfig.responseResult.succeeded', false)) {
this.$store.commit('showNotification', {
message: this.$t('navigation.saveSuccess'),
style: 'success',
icon: 'check'
})
} else {
throw new Error(_.get(resp, 'data.navigation.updateTree.responseResult.message', 'An unexpected error occurred.'))
}
} catch (err) {
this.$store.commit('pushGraphError', err)
`,
variables: {
tree: state.trees,
mode: state.config.mode
}
this.$store.commit('loadingStop', 'admin-navigation-save')
},
async refresh () {
await this.$apollo.queries.trees.refetch()
this.current = {}
})
if (resp?.data.navigation.updateTree.responseResult.succeeded && resp?.data.navigation.updateConfig.responseResult.succeeded) {
this.$store.commit('showNotification', {
message: 'Navigation has been refreshed.',
message: t('navigation.saveSuccess'),
style: 'success',
icon: 'cached'
icon: 'check'
})
} else {
throw new Error(resp?.data.navigation.updateTree.operation.message || 'An unexpected error occurred.')
}
} catch (err) {
this.$store.commit('pushGraphError', err)
}
// apollo: {
// config: {
// query: gql`
// {
// navigation {
// config {
// mode
// }
// }
// }
// `,
// fetchPolicy: 'network-only',
// update: (data) => _.cloneDeep(data.navigation.config),
// watchLoading (isLoading) {
// this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-navigation-config')
// }
// },
// trees: {
// query: gql`
// {
// navigation {
// tree {
// locale
// items {
// id
// kind
// label
// icon
// targetType
// target
// visibilityMode
// visibilityGroups
// }
// }
// }
// }
// `,
// fetchPolicy: 'network-only',
// update: (data) => _.cloneDeep(data.navigation.tree),
// watchLoading (isLoading) {
// this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-navigation-tree')
// }
// },
// groups: {
// query: gql`
// query {
// groups {
// list {
// id
// name
// isSystem
// userCount
// createdAt
// updatedAt
// }
// }
// }
// `,
// fetchPolicy: 'network-only',
// update: (data) => data.groups.list,
// watchLoading (isLoading) {
// this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-navigation-groups')
// }
// },
// allLocales: {
// query: gql`
// {
// localization {
// locales {
// code
// name
// nativeName
// }
// }
// }
// `,
// fetchPolicy: 'network-only',
// update: (data) => data.localization.locales,
// watchLoading (isLoading) {
// this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-navigation-locales')
// }
// }
// }
this.$store.commit('loadingStop', 'admin-navigation-save')
}
async function refresh () {
load()
state.current = {}
this.$store.commit('showNotification', {
message: 'Navigation has been refreshed.',
style: 'success',
icon: 'cached'
})
}
// apollo: {
// config: {
// query: gql`
// {
// navigation {
// config {
// mode
// }
// }
// }
// `,
// fetchPolicy: 'network-only',
// update: (data) => _.cloneDeep(data.navigation.config),
// watchLoading (isLoading) {
// this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-navigation-config')
// }
// },
// trees: {
// query: gql`
// {
// navigation {
// tree {
// locale
// items {
// id
// kind
// label
// icon
// targetType
// target
// visibilityMode
// visibilityGroups
// }
// }
// }
// }
// `,
// fetchPolicy: 'network-only',
// update: (data) => _.cloneDeep(data.navigation.tree),
// watchLoading (isLoading) {
// this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-navigation-tree')
// }
// },
// groups: {
// query: gql`
// query {
// groups {
// list {
// id
// name
// isSystem
// userCount
// createdAt
// updatedAt
// }
// }
// }
// `,
// fetchPolicy: 'network-only',
// update: (data) => data.groups.list,
// watchLoading (isLoading) {
// this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-navigation-groups')
// }
// },
// allLocales: {
// query: gql`
// {
// localization {
// locales {
// code
// name
// nativeName
// }
// }
// }
// `,
// fetchPolicy: 'network-only',
// update: (data) => data.localization.locales,
// watchLoading (isLoading) {
// this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-navigation-locales')
// }
// }
// }
</script>
<style lang='scss' scoped>
.clickable {
cursor: pointer;

@ -323,9 +323,8 @@ q-page.admin-mail
</template>
<script setup>
import cloneDeep from 'lodash/cloneDeep'
import { cloneDeep } from 'lodash-es'
import gql from 'graphql-tag'
import _get from 'lodash/get'
import filesize from 'filesize'
import filesizeParser from 'filesize-parser'
@ -492,7 +491,7 @@ async function save () {
uploadMaxFileSize: filesizeParser(state.humanUploadMaxFileSize || '0')
}
})
const resp = _get(respRaw, 'data.updateSystemSecurity.status', {})
const resp = respRaw?.data?.updateSystemSecurity?.status || {}
if (resp.succeeded) {
$q.notify({
type: 'positive',

@ -583,10 +583,8 @@ q-page.admin-storage
</template>
<script setup>
import find from 'lodash/find'
import cloneDeep from 'lodash/cloneDeep'
import { cloneDeep, find, transform } from 'lodash-es'
import gql from 'graphql-tag'
import transform from 'lodash/transform'
import * as VNetworkGraph from 'v-network-graph'
import { useI18n } from 'vue-i18n'

@ -227,7 +227,7 @@ q-page.admin-system
</template>
<script setup>
import cloneDeep from 'lodash/cloneDeep'
import { cloneDeep } from 'lodash-es'
import gql from 'graphql-tag'
import { useI18n } from 'vue-i18n'
import { useMeta, useQuasar } from 'quasar'

@ -203,8 +203,7 @@ q-page.admin-theme
<script setup>
import gql from 'graphql-tag'
import cloneDeep from 'lodash/cloneDeep'
import startCase from 'lodash/startCase'
import { cloneDeep, startCase } from 'lodash-es'
import { useI18n } from 'vue-i18n'
import { useMeta, useQuasar } from 'quasar'
import { onMounted, reactive, watch } from 'vue'

@ -106,7 +106,7 @@ q-page.admin-groups
<script setup>
import gql from 'graphql-tag'
import cloneDeep from 'lodash/cloneDeep'
import { cloneDeep } from 'lodash-es'
import { DateTime } from 'luxon'
import { useI18n } from 'vue-i18n'
import { useMeta, useQuasar } from 'quasar'

@ -92,7 +92,7 @@ q-page.admin-webhooks
</template>
<script setup>
import cloneDeep from 'lodash/cloneDeep'
import { cloneDeep } from 'lodash-es'
import gql from 'graphql-tag'
import { useI18n } from 'vue-i18n'

@ -35,12 +35,12 @@ const routes = [
{ path: ':siteid/editors', component: () => import('../pages/AdminEditors.vue') },
{ path: ':siteid/locale', component: () => import('../pages/AdminLocale.vue') },
{ path: ':siteid/login', component: () => import('../pages/AdminLogin.vue') },
// { path: ':siteid/navigation', component: () => import('../pages/AdminNavigation.vue') },
{ path: ':siteid/navigation', component: () => import('../pages/AdminNavigation.vue') },
// { path: ':siteid/rendering', component: () => import('../pages/AdminRendering.vue') },
{ path: ':siteid/storage/:id?', component: () => import('../pages/AdminStorage.vue') },
{ path: ':siteid/theme', component: () => import('../pages/AdminTheme.vue') },
// -> Users
// { path: 'auth', component: () => import('../pages/AdminAuth.vue') },
{ path: 'auth', component: () => import('../pages/AdminAuth.vue') },
{ path: 'groups/:id?/:section?', component: () => import('../pages/AdminGroups.vue') },
{ path: 'users/:id?/:section?', component: () => import('../pages/AdminUsers.vue') },
// -> System

@ -4702,6 +4702,13 @@ __metadata:
languageName: node
linkType: hard
"lodash-es@npm:4.17.21":
version: 4.17.21
resolution: "lodash-es@npm:4.17.21"
checksum: 05cbffad6e2adbb331a4e16fbd826e7faee403a1a04873b82b42c0f22090f280839f85b95393f487c1303c8a3d2a010048bf06151a6cbe03eee4d388fb0a12d2
languageName: node
linkType: hard
"lodash._reinterpolate@npm:^3.0.0":
version: 3.0.0
resolution: "lodash._reinterpolate@npm:3.0.0"
@ -6844,6 +6851,7 @@ __metadata:
js-cookie: 3.0.1
jwt-decode: 3.1.2
lodash: 4.17.21
lodash-es: 4.17.21
luxon: 2.4.0
pinia: 2.0.14
pug: 3.0.2

Loading…
Cancel
Save