feat: file manager improvements + admin user edit fixes

pull/6078/head
Nicolas Giard 2 years ago
parent b4769b9ac6
commit bfbb64a749
No known key found for this signature in database
GPG Key ID: 85061B8F9D55B7C8

@ -91,6 +91,9 @@ module.exports = {
}
},
Mutation: {
/**
* CREATE FOLDER
*/
async createFolder (obj, args, context) {
try {
// Get parent path
@ -138,6 +141,52 @@ module.exports = {
} catch (err) {
return graphHelper.generateError(err)
}
},
/**
* DELETE FOLDER
*/
async deleteFolder (obj, args, context) {
try {
// Get folder
const folder = await WIKI.db.knex('tree').where('id', args.folderId).first()
const folderPath = folder.folderPath ? `${folder.folderPath}.${folder.fileName}` : folder.fileName
WIKI.logger.debug(`Deleting folder ${folder.id} at path ${folderPath}...`)
// Delete all children
const deletedNodes = await WIKI.db.knex('tree').where('folderPath', '~', `${folderPath}.*`).del().returning(['id', 'type'])
// Delete folders
const deletedFolders = deletedNodes.filter(n => n.type === 'folder').map(n => n.id)
if (deletedFolders.length > 0) {
WIKI.logger.debug(`Deleted ${deletedFolders.length} children folders.`)
}
// Delete pages
const deletedPages = deletedNodes.filter(n => n.type === 'page').map(n => n.id)
if (deletedPages.length > 0) {
WIKI.logger.debug(`Deleting ${deletedPages.length} children pages...`)
// TODO: Delete page
}
// Delete assets
const deletedAssets = deletedNodes.filter(n => n.type === 'asset').map(n => n.id)
if (deletedAssets.length > 0) {
WIKI.logger.debug(`Deleting ${deletedPages.length} children assets...`)
// TODO: Delete asset
}
// Delete the folder itself
await WIKI.db.knex('tree').where('id', folder.id).del()
WIKI.logger.debug(`Deleting folder ${folder.id} successfully.`)
return {
operation: graphHelper.generateSuccess('Folder deleted successfully')
}
} catch (err) {
return graphHelper.generateError(err)
}
}
},
TreeItem: {

@ -352,8 +352,12 @@ module.exports = {
strategyKey: authStrategy.module,
strategyIcon: authModule.icon,
config: authStrategy.module === 'local' ? {
isTfaSetup: value.tfaSecret?.length > 0
} : {}
isPasswordSet: value.password?.length > 0,
isTfaSetup: value.tfaSecret?.length > 0,
isTfaRequired: value.tfaRequired ?? false,
mustChangePwd: value.mustChangePwd ?? false,
restrictLogin: value.restrictLogin ?? false
} : value
})
}, [])
},

@ -76,7 +76,7 @@ type TreeItemPage {
depth: Int
fileName: String
folderPath: String
pageEditor: String
editor: String
pageType: String
title: String
updatedAt: Date

@ -63,6 +63,7 @@
"codemirror": "6.0.1",
"filesize": "10.0.5",
"filesize-parser": "1.5.0",
"fuse.js": "6.6.2",
"graphql": "16.6.0",
"graphql-tag": "2.12.6",
"js-cookie": "3.0.1",

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 320 200" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<rect x="0" y="0" width="320" height="200" style="fill:url(#_Linear1);"/>
<path d="M203.194,73.306L175.694,45.806C175.18,45.289 174.481,45 173.75,45L129.75,45C122.168,45 116,51.168 116,58.75L116,141.25C116,148.832 122.168,155 129.75,155L190.25,155C197.832,155 204,148.832 204,141.25L204,75.25C204,74.521 203.711,73.82 203.194,73.306Z" style="fill:rgb(107,227,162);fill-rule:nonzero;"/>
<path d="M204,78L184.75,78C177.168,78 171,71.832 171,64.25L171,45C171,43.482 172.229,42.25 173.75,42.25C175.271,42.25 176.5,43.482 176.5,45L176.5,64.25C176.5,68.798 180.202,72.5 184.75,72.5L204,72.5C205.521,72.5 206.75,73.732 206.75,75.25C206.75,76.768 205.521,78 204,78Z" style="fill:rgb(50,69,97);fill-rule:nonzero;"/>
<path d="M182,102.75L138,102.75C136.479,102.75 135.25,101.518 135.25,100C135.25,98.482 136.479,97.25 138,97.25L182,97.25C183.521,97.25 184.75,98.482 184.75,100C184.75,101.518 183.521,102.75 182,102.75Z" style="fill:rgb(50,69,97);fill-rule:nonzero;"/>
<path d="M182,116.5L138,116.5C136.479,116.5 135.25,115.268 135.25,113.75C135.25,112.232 136.479,111 138,111L182,111C183.521,111 184.75,112.232 184.75,113.75C184.75,115.268 183.521,116.5 182,116.5Z" style="fill:rgb(50,69,97);fill-rule:nonzero;"/>
<path d="M165.5,130.25L138,130.25C136.479,130.25 135.25,129.018 135.25,127.5C135.25,125.982 136.479,124.75 138,124.75L165.5,124.75C167.021,124.75 168.25,125.982 168.25,127.5C168.25,129.018 167.021,130.25 165.5,130.25Z" style="fill:rgb(50,69,97);fill-rule:nonzero;"/>
<defs>
<linearGradient id="_Linear1" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(320,100,-62.5,200,0,100)"><stop offset="0" style="stop-color:rgb(38,43,49);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(22,27,33);stop-opacity:1"/></linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

@ -13,6 +13,7 @@ q-layout.fileman(view='hHh lpR lFr', container)
ref='searchField'
style='width: 100%;'
label='Search folder...'
:debounce='500'
)
template(v-slot:prepend)
q-icon(name='las la-search')
@ -51,7 +52,7 @@ q-layout.fileman(view='hHh lpR lFr', container)
.q-pa-md
template(v-if='currentFileDetails')
q-img.rounded-borders.q-mb-md(
src='https://picsum.photos/id/134/340/340'
src='/_assets/illustrations/fileman-page.svg'
width='100%'
fit='cover'
:ratio='16/10'
@ -129,13 +130,21 @@ q-layout.fileman(view='hHh lpR lFr', container)
size='xs'
)
q-item-section.q-pr-sm Browse Using Titles
q-item(clickable)
q-item(clickable, @click='state.isCompact = !state.isCompact')
q-item-section(side)
q-icon(name='las la-stop', color='grey', size='xs')
q-icon(
:name='state.isCompact ? `las la-check-square` : `las la-stop`'
:color='state.isCompact ? `positive` : `grey`'
size='xs'
)
q-item-section.q-pr-sm Compact List
q-item(clickable)
q-item(clickable, @click='state.shouldShowFolders = !state.shouldShowFolders')
q-item-section(side)
q-icon(name='las la-check-square', color='positive', size='xs')
q-icon(
:name='state.shouldShowFolders ? `las la-check-square` : `las la-stop`'
:color='state.shouldShowFolders ? `positive` : `grey`'
size='xs'
)
q-item-section.q-pr-sm Show Folders
q-btn.q-mr-sm(
flat
@ -191,10 +200,10 @@ q-layout.fileman(view='hHh lpR lFr', container)
@dblclick.native='openItem(item)'
)
q-item-section.fileman-filelist-icon(avatar)
q-icon(:name='item.icon', size='xl')
q-icon(:name='item.icon', :size='state.isCompact ? `md` : `xl`')
q-item-section.fileman-filelist-label
q-item-label {{item.title}}
q-item-label(caption) {{item.caption}}
q-item-label(caption, v-if='!state.isCompact') {{item.caption}}
q-item-section.fileman-filelist-side(side, v-if='item.side')
.text-caption {{item.side}}
//- RIGHT-CLICK MENU
@ -211,11 +220,11 @@ q-layout.fileman(view='hHh lpR lFr', container)
q-item-section(side)
q-icon(name='las la-edit', color='orange')
q-item-section Edit
q-item(clickable, v-if='item.type !== `folder`')
q-item(clickable, v-if='item.type !== `folder`', @click='openItem(item)')
q-item-section(side)
q-icon(name='las la-eye', color='primary')
q-item-section View
q-item(clickable, v-if='item.type !== `folder`')
q-item(clickable, v-if='item.type !== `folder`', @click='copyItemURL(item)')
q-item-section(side)
q-icon(name='las la-clipboard', color='primary')
q-item-section Copy URL
@ -231,7 +240,7 @@ q-layout.fileman(view='hHh lpR lFr', container)
q-item-section(side)
q-icon(name='las la-arrow-right', color='teal')
q-item-section Move to...
q-item(clickable)
q-item(clickable, @click='delItem(item)')
q-item-section(side)
q-icon(name='las la-trash-alt', color='negative')
q-item-section.text-negative Delete
@ -255,7 +264,9 @@ import { filesize } from 'filesize'
import { useQuasar } from 'quasar'
import { DateTime } from 'luxon'
import { cloneDeep, find } from 'lodash-es'
import { useRoute, useRouter } from 'vue-router'
import gql from 'graphql-tag'
import Fuse from 'fuse.js/dist/fuse.basic.esm'
import NewMenu from './PageNewMenu.vue'
import Tree from './TreeNav.vue'
@ -277,6 +288,11 @@ const $q = useQuasar()
const pageStore = usePageStore()
const siteStore = useSiteStore()
// ROUTER
const router = useRouter()
const route = useRoute()
// I18N
const { t } = useI18n()
@ -291,6 +307,8 @@ const state = reactive({
treeNodes: {},
treeRoots: [],
displayMode: 'title',
isCompact: false,
shouldShowFolders: true,
isUploading: false,
shouldCancelUpload: false,
uploadPercentage: 0,
@ -314,8 +332,29 @@ const folderPath = computed(() => {
}
})
const filteredFiles = computed(() => {
if (state.search) {
const fuse = new Fuse(state.fileList, {
keys: [
'title',
'fileName'
]
})
return fuse.search(state.search).map(n => n.item)
} else {
return state.fileList
}
})
const files = computed(() => {
return state.fileList.map(f => {
return filteredFiles.value.filter(f => {
console.info(f)
// -> Show Folders Filter
if (f.type === 'folder' && !state.shouldShowFolders) {
return false
}
return true
}).map(f => {
switch (f.type) {
case 'folder': {
f.icon = fileTypes.folder.icon
@ -413,10 +452,9 @@ async function treeLazyLoad (nodeId, { done, fail }) {
done()
}
async function loadTree (parentId, types, noCache = false) {
async function loadTree (parentId, types) {
if (!parentId) {
parentId = null
state.treeRoots = []
}
if (parentId === state.currentFolderId) {
state.fileListLoading = true
@ -451,7 +489,7 @@ async function loadTree (parentId, types, noCache = false) {
title
createdAt
updatedAt
pageEditor
editor
}
... on TreeItemAsset {
id
@ -479,22 +517,21 @@ async function loadTree (parentId, types, noCache = false) {
switch (item.__typename) {
case 'TreeItemFolder': {
// -> Tree Nodes
if (!state.treeNodes[item.id] || (parentId && !treeComp.value.isLoaded(item.id))) {
if (!state.treeNodes[item.id]) {
state.treeNodes[item.id] = {
folderPath: item.folderPath,
fileName: item.fileName,
title: item.title,
children: []
}
if (item.folderPath) {
if (!state.treeNodes[parentId].children.includes(item.id)) {
state.treeNodes[parentId].children.push(item.id)
}
children: state.treeNodes[item.id]?.children ?? []
}
}
// -> Set Tree Roots
if (!item.folderPath) {
// -> Set Ancestors / Tree Roots
if (item.folderPath) {
if (!state.treeNodes[parentId].children.includes(item.id)) {
state.treeNodes[parentId].children.push(item.id)
}
} else {
newTreeRoots.push(item.id)
}
@ -504,6 +541,7 @@ async function loadTree (parentId, types, noCache = false) {
id: item.id,
type: 'folder',
title: item.title,
fileName: item.fileName,
children: 0
})
}
@ -516,7 +554,9 @@ async function loadTree (parentId, types, noCache = false) {
type: 'asset',
title: item.title,
fileType: 'pdf',
fileSize: 19000
fileSize: 19000,
folderPath: item.folderPath,
fileName: item.fileName
})
}
break
@ -528,7 +568,9 @@ async function loadTree (parentId, types, noCache = false) {
type: 'page',
title: item.title,
pageType: 'markdown',
updatedAt: '2022-11-24T18:27:00Z'
updatedAt: '2022-11-24T18:27:00Z',
folderPath: item.folderPath,
fileName: item.fileName
})
}
break
@ -551,6 +593,9 @@ async function loadTree (parentId, types, noCache = false) {
state.fileListLoading = false
})
}
if (parentId) {
treeComp.value.setLoaded(parentId)
}
}
function treeContextAction (nodeId, action) {
@ -566,6 +611,10 @@ function treeContextAction (nodeId, action) {
}
}
// --------------------------------------
// FOLDER METHODS
// --------------------------------------
function newFolder (parentId) {
$q.dialog({
component: FolderCreateDialog,
@ -577,7 +626,7 @@ function newFolder (parentId) {
})
}
function delFolder (folderId) {
function delFolder (folderId, mustReload = false) {
$q.dialog({
component: FolderDeleteDialog,
componentProps: {
@ -591,15 +640,23 @@ function delFolder (folderId) {
}
}
delete state.treeNodes[folderId]
if (state.treeRoots.includes(folderId)) {
state.treeRoots = state.treeRoots.filter(n => n !== folderId)
}
if (mustReload) {
loadTree(state.currentFolderId, null)
}
})
}
function reloadFolder (folderId) {
loadTree(folderId, null, true)
loadTree(folderId, null)
treeComp.value.resetLoaded()
}
// -> Upload Methods
// --------------------------------------
// UPLOAD METHODS
// --------------------------------------
function uploadFile () {
fileIpt.value.click()
@ -678,6 +735,10 @@ function uploadCancel () {
state.uploadPercentage = 0
}
// --------------------------------------
// ITEM LIST ACTIONS
// --------------------------------------
function selectItem (item) {
if (item.type === 'folder') {
state.currentFolderId = item.id
@ -688,7 +749,60 @@ function selectItem (item) {
}
function openItem (item) {
console.info(item.id)
switch (item.type) {
case 'folder': {
return
}
case 'page': {
const pagePath = item.folderPath ? `${item.folderPath}/${item.fileName}` : item.fileName
router.push(`/${pagePath}`)
close()
break
}
case 'asset': {
// TODO: Open asset
close()
break
}
}
}
async function copyItemURL (item) {
try {
switch (item.type) {
case 'page': {
const pagePath = item.folderPath ? `${item.folderPath}/${item.fileName}` : item.fileName
await navigator.clipboard.writeText(`${window.location.origin}/${pagePath}`)
break
}
case 'asset': {
// TODO: Copy asset URL to clibpard
break
}
default: {
throw new Error('Invalid Item Type')
}
}
$q.notify({
type: 'positive',
message: t('fileman.copyURLSuccess')
})
} catch (err) {
$q.notify({
type: 'negative',
message: 'Failed to copy URL to clipboard.',
caption: err.message
})
}
}
function delItem (item) {
switch (item.type) {
case 'folder': {
delFolder(item.id, true)
break
}
}
}
// MOUNTED

@ -19,6 +19,7 @@ q-dialog(ref='dialogRef', @hide='onDialogHide')
lazy-rules='ondemand'
autofocus
ref='iptTitle'
@keyup.enter='create'
)
q-item
blueprint-icon.self-start(icon='file-submodule')
@ -34,6 +35,7 @@ q-dialog(ref='dialogRef', @hide='onDialogHide')
:hint='t(`fileman.folderFileNameHint`)'
lazy-rules='ondemand'
@focus='state.pathDirty = true'
@keyup.enter='create'
)
q-card-actions.card-actions
q-space

@ -126,8 +126,11 @@ function setOpened (nodeId) {
function isLoaded (nodeId) {
return state.loaded[nodeId]
}
function resetLoaded (nodeId) {
state.loaded[nodeId] = false
function setLoaded (nodeId, value) {
state.loaded[nodeId] = value
}
function resetLoaded () {
state.loaded = {}
}
// PROVIDE
@ -146,6 +149,7 @@ provide('emitContextAction', emitContextAction)
defineExpose({
setOpened,
isLoaded,
setLoaded,
resetLoaded
})

@ -38,17 +38,20 @@ q-layout(view='hHh lpR fFf', container)
)
q-drawer.bg-dark-6(:model-value='true', :width='250', dark)
q-list(padding, v-if='state.loading < 1')
q-item(
template(
v-for='sc of sections'
:key='`section-` + sc.key'
clickable
:to='{ params: { section: sc.key } }'
active-class='bg-primary text-white'
:disabled='sc.disabled'
)
q-item-section(side)
q-icon(:name='sc.icon', color='white')
q-item-section {{sc.text}}
q-item(
v-if='!sc.disabled || flagsStore.experimental'
clickable
:to='{ params: { section: sc.key } }'
active-class='bg-primary text-white'
:disabled='sc.disabled'
)
q-item-section(side)
q-icon(:name='sc.icon', color='white')
q-item-section {{sc.text}}
q-page-container
q-page(v-if='state.loading > 0')
.flex.q-pa-lg.items-center
@ -268,7 +271,7 @@ q-layout(view='hHh lpR fFf', container)
q-item-section
q-item-label {{t(`admin.users.changePassword`)}}
q-item-label(caption) {{t(`admin.users.changePasswordHint`)}}
q-item-label(caption): strong(:class='localAuth.password ? `text-positive` : `text-negative`') {{localAuth.password ? t(`admin.users.pwdSet`) : t(`admin.users.pwdNotSet`)}}
q-item-label(caption): strong(:class='localAuth.isPasswordSet ? `text-positive` : `text-negative`') {{localAuth.isPasswordSet ? t(`admin.users.pwdSet`) : t(`admin.users.pwdNotSet`)}}
q-item-section(side)
q-btn.acrylic-btn(
flat
@ -316,7 +319,7 @@ q-layout(view='hHh lpR fFf', container)
q-item-label(caption) {{t(`admin.users.tfaRequiredHint`)}}
q-item-section(avatar)
q-toggle(
v-model='localAuth.tfaRequired'
v-model='localAuth.isTfaRequired'
color='primary'
checked-icon='las la-check'
unchecked-icon='las la-times'
@ -328,7 +331,7 @@ q-layout(view='hHh lpR fFf', container)
q-item-section
q-item-label {{t(`admin.users.tfaInvalidate`)}}
q-item-label(caption) {{t(`admin.users.tfaInvalidateHint`)}}
q-item-label(caption): strong(:class='localAuth.tfaSecret ? `text-positive` : `text-negative`') {{localAuth.tfaSecret ? t(`admin.users.tfaSet`) : t(`admin.users.tfaNotSet`)}}
q-item-label(caption): strong(:class='localAuth.isTfaSetup ? `text-positive` : `text-negative`') {{localAuth.isTfaSetup ? t(`admin.users.tfaSet`) : t(`admin.users.tfaNotSet`)}}
q-item-section(side)
q-btn.acrylic-btn(
flat
@ -348,14 +351,14 @@ q-layout(view='hHh lpR fFf', container)
) {{t('admin.users.noLinkedProviders')}}
template(
v-for='(prv, idx) in linkedAuthProviders'
:key='prv._id'
:key='prv.authId'
)
q-separator.q-my-sm(inset, v-if='idx > 0')
q-item
blueprint-icon(icon='google', :hue-rotate='-45')
blueprint-icon(:icon='prv.strategyIcon', :hue-rotate='-45')
q-item-section
q-item-label {{prv._moduleName}}
q-item-label(caption) {{prv.key}}
q-item-label {{prv.authName}}
q-item-label(caption) {{prv.config.key}}
q-page(v-else-if='route.params.section === `groups`')
.q-pa-md
@ -506,7 +509,7 @@ q-layout(view='hHh lpR fFf', container)
<script setup>
import gql from 'graphql-tag'
import { cloneDeep, find, findKey, map, some } from 'lodash-es'
import { cloneDeep, find, map, some } from 'lodash-es'
import { DateTime } from 'luxon'
import { useI18n } from 'vue-i18n'
@ -515,6 +518,7 @@ import { computed, onMounted, reactive, watch } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { useAdminStore } from 'src/stores/admin'
import { useFlagsStore } from 'src/stores/flags'
import UserChangePwdDialog from './UserChangePwdDialog.vue'
import UtilCodeEditor from './UtilCodeEditor.vue'
@ -526,6 +530,7 @@ const $q = useQuasar()
// STORES
const adminStore = useAdminStore()
const flagsStore = useFlagsStore()
// ROUTER
@ -553,7 +558,7 @@ const state = reactive({
const sections = [
{ key: 'overview', text: t('admin.users.overview'), icon: 'las la-user' },
{ key: 'activity', text: t('admin.users.activity'), icon: 'las la-chart-area' },
{ key: 'activity', text: t('admin.users.activity'), icon: 'las la-chart-area', disabled: true },
{ key: 'auth', text: t('admin.users.auth'), icon: 'las la-key' },
{ key: 'groups', text: t('admin.users.groups'), icon: 'las la-users' },
{ key: 'metadata', text: t('admin.users.metadata'), icon: 'las la-clipboard-list' },
@ -576,17 +581,13 @@ const metadata = computed({
}
})
const localAuthId = computed(() => {
return findKey(state.user.auth, ['module', 'local'])
})
const localAuth = computed({
get () {
return localAuthId.value ? state.user.auth?.[localAuthId.value] || {} : {}
return find(state.user?.auth, ['strategyKey', 'local'])?.config ?? {}
},
set (val) {
if (localAuthId.value) {
state.user.auth[localAuthId.value] = val
if (localAuth.value.authId) {
find(state.user.auth, ['strategyKey', 'local']).config = val
}
}
})
@ -594,12 +595,7 @@ const localAuth = computed({
const linkedAuthProviders = computed(() => {
if (!state.user?.auth) { return [] }
return map(state.user.auth, (obj, key) => {
return {
...obj,
_id: key
}
}).filter(prv => prv.module !== 'local')
return state.user.auth.filter(prv => prv.strategyKey !== 'local')
})
// WATCHERS
@ -630,7 +626,13 @@ async function fetchUser () {
isSystem
isVerified
isActive
auth
auth {
authId
authName
strategyKey
strategyIcon
config
}
meta
prefs
lastLoginAt

@ -1610,5 +1610,6 @@
"admin.flags.experimental.hint": "Enable unstable / unfinished features. DO NOT enable in a production environment!",
"admin.flags.advanced.label": "Custom Configuration",
"admin.flags.advanced.hint": "Set custom configuration flags. Note that all values are public to all users! Do not insert senstive data.",
"admin.flags.saveSuccess": "Flags have been updated successfully."
"admin.flags.saveSuccess": "Flags have been updated successfully.",
"fileman.copyURLSuccess": "URL has been copied to the clipboard."
}

@ -70,26 +70,27 @@ q-layout.admin(view='hHh Lpr lff')
q-item-section(avatar)
q-icon(name='img:/_assets/icons/fluent-web.svg')
q-item-section {{ t('admin.general.title') }}
q-item(:to='`/_admin/` + adminStore.currentSiteId + `/analytics`', v-ripple, active-class='bg-primary text-white', disabled)
q-item-section(avatar)
q-icon(name='img:/_assets/icons/fluent-bar-chart.svg')
q-item-section {{ t('admin.analytics.title') }}
q-item(:to='`/_admin/` + adminStore.currentSiteId + `/approvals`', v-ripple, active-class='bg-primary text-white', disabled)
q-item-section(avatar)
q-icon(name='img:/_assets/icons/fluent-inspection.svg')
q-item-section {{ t('admin.approval.title') }}
q-item(:to='`/_admin/` + adminStore.currentSiteId + `/comments`', v-ripple, active-class='bg-primary text-white', disabled)
q-item-section(avatar)
q-icon(name='img:/_assets/icons/fluent-comments.svg')
q-item-section {{ t('admin.comments.title') }}
q-item(:to='`/_admin/` + adminStore.currentSiteId + `/blocks`', v-ripple, active-class='bg-primary text-white', disabled)
q-item-section(avatar)
q-icon(name='img:/_assets/icons/fluent-rfid-tag.svg')
q-item-section {{ t('admin.blocks.title') }}
q-item(:to='`/_admin/` + adminStore.currentSiteId + `/editors`', v-ripple, active-class='bg-primary text-white', disabled)
q-item-section(avatar)
q-icon(name='img:/_assets/icons/fluent-cashbook.svg')
q-item-section {{ t('admin.editors.title') }}
template(v-if='flagsStore.experimental')
q-item(:to='`/_admin/` + adminStore.currentSiteId + `/analytics`', v-ripple, active-class='bg-primary text-white', disabled)
q-item-section(avatar)
q-icon(name='img:/_assets/icons/fluent-bar-chart.svg')
q-item-section {{ t('admin.analytics.title') }}
q-item(:to='`/_admin/` + adminStore.currentSiteId + `/approvals`', v-ripple, active-class='bg-primary text-white', disabled)
q-item-section(avatar)
q-icon(name='img:/_assets/icons/fluent-inspection.svg')
q-item-section {{ t('admin.approval.title') }}
q-item(:to='`/_admin/` + adminStore.currentSiteId + `/comments`', v-ripple, active-class='bg-primary text-white', disabled)
q-item-section(avatar)
q-icon(name='img:/_assets/icons/fluent-comments.svg')
q-item-section {{ t('admin.comments.title') }}
q-item(:to='`/_admin/` + adminStore.currentSiteId + `/blocks`', v-ripple, active-class='bg-primary text-white', disabled)
q-item-section(avatar)
q-icon(name='img:/_assets/icons/fluent-rfid-tag.svg')
q-item-section {{ t('admin.blocks.title') }}
q-item(:to='`/_admin/` + adminStore.currentSiteId + `/editors`', v-ripple, active-class='bg-primary text-white', disabled)
q-item-section(avatar)
q-icon(name='img:/_assets/icons/fluent-cashbook.svg')
q-item-section {{ t('admin.editors.title') }}
q-item(:to='`/_admin/` + adminStore.currentSiteId + `/locale`', v-ripple, active-class='bg-primary text-white')
q-item-section(avatar)
q-icon(name='img:/_assets/icons/fluent-language.svg')
@ -134,7 +135,7 @@ q-layout.admin(view='hHh Lpr lff')
q-item-section {{ t('admin.api.title') }}
q-item-section(side)
status-light(:color='adminStore.info.isApiEnabled ? `positive` : `negative`')
q-item(to='/_admin/audit', v-ripple, active-class='bg-primary text-white', disabled)
q-item(to='/_admin/audit', v-ripple, active-class='bg-primary text-white', disabled, v-if='flagsStore.experimental')
q-item-section(avatar)
q-icon(name='img:/_assets/icons/fluent-event-log.svg')
q-item-section {{ t('admin.audit.title') }}
@ -156,7 +157,7 @@ q-layout.admin(view='hHh Lpr lff')
q-item-section {{ t('admin.mail.title') }}
q-item-section(side)
status-light(:color='adminStore.info.isMailConfigured ? `positive` : `warning`')
q-item(to='/_admin/rendering', v-ripple, active-class='bg-primary text-white', disabled)
q-item(to='/_admin/rendering', v-ripple, active-class='bg-primary text-white', disabled, v-if='flagsStore.experimental')
q-item-section(avatar)
q-icon(name='img:/_assets/icons/fluent-rich-text-converter.svg')
q-item-section {{ t('admin.rendering.title') }}
@ -170,7 +171,7 @@ q-layout.admin(view='hHh Lpr lff')
q-item-section(avatar)
q-icon(name='img:/_assets/icons/fluent-protect.svg')
q-item-section {{ t('admin.security.title') }}
q-item(to='/_admin/ssl', v-ripple, active-class='bg-primary text-white', disabled)
q-item(to='/_admin/ssl', v-ripple, active-class='bg-primary text-white', disabled, v-if='flagsStore.experimental')
q-item-section(avatar)
q-icon(name='img:/_assets/icons/fluent-security-ssl.svg')
q-item-section {{ t('admin.ssl.title') }}
@ -218,8 +219,9 @@ import { defineAsyncComponent, onMounted, reactive, ref, watch } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { useI18n } from 'vue-i18n'
import { useAdminStore } from '../stores/admin'
import { useSiteStore } from '../stores/site'
import { useAdminStore } from 'src/stores/admin'
import { useFlagsStore } from 'src/stores/flags'
import { useSiteStore } from 'src/stores/site'
// COMPONENTS
@ -237,6 +239,7 @@ const $q = useQuasar()
// STORES
const adminStore = useAdminStore()
const flagsStore = useFlagsStore()
const siteStore = useSiteStore()
// ROUTER

@ -234,6 +234,7 @@ q-page.column
aria-label='Page Data'
@click='togglePageData'
disable
v-if='flagsStore.experimental'
)
q-tooltip(anchor='center left' self='center right') Page Data
q-separator.q-my-sm(inset)
@ -322,6 +323,7 @@ import { useI18n } from 'vue-i18n'
import { DateTime } from 'luxon'
import { useEditorStore } from 'src/stores/editor'
import { useFlagsStore } from 'src/stores/flags'
import { usePageStore } from 'src/stores/page'
import { useSiteStore } from 'src/stores/site'
@ -349,6 +351,7 @@ const $q = useQuasar()
// STORES
const editorStore = useEditorStore()
const flagsStore = useFlagsStore()
const pageStore = usePageStore()
const siteStore = useSiteStore()
@ -702,6 +705,7 @@ async function saveChanges () {
width: 40px;
border-radius: 4px !important;
background-color: rgba(0,0,0,.75);
backdrop-filter: blur(5px);
color: #FFF;
position: fixed;
right: 486px;

@ -4127,6 +4127,13 @@ __metadata:
languageName: node
linkType: hard
"fuse.js@npm:6.6.2":
version: 6.6.2
resolution: "fuse.js@npm:6.6.2"
checksum: 17ae758ce205276ebd88bd9c9f088a100be0b4896abac9f6b09847151269d1690f41d7f98ff5813d4a58973162dbd99d0072ce807020fee6f9de60170f6b08eb
languageName: node
linkType: hard
"gauge@npm:^4.0.3":
version: 4.0.4
resolution: "gauge@npm:4.0.4"
@ -7179,6 +7186,7 @@ __metadata:
eslint-plugin-vue: 9.7.0
filesize: 10.0.5
filesize-parser: 1.5.0
fuse.js: 6.6.2
graphql: 16.6.0
graphql-tag: 2.12.6
js-cookie: 3.0.1

Loading…
Cancel
Save