feat: file manager (wip)

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

@ -618,7 +618,7 @@ module.exports = {
return obj.icon || 'las la-file-alt'
},
password (obj) {
return obj ? '********' : ''
return obj.password ? '********' : ''
},
async tags (obj) {
return WIKI.db.pages.relatedQuery('tags').for(obj.id)

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="96px" height="96px"><path fill="#90CAF9" d="M40 45L8 45 8 3 30 3 40 13z"/><path fill="#E1F5FE" d="M38.5 14L29 14 29 4.5z"/><path fill="#1976D2" d="M16 21H33V23H16zM16 25H29V27H16zM16 29H33V31H16zM16 33H29V35H16z"/></svg>

After

Width:  |  Height:  |  Size: 288 B

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="96px" height="96px"><path fill="#FF5722" d="M40 45L8 45 8 3 30 3 40 13z"/><path fill="#FBE9E7" d="M38.5 14L29 14 29 4.5z"/><path fill="#FFEBEE" d="M15.81 29.5V33H13.8v-9.953h3.391c.984 0 1.77.306 2.355.916s.878 1.403.878 2.379-.29 1.745-.868 2.311S18.175 29.5 17.149 29.5H15.81zM15.81 27.825h1.381c.383 0 .679-.125.889-.376s.314-.615.314-1.094c0-.497-.107-.892-.321-1.187-.214-.293-.501-.442-.861-.447H15.81V27.825zM21.764 33v-9.953h2.632c1.162 0 2.089.369 2.778 1.107.691.738 1.043 1.75 1.057 3.035v1.613c0 1.308-.346 2.335-1.035 3.079C26.504 32.628 25.553 33 24.341 33H21.764zM23.773 24.722v6.61h.602c.67 0 1.142-.177 1.415-.53.273-.353.417-.962.431-1.828v-1.729c0-.93-.13-1.578-.39-1.944-.26-.367-.702-.56-1.326-.578H23.773zM34.807 28.939h-3.124V33h-2.01v-9.953h5.51v1.675h-3.5v2.55h3.124V28.939z"/></svg>

After

Width:  |  Height:  |  Size: 876 B

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="96px" height="96px"><linearGradient id="WQEfvoQAcpQgQgyjQQ4Hqa" x1="24" x2="24" y1="6.708" y2="14.977" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#eba600"/><stop offset="1" stop-color="#c28200"/></linearGradient><path fill="url(#WQEfvoQAcpQgQgyjQQ4Hqa)" d="M24.414,10.414l-2.536-2.536C21.316,7.316,20.553,7,19.757,7L5,7C3.895,7,3,7.895,3,9l0,30 c0,1.105,0.895,2,2,2l38,0c1.105,0,2-0.895,2-2V13c0-1.105-0.895-2-2-2l-17.172,0C25.298,11,24.789,10.789,24.414,10.414z"/><linearGradient id="WQEfvoQAcpQgQgyjQQ4Hqb" x1="24" x2="24" y1="10.854" y2="40.983" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#ffd869"/><stop offset="1" stop-color="#fec52b"/></linearGradient><path fill="url(#WQEfvoQAcpQgQgyjQQ4Hqb)" d="M21.586,14.414l3.268-3.268C24.947,11.053,25.074,11,25.207,11H43c1.105,0,2,0.895,2,2v26 c0,1.105-0.895,2-2,2H5c-1.105,0-2-0.895-2-2V15.5C3,15.224,3.224,15,3.5,15h16.672C20.702,15,21.211,14.789,21.586,14.414z"/></svg>

After

Width:  |  Height:  |  Size: 1020 B

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="96px" height="96px"><linearGradient id="NWlHyh8VOXjBX4UVkfWP8a" x1="17.326" x2="30.777" y1="14" y2="14" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#f44f5a"/><stop offset=".443" stop-color="#ee3d4a"/><stop offset="1" stop-color="#e52030"/></linearGradient><path fill="url(#NWlHyh8VOXjBX4UVkfWP8a)" d="M24,4c-3.866,0-7,3.134-7,7s7,13,7,13s7-9.134,7-13S27.866,4,24,4z"/><linearGradient id="NWlHyh8VOXjBX4UVkfWP8b" x1="29.687" x2="35.124" y1="11.916" y2="24.746" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#f44f5a"/><stop offset=".443" stop-color="#ee3d4a"/><stop offset="1" stop-color="#e52030"/></linearGradient><path fill="url(#NWlHyh8VOXjBX4UVkfWP8b)" d="M41.321,14c-1.933-3.348-6.214-4.495-9.562-2.562S24,24,24,24s11.41,1.495,14.758-0.438 C42.106,21.629,43.254,17.348,41.321,14z"/><linearGradient id="NWlHyh8VOXjBX4UVkfWP8c" x1="36.155" x2="29.018" y1="22.884" y2="35.373" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#f44f5a"/><stop offset=".443" stop-color="#ee3d4a"/><stop offset="1" stop-color="#e52030"/></linearGradient><path fill="url(#NWlHyh8VOXjBX4UVkfWP8c)" d="M41.321,34c1.933-3.348,0.786-7.629-2.562-9.562C35.41,22.505,24,24,24,24 s4.41,10.629,7.758,12.562C35.106,38.495,39.388,37.348,41.321,34z"/><linearGradient id="NWlHyh8VOXjBX4UVkfWP8d" x1="31.26" x2="16.81" y1="34" y2="34" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#f44f5a"/><stop offset=".443" stop-color="#ee3d4a"/><stop offset="1" stop-color="#e52030"/></linearGradient><path fill="url(#NWlHyh8VOXjBX4UVkfWP8d)" d="M24,44c3.866,0,7-3.134,7-7s-7-13-7-13s-7,9.134-7,13S20.134,44,24,44z"/><linearGradient id="NWlHyh8VOXjBX4UVkfWP8e" x1="18.93" x2="11.708" y1="35.082" y2="22.847" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#f44f5a"/><stop offset=".443" stop-color="#ee3d4a"/><stop offset="1" stop-color="#e52030"/></linearGradient><path fill="url(#NWlHyh8VOXjBX4UVkfWP8e)" d="M6.679,34c1.933,3.348,6.214,4.495,9.562,2.562C19.59,34.629,24,24,24,24 s-11.41-1.495-14.758,0.438S4.746,30.652,6.679,34z"/><linearGradient id="NWlHyh8VOXjBX4UVkfWP8f" x1="11.753" x2="18.55" y1="25.277" y2="13.382" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#f44f5a"/><stop offset=".443" stop-color="#ee3d4a"/><stop offset="1" stop-color="#e52030"/></linearGradient><path fill="url(#NWlHyh8VOXjBX4UVkfWP8f)" d="M16.242,11.438C12.894,9.505,8.612,10.652,6.68,14c-1.933,3.348-0.786,7.629,2.562,9.562 C12.59,25.495,24,24,24,24s-5.787-7.561-6.826-11.835C16.858,11.862,16.545,11.613,16.242,11.438z"/><path fill="#d61827" d="M24,12.143c-0.552,0-1,0.448-1,1C23,13.695,24,24,24,24s1-10.305,1-10.857 C25,12.591,24.552,12.143,24,12.143z"/><path fill="#d61827" d="M24,36c0.552,0,1-0.448,1-1s-1-10.857-1-10.857S23,34.448,23,35S23.448,36,24,36z"/><path fill="#d61827" d="M13.67,30.036c0.276,0.478,0.888,0.642,1.366,0.366s8.903-6.295,8.903-6.295 s-9.424,4.286-9.903,4.563S13.393,29.557,13.67,30.036z"/><path fill="#d61827" d="M34.33,18.107c-0.276-0.478-0.888-0.642-1.366-0.366c-0.478,0.276-8.903,6.295-8.903,6.295 s9.424-4.286,9.903-4.563C34.443,19.197,34.607,18.585,34.33,18.107z"/><path fill="#d61827" d="M34.33,30.036c0.276-0.478,0.112-1.09-0.366-1.366c-0.478-0.276-9.903-4.563-9.903-4.563 s8.424,6.018,8.903,6.295C33.443,30.678,34.054,30.514,34.33,30.036z"/><path fill="#d61827" d="M13.67,18.107c-0.276,0.478-0.112,1.09,0.366,1.366c0.478,0.276,9.903,4.563,9.903,4.563 s-8.424-6.018-8.903-6.295S13.946,17.629,13.67,18.107z"/><linearGradient id="NWlHyh8VOXjBX4UVkfWP8g" x1="19.321" x2="27.528" y1="19.321" y2="27.528" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#fed100"/><stop offset="1" stop-color="#e36001"/></linearGradient><path fill="url(#NWlHyh8VOXjBX4UVkfWP8g)" d="M24,19c-2.761,0-5,2.239-5,5s2.239,5,5,5s5-2.239,5-5S26.761,19,24,19z"/></svg>

After

Width:  |  Height:  |  Size: 3.8 KiB

@ -0,0 +1,169 @@
<template lang="pug">
q-layout(view='hHh lpR fFf', container)
q-header.card-header
q-toolbar(dark)
q-icon(name='img:/_assets/icons/fluent-folder.svg', left, size='md')
span {{t(`fileman.title`)}}
q-toolbar(dark)
q-input(
dark
v-model='state.search'
standout='bg-white text-dark'
dense
ref='searchField'
style='width: 100%;'
label='Search folder...'
)
template(v-slot:prepend)
q-icon(name='las la-search')
template(v-slot:append)
q-icon.cursor-pointer(
name='las la-times'
@click='state.search=``'
v-if='state.search.length > 0'
:color='$q.dark.isActive ? `blue` : `grey-4`'
)
q-toolbar(dark)
q-space
q-btn.q-mr-sm(
flat
dense
color='blue-4'
:aria-label='t(`common.actions.upload`)'
icon='las la-plus-circle'
@click=''
)
q-tooltip(anchor='bottom middle', self='top middle') {{t(`common.actions.upload`)}}
q-btn(
flat
dense
color='positive'
:aria-label='t(`common.actions.upload`)'
icon='las la-cloud-upload-alt'
@click=''
)
q-tooltip(anchor='bottom middle', self='top middle') {{t(`common.actions.upload`)}}
q-separator.q-mx-sm(vertical, dark, inset)
q-btn.q-mr-sm(
flat
dense
color='blue-grey-4'
:aria-label='t(`common.actions.upload`)'
icon='las la-check-square'
@click=''
)
q-tooltip(anchor='bottom middle', self='top middle') {{t(`common.actions.upload`)}}
q-btn.q-mr-sm(
flat
dense
color='blue-grey-4'
:aria-label='t(`common.actions.upload`)'
icon='las la-list'
@click=''
)
q-tooltip(anchor='bottom middle', self='top middle') {{t(`common.actions.upload`)}}
q-btn(
flat
dense
color='blue-grey-4'
:aria-label='t(`common.actions.refresh`)'
icon='las la-redo-alt'
@click=''
:loading='state.loading > 0'
)
q-tooltip(anchor='bottom middle', self='top middle') {{t(`common.actions.refresh`)}}
q-separator.q-mx-sm(vertical, dark, inset)
q-btn(
flat
dense
color='white'
:aria-label='t(`common.actions.close`)'
icon='las la-times'
@click='close'
)
q-tooltip(anchor='bottom middle', self='top middle') {{t(`common.actions.close`)}}
q-drawer.bg-blue-grey-1(:model-value='true', :width='350')
q-list(padding, v-if='state.loading < 1')
q-drawer.bg-grey-1(:model-value='true', :width='350', side='right')
q-list(padding, v-if='state.loading < 1')
q-page-container
q-page.bg-white
q-bar.bg-grey-1
small.text-caption.text-grey-7 / foo / bar
q-list.fileman-filelist
q-item(clickable)
q-item-section(avatar)
q-icon(name='img:/_assets/icons/fluent-folder.svg', size='xl')
q-item-section
q-item-label Beep Boop
q-item-label(caption) 19 Items
q-item-section(side)
.text-caption 1
q-item(clickable)
q-item-section(avatar)
q-icon(name='img:/_assets/icons/fluent-folder.svg', size='xl')
q-item-section
q-item-label Beep Boop
q-item-label(caption) 19 Items
q-item-section(side)
.text-caption 1
q-item(clickable)
q-item-section(avatar)
q-icon(name='img:/_assets/icons/color-document.svg', size='xl')
q-item-section
q-item-label Beep Boop
q-item-label(caption) Markdown
q-item-section(side)
.text-caption 1
q-item(clickable)
q-item-section(avatar)
q-icon(name='img:/_assets/icons/color-pdf.svg', size='xl')
q-item-section
q-item-label Beep Boop
q-item-label(caption) 4 pages
q-item-section(side)
.text-caption 2022/01/01
</template>
<script setup>
import { useI18n } from 'vue-i18n'
import { reactive } from 'vue'
import { useSiteStore } from 'src/stores/site'
// STORES
const siteStore = useSiteStore()
// I18N
const { t } = useI18n()
// DATA
const state = reactive({
loading: 0,
search: ''
})
// METHODS
function close () {
siteStore.overlay = null
}
</script>
<style lang="scss">
.fileman {
&-filelist {
padding: 8px 12px;
> .q-item {
padding: 8px 6px;
border-radius: 8px;
}
}
}
</style>

@ -80,12 +80,22 @@ q-header.bg-header.text-white.site-header(
flat
round
dense
icon='las la-tools'
icon='las la-folder-open'
color='positive'
aria-label='File Manager'
@click='toggleFileManager'
)
q-tooltip File Manager
q-btn.q-ml-md(
flat
round
dense
icon='las la-tools'
color='pink'
to='/_admin'
aria-label='Administration'
:aria-label='t(`common.header.admin`)'
)
q-tooltip Administration
q-tooltip {{ t('common.header.admin') }}
account-menu
</template>
@ -116,6 +126,12 @@ const { t } = useI18n()
const state = reactive({
search: ''
})
// METHODS
function toggleFileManager () {
siteStore.overlay = 'FileManager'
}
</script>
<style lang="scss">

@ -24,7 +24,7 @@ q-menu(
blueprint-icon(icon='advance')
q-item-section.q-pr-sm New Redirection
q-separator.q-my-sm(inset)
q-item(clickable, to='/_assets')
q-item(clickable, @click='openFileManager')
blueprint-icon(icon='add-image')
q-item-section.q-pr-sm Upload Media Asset
</template>
@ -34,6 +34,7 @@ import { useI18n } from 'vue-i18n'
import { useQuasar } from 'quasar'
import { usePageStore } from 'src/stores/page'
import { useSiteStore } from 'src/stores/site'
// QUASAR
@ -42,6 +43,7 @@ const $q = useQuasar()
// STORES
const pageStore = usePageStore()
const siteStore = useSiteStore()
// I18N
@ -53,4 +55,8 @@ function create (editor) {
window.location.assign('/_edit/new')
// pageStore.pageCreate({ editor })
}
function openFileManager () {
siteStore.overlay = 'FileManager'
}
</script>

@ -205,7 +205,7 @@ function create () {
function persist () {
const rels = cloneDeep(pageStore.relations)
for (const rel of rels) {
if (rel.id === state.editId) {
if (rel.id === props.editId) {
rel.position = state.pos
rel.label = state.label
rel.caption = state.caption

@ -1,20 +1,20 @@
<template lang="pug">
q-card.page-scripts-dialog(style='width: 860px; max-width: 90vw;')
q-toolbar.bg-primary.text-white
.text-subtitle2 {{$t('editor.pageScripts.title')}} - {{$t('editor.props.' + mode)}}
.text-subtitle2 {{t('editor.pageScripts.title')}} - {{t('editor.props.' + props.mode)}}
q-space
q-chip(
square
style='background-color: rgba(0,0,0,.1)'
text-color='white'
)
.text-caption {{this.languageLabel}}
.text-caption {{languageLabel}}
div(style='min-height: 450px;')
q-no-ssr(:placeholder='$t(`common.loading`)')
q-no-ssr(:placeholder='t(`common.loading`)')
util-code-editor(
v-if='showEditor'
v-if='state.showEditor'
ref='editor'
v-model='content'
v-model='state.content'
:language='language'
:min-height='450'
)
@ -22,7 +22,7 @@ q-card.page-scripts-dialog(style='width: 860px; max-width: 90vw;')
q-space
q-btn.acrylic-btn(
icon='las la-times'
:label='$t(`common.actions.discard`)'
:label='t(`common.actions.discard`)'
color='grey-7'
padding='xs md'
v-close-popup
@ -30,7 +30,7 @@ q-card.page-scripts-dialog(style='width: 860px; max-width: 90vw;')
)
q-btn(
icon='las la-check'
:label='$t(`common.actions.save`)'
:label='t(`common.actions.save`)'
unelevated
color='primary'
padding='xs md'
@ -39,28 +39,49 @@ q-card.page-scripts-dialog(style='width: 860px; max-width: 90vw;')
)
</template>
<script>
<script setup>
import { computed, nextTick, onMounted, reactive } from 'vue'
import { useQuasar } from 'quasar'
import { useI18n } from 'vue-i18n'
import UtilCodeEditor from './UtilCodeEditor.vue'
export default {
components: {
UtilCodeEditor
},
props: {
import { usePageStore } from 'src/stores/page'
import { useSiteStore } from 'src/stores/site'
// PROPS
const props = defineProps({
mode: {
type: String,
default: 'css'
}
},
data () {
return {
})
// QUASAR
const $q = useQuasar()
// STORES
const pageStore = usePageStore()
const siteStore = useSiteStore()
// I18N
const { t } = useI18n()
// DATA
const state = reactive({
content: '',
showEditor: false
}
},
computed: {
language () {
switch (this.mode) {
})
// COMPUTED
const language = computed(() => {
switch (props.mode) {
case 'jsLoad':
case 'jsUnload':
return 'javascript'
@ -69,9 +90,10 @@ export default {
default:
return 'plaintext'
}
},
languageLabel () {
switch (this.language) {
})
const languageLabel = computed(() => {
switch (language.value) {
case 'javascript':
return 'Javascript'
case 'css':
@ -79,25 +101,30 @@ export default {
default:
return 'Plain Text'
}
},
contentStoreKey () {
return 'script' + this.mode.charAt(0).toUpperCase() + this.mode.slice(1)
}
},
mounted () {
this.content = this.$store.get(`page/${this.contentStoreKey}`)
this.$nextTick(() => {
})
const contentStoreKey = computed(() => {
return 'script' + props.mode.charAt(0).toUpperCase() + props.mode.slice(1)
})
// METHODS
function persist () {
pageStore.$patch({
[contentStoreKey]: state.content
})
}
// MOUNTED
onMounted(() => {
state.content = pageStore[contentStoreKey.value]
nextTick(() => {
setTimeout(() => {
this.showEditor = true
state.showEditor = true
}, 250)
})
},
methods: {
persist () {
this.$store.set(`page/${this.contentStoreKey}`, this.content)
}
}
}
})
</script>
<style lang="scss">

@ -190,6 +190,8 @@
"admin.general.defaultTimeFormatHint": "The default time format for new users.",
"admin.general.defaultTimezone": "Default Timezone",
"admin.general.defaultTimezoneHint": "The default timezone for new users.",
"admin.general.defaultTocDepth": "Default ToC Depth",
"admin.general.defaultTocDepthHint": "The default minimum and maximum header levels to show in the table of contents.",
"admin.general.displaySiteTitle": "Display Site Title",
"admin.general.displaySiteTitleHint": "Should the site title be displayed next to the logo? If your logo isn't square and contain your brand name, turn this option off.",
"admin.general.favicon": "Favicon",
@ -289,6 +291,8 @@
"admin.groups.users": "Users",
"admin.groups.usersCount": "0 user | 1 user | {count} users",
"admin.groups.usersNone": "This group doesn't have any user yet.",
"admin.icons.subtitle": "Configure the icon packs available for use",
"admin.icons.title": "Icons",
"admin.instances.activeConnections": "Active Connections",
"admin.instances.activeListeners": "Active Listeners",
"admin.instances.firstSeen": "First Seen",
@ -1177,18 +1181,18 @@
"common.error.title": "Error",
"common.error.unauthorized.hint": "You don't have the required permissions to access this page.",
"common.error.unauthorized.title": "Unauthorized",
"common.error.unexpected": "An unexpected error occurred.",
"common.error.unknownsite.hint": "There's no wiki site at this host.",
"common.error.unknownsite.title": "Unknown Site",
"common.error.unexpected": "An unexpected error occurred.",
"common.field.createdOn": "Created On",
"common.field.id": "ID",
"common.field.lastUpdated": "Last Updated",
"common.field.name": "Name",
"common.field.task": "Task",
"common.footerGeneric": "Powered by {link}, an open source project.",
"common.footerPoweredBy": "Powered by {link}",
"common.footerCopyright": "© {year} {company}. All rights reserved.",
"common.footerGeneric": "Powered by {link}, an open source project.",
"common.footerLicense": "Content is available under the {license}, by {company}.",
"common.footerPoweredBy": "Powered by {link}",
"common.header.account": "Account",
"common.header.admin": "Administration",
"common.header.assets": "Assets",
@ -1423,6 +1427,7 @@
"editor.props.dateRangeHint": "Select the start and end date for this page publication. The page will only be accessible to users with read access within the selected date range.",
"editor.props.draft": "Draft",
"editor.props.draftHint": "Visible to users with write access only.",
"editor.props.icon": "Icon",
"editor.props.info": "Info",
"editor.props.jsLoad": "Javascript - On Load",
"editor.props.jsLoadHint": "Execute javascript once the page is loaded",
@ -1465,6 +1470,7 @@
"editor.unsaved.body": "You have unsaved changes. Are you sure you want to leave the editor and discard any modifications you made since the last save?",
"editor.unsaved.title": "Discard Unsaved Changes?",
"editor.unsavedWarning": "You have unsaved edits. Are you sure you want to leave the editor?",
"fileman.title": "File Manager",
"history.restore.confirmButton": "Restore",
"history.restore.confirmText": "Are you sure you want to restore this page content as it was on {date}? This version will be copied on top of the current history. As such, newer versions will still be preserved.",
"history.restore.confirmTitle": "Restore page version?",
@ -1476,6 +1482,18 @@
"profile.appearanceHint": "Use the light or dark theme.",
"profile.appearanceLight": "Light",
"profile.auth": "Authentication",
"profile.authChangePassword": "Change Password",
"profile.authInfo": "Your account is associated with the following authentication methods:",
"profile.authLoadingFailed": "Failed to load authentication methods.",
"profile.authModifyTfa": "Modify 2FA",
"profile.authSetTfa": "Set 2FA",
"profile.avatar": "Avatar",
"profile.avatarClearFailed": "Failed to clear profile picture.",
"profile.avatarClearSuccess": "Profile picture cleared successfully.",
"profile.avatarUploadFailed": "Failed to upload user profile picture.",
"profile.avatarUploadHint": "For best results, use a 180x180 image of type JPG or PNG.",
"profile.avatarUploadSuccess": "Profile picture uploaded successfully.",
"profile.avatarUploadTitle": "Upload your user profile picture.",
"profile.darkMode": "Dark Mode",
"profile.darkModeHint": "Change the appareance of the site to a dark theme.",
"profile.dateFormat": "Date Format",
@ -1485,13 +1503,16 @@
"profile.email": "Email Address",
"profile.emailHint": "The email address used for login.",
"profile.groups": "Groups",
"profile.groupsInfo": "You're currently part of the following groups:",
"profile.groupsLoadingFailed": "Failed to load groups.",
"profile.groupsNone": "You're not part of any group.",
"profile.jobTitle": "Job Title",
"profile.jobTitleHint": "Your position in your organization; shown on your profile page.",
"profile.localeDefault": "Locale Default",
"profile.location": "Location",
"profile.locationHint": "Your city and country; shown on your profile page.",
"profile.myInfo": "My Info",
"profile.notifications": "Notifications",
"profile.pages.emptyList": "No pages to display.",
"profile.pages.headerCreatedAt": "Created",
"profile.pages.headerPath": "Path",
@ -1515,6 +1536,7 @@
"profile.timezone": "Timezone",
"profile.timezoneHint": "Set your timezone to display local time correctly.",
"profile.title": "Profile",
"profile.uploadNewAvatar": "Upload New Image",
"profile.viewPublicProfile": "View Public Profile",
"tags.clearSelection": "Clear Selection",
"tags.currentSelection": "Current Selection",
@ -1536,24 +1558,5 @@
"welcome.admin": "Administration Area",
"welcome.createHome": "Create the homepage",
"welcome.subtitle": "Let's get started...",
"welcome.title": "Welcome to Wiki.js!",
"profile.avatar": "Avatar",
"profile.uploadNewAvatar": "Upload New Image",
"profile.avatarUploadTitle": "Upload your user profile picture.",
"profile.avatarUploadHint": "For best results, use a 180x180 image of type JPG or PNG.",
"profile.groupsInfo": "You're currently part of the following groups:",
"profile.groupsNone": "You're not part of any group.",
"profile.authInfo": "Your account is associated with the following authentication methods:",
"profile.authSetTfa": "Set 2FA",
"profile.authModifyTfa": "Modify 2FA",
"profile.authChangePassword": "Change Password",
"profile.authLoadingFailed": "Failed to load authentication methods.",
"profile.notifications": "Notifications",
"profile.avatarUploadSuccess": "Profile picture uploaded successfully.",
"profile.avatarUploadFailed": "Failed to upload user profile picture.",
"profile.avatarClearSuccess": "Profile picture cleared successfully.",
"profile.avatarClearFailed": "Failed to clear profile picture.",
"admin.general.defaultTocDepth": "Default ToC Depth",
"admin.general.defaultTocDepthHint": "The default minimum and maximum header levels to show in the table of contents.",
"editor.props.icon": "Icon"
"welcome.title": "Welcome to Wiki.js!"
}

@ -142,6 +142,10 @@ q-layout.admin(view='hHh Lpr lff')
q-item-section(avatar)
q-icon(name='img:/_assets/icons/fluent-module.svg')
q-item-section {{ t('admin.extensions.title') }}
q-item(to='/_admin/icons', v-ripple, active-class='bg-primary text-white')
q-item-section(avatar)
q-icon(name='img:/_assets/icons/fluent-spring.svg')
q-item-section {{ t('admin.icons.title') }}
q-item(to='/_admin/instances', v-ripple, active-class='bg-primary text-white')
q-item-section(avatar)
q-icon(name='img:/_assets/icons/fluent-network.svg')

@ -76,6 +76,16 @@ q-layout(view='hHh Lpr lff')
round
size='md'
)
q-dialog.main-overlay(
v-model='siteStore.overlayIsShown'
persistent
full-width
full-height
no-shake
transition-show='jump-up'
transition-hide='jump-down'
)
component(:is='overlays[siteStore.overlay]')
footer-nav
</template>
@ -91,6 +101,14 @@ import { useSiteStore } from '../stores/site'
import HeaderNav from '../components/HeaderNav.vue'
import FooterNav from 'src/components/FooterNav.vue'
import LoadingGeneric from 'src/components/LoadingGeneric.vue'
const overlays = {
FileManager: defineAsyncComponent({
loader: () => import('../components/FileManager.vue'),
loadingComponent: LoadingGeneric
})
}
// QUASAR
@ -150,6 +168,30 @@ body.body--dark {
background-color: $dark-6;
}
.main-overlay {
> .q-dialog__backdrop {
background-color: rgba(0,0,0,.6);
}
> .q-dialog__inner {
padding: 24px 64px;
@media (max-width: $breakpoint-sm-max) {
padding: 0;
}
> .q-layout-container {
@at-root .body--light & {
background-image: linear-gradient(to bottom, $dark-5 10px, $grey-3 11px, $grey-4);
}
@at-root .body--dark & {
background-image: linear-gradient(to bottom, $dark-4 10px, $dark-4 11px, $dark-3);
}
border-radius: 6px;
box-shadow: 0 0 0 2px rgba(0,0,0,.5);
}
}
}
.q-footer {
.q-bar {
@at-root .body--light & {

@ -0,0 +1,116 @@
<template lang='pug'>
q-page.admin-icons
.row.q-pa-md.items-center
.col-auto
img.admin-icon.admin-icons-icon.animated.fadeInLeft(src='/_assets/icons/fluent-spring.svg')
.col.q-pl-md
.text-h5.text-primary.animated.fadeInLeft {{ t('admin.icons.title') }}
.text-subtitle1.text-grey.animated.fadeInLeft.wait-p2s {{ t('admin.icons.subtitle') }}
.col-auto
q-btn.acrylic-btn.q-mr-sm(
icon='las la-question-circle'
flat
color='grey'
:href='siteStore.docsBase + `/system/icons`'
target='_blank'
type='a'
)
q-btn.acrylic-btn(
icon='las la-redo-alt'
flat
color='secondary'
:loading='state.loading > 0'
@click='load'
)
q-separator(inset)
.row.q-pa-md.q-col-gutter-md
.col-12
q-card.shadow-1 Beep boop
</template>
<script setup>
import gql from 'graphql-tag'
import { cloneDeep } from 'lodash-es'
import { useI18n } from 'vue-i18n'
import { useMeta, useQuasar } from 'quasar'
import { computed, onMounted, reactive, watch } from 'vue'
import { useAdminStore } from 'src/stores/admin'
import { useSiteStore } from 'src/stores/site'
// QUASAR
const $q = useQuasar()
// STORES
const adminStore = useAdminStore()
const siteStore = useSiteStore()
// I18N
const { t } = useI18n()
// META
useMeta({
title: t('admin.icons.title')
})
// DATA
const state = reactive({
loading: false,
extensions: []
})
// METHODS
async function load () {
state.loading++
$q.loading.show()
const resp = await APOLLO_CLIENT.query({
query: gql`
query fetchExtensions {
systemExtensions {
key
title
description
isInstalled
isInstallable
isCompatible
}
}
`,
fetchPolicy: 'network-only'
})
state.extensions = cloneDeep(resp?.data?.systemExtensions)
$q.loading.hide()
state.loading--
}
// MOUNTED
onMounted(() => {
load()
})
</script>
<style lang='scss'>
.admin-icons {
&-icon {
animation: fadeInLeft .6s forwards, flower-rotate 30s linear infinite;
}
}
@keyframes flower-rotate {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
</style>

@ -48,6 +48,7 @@ const routes = [
// -> System
{ path: 'api', component: () => import('pages/AdminApi.vue') },
{ path: 'extensions', component: () => import('pages/AdminExtensions.vue') },
{ path: 'icons', component: () => import('pages/AdminIcons.vue') },
{ path: 'instances', component: () => import('pages/AdminInstances.vue') },
{ path: 'mail', component: () => import('pages/AdminMail.vue') },
// { path: 'rendering', component: () => import('pages/AdminRendering.vue') },

@ -25,6 +25,7 @@ export const useSiteStore = defineStore('site', {
pageDataTemplates: [],
showSideNav: true,
showSidebar: true,
overlay: 'FileManager',
theme: {
dark: false,
injectCSS: '',
@ -54,7 +55,9 @@ export const useSiteStore = defineStore('site', {
},
docsBase: 'https://next.js.wiki/docs'
}),
getters: {},
getters: {
overlayIsShown: (state) => Boolean(state.overlay)
},
actions: {
async loadSite (hostname) {
try {

Loading…
Cancel
Save