feat: profile page to vue 3 composition + add fonts / width options to theme + site config preload

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

@ -486,10 +486,13 @@ exports.up = async knex => {
injectCSS: '',
injectHead: '',
injectBody: '',
contentWidth: 'full',
sidebarPosition: 'left',
tocPosition: 'right',
showSharingMenu: true,
showPrintBtn: true
showPrintBtn: true,
baseFont: 'roboto',
contentFont: 'roboto'
}
}
})

@ -39,33 +39,33 @@ module.exports = {
})
}, []), 'key')
}))
},
/**
* Fetch active authentication strategies
*/
async authSiteStrategies (obj, args, context, info) {
let strategies = await WIKI.models.authentication.getStrategies()
strategies = strategies.map(stg => {
const strategyInfo = _.find(WIKI.data.authentication, ['key', stg.strategyKey]) || {}
return {
...stg,
strategy: strategyInfo,
config: _.sortBy(_.transform(stg.config, (res, value, key) => {
const configData = _.get(strategyInfo.props, key, false)
if (configData) {
res.push({
key,
value: JSON.stringify({
...configData,
value
})
})
}
}, []), 'key')
}
})
return args.enabledOnly ? _.filter(strategies, 'isEnabled') : strategies
}
// /**
// * Fetch active authentication strategies
// */
// async activeStrategies (obj, args, context, info) {
// let strategies = await WIKI.models.authentication.getStrategies()
// strategies = strategies.map(stg => {
// const strategyInfo = _.find(WIKI.data.authentication, ['key', stg.strategyKey]) || {}
// return {
// ...stg,
// strategy: strategyInfo,
// config: _.sortBy(_.transform(stg.config, (res, value, key) => {
// const configData = _.get(strategyInfo.props, key, false)
// if (configData) {
// res.push({
// key,
// value: JSON.stringify({
// ...configData,
// value
// })
// })
// }
// }, []), 'key')
// }
// })
// return args.enabledOnly ? _.filter(strategies, 'isEnabled') : strategies
// }
},
Mutation: {
/**

@ -7,10 +7,12 @@ extend type Query {
apiState: Boolean
authStrategies(
siteId: UUID
authStrategies: [AuthenticationStrategy]
authSiteStrategies(
siteId: UUID!
enabledOnly: Boolean
): [AuthenticationStrategy]
): [AuthenticationSiteStrategy]
}
extend type Mutation {
@ -70,12 +72,12 @@ extend type Mutation {
# -----------------------------------------------
type AuthenticationStrategy {
key: String!
props: [KeyValuePair] @auth(requires: ["manage:system"])
title: String!
key: String
props: [KeyValuePair]
title: String
description: String
isAvailable: Boolean
useForm: Boolean!
useForm: Boolean
usernameType: String
logo: String
color: String
@ -83,16 +85,16 @@ type AuthenticationStrategy {
icon: String
}
type AuthenticationActiveStrategy {
key: String!
strategy: AuthenticationStrategy!
displayName: String!
order: Int!
isEnabled: Boolean!
config: [KeyValuePair] @auth(requires: ["manage:system"])
selfRegistration: Boolean!
domainWhitelist: [String]! @auth(requires: ["manage:system"])
autoEnrollGroups: [Int]! @auth(requires: ["manage:system"])
type AuthenticationSiteStrategy {
key: String
strategy: AuthenticationStrategy
displayName: String
order: Int
isEnabled: Boolean
config: [KeyValuePair]
selfRegistration: Boolean
domainWhitelist: [String]
autoEnrollGroups: [Int]
}
type AuthenticationLoginResponse {

@ -3,42 +3,42 @@
# ===============================================
extend type Query {
sites: [Site] @auth(requires: ["manage:system"])
sites: [Site]
siteById (
id: UUID!
): Site @auth(requires: ["manage:system"])
): Site
siteByHostname (
hostname: String!
exact: Boolean!
): Site @auth(requires: ["manage:system"])
): Site
}
extend type Mutation {
createSite (
hostname: String!
title: String!
): SiteCreateResponse @auth(requires: ["manage:system"])
): SiteCreateResponse
updateSite (
id: UUID!
patch: SiteUpdateInput!
): DefaultResponse @auth(requires: ["manage:system"])
): DefaultResponse
uploadSiteLogo (
id: UUID!
image: Upload!
): DefaultResponse @auth(requires: ["manage:system"])
): DefaultResponse
uploadSiteFavicon (
id: UUID!
image: Upload!
): DefaultResponse @auth(requires: ["manage:system"])
): DefaultResponse
deleteSite (
id: UUID!
): DefaultResponse @auth(requires: ["manage:system"])
): DefaultResponse
}
# -----------------------------------------------
@ -103,13 +103,22 @@ type SiteTheme {
injectCSS: String
injectHead: String
injectBody: String
contentWidth: SiteThemeWidth
sidebarPosition: SiteThemePosition
tocPosition: SiteThemePosition
showSharingMenu: Boolean
showPrintBtn: Boolean
baseFont: String
contentFont: String
}
enum SiteThemeWidth {
full
centered
}
enum SiteThemePosition {
off
left
right
}
@ -172,8 +181,11 @@ input SiteThemeInput {
injectCSS: String
injectHead: String
injectBody: String
contentWidth: SiteThemeWidth
sidebarPosition: SiteThemePosition
tocPosition: SiteThemePosition
showSharingMenu: Boolean
showPrintBtn: Boolean
baseFont: String
contentFont: String
}

@ -97,7 +97,9 @@ module.exports = class Site extends Model {
sidebarPosition: 'left',
tocPosition: 'right',
showSharingMenu: true,
showPrintBtn: true
showPrintBtn: true,
baseFont: 'roboto',
contentFont: 'roboto'
}
})
})

@ -8,9 +8,7 @@
<meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width">
<title>Wiki.js</title>
<!--preload-links-->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Rubik:wght@300..900&display=swap" rel="stylesheet">
<link href="/_assets/fonts/roboto/roboto.css" rel="stylesheet" />
<style type="text/css">
@keyframes initspinner {
to { transform: rotate(360deg); }
@ -69,7 +67,7 @@
</style>
</noscript>
</head>
<body>
<body class="wiki-root">
<div class="init-loading"></div>
<div id="app"><!-- quasar:entry-point --></div>
</body>

@ -0,0 +1,40 @@
/* roboto-300 - vietnamese_latin-ext_latin_greek-ext_greek_cyrillic-ext_cyrillic */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 300;
src: local(''),
url('/_assets/fonts/roboto/roboto-all-300.woff2') format('woff2')
}
/* roboto-regular - vietnamese_latin-ext_latin_greek-ext_greek_cyrillic-ext_cyrillic */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
src: local(''),
url('/_assets/fonts/roboto/roboto-all-regular.woff2') format('woff2')
}
/* roboto-500 - vietnamese_latin-ext_latin_greek-ext_greek_cyrillic-ext_cyrillic */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 500;
src: local(''),
url('/_assets/fonts/roboto/roboto-all-500.woff2') format('woff2')
}
/* roboto-700 - vietnamese_latin-ext_latin_greek-ext_greek_cyrillic-ext_cyrillic */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 700;
src: local(''),
url('/_assets/fonts/roboto/roboto-all-700.woff2') format('woff2')
}
/* roboto-900 - vietnamese_latin-ext_latin_greek-ext_greek_cyrillic-ext_cyrillic */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 900;
src: local(''),
url('/_assets/fonts/roboto/roboto-all-900.woff2') format('woff2')
}

@ -0,0 +1,39 @@
/* cyrillic-ext */
@font-face {
font-family: 'Rubik';
font-display: swap;
src: url(/_assets/fonts/rubik/rubik-variable-cyrillic-ext.woff2) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Rubik';
font-display: swap;
src: url(/_assets/fonts/rubik/rubik-variable-cyrillic.woff2) format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* hebrew */
@font-face {
font-family: 'Rubik';
font-display: swap;
src: url(/_assets/fonts/rubik/rubik-variable-hebrew.woff2) format('woff2');
unicode-range: U+0590-05FF, U+200C-2010, U+20AA, U+25CC, U+FB1D-FB4F;
}
/* latin-ext */
@font-face {
font-family: 'Rubik';
font-display: swap;
src: url(/_assets/fonts/rubik/rubik-variable-latin-ext.woff2) format('woff2');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Rubik';
font-display: swap;
src: url(/_assets/fonts/rubik/rubik-variable-latin.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
body.wiki-root {
font-family: 'Rubik', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
}

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 40" width="80px" height="80px"><path fill="#b6dcfe" d="M37,29v-8c0,0,0-4-3-5c0,0,4-1,5,5v8H37z"/><path fill="#b6dcfe" d="M26,26c0,1.657,1.343,3,3,3v-6C27.343,23,26,24.343,26,26z"/><polygon fill="#b6dcfe" points="9,8 13,8 21,29.5 17,29.5"/><path fill="#4788c7" d="M21.469,29.325l-8-21.5C13.396,7.63,13.209,7.5,13,7.5H9c-0.001,0-0.003,0.001-0.004,0.001 C8.921,7.502,8.85,7.521,8.784,7.553C8.765,7.562,8.751,7.577,8.733,7.588C8.689,7.617,8.65,7.65,8.616,7.691 C8.609,7.701,8.597,7.705,8.59,7.715C8.581,7.728,8.581,7.744,8.573,7.757C8.562,7.778,8.544,7.794,8.535,7.816l-8.5,21.5 c-0.102,0.257,0.024,0.547,0.281,0.648C0.377,29.989,0.438,30,0.5,30c0.199,0,0.388-0.12,0.465-0.316L3.607,23H14 c0.016,0,0.029-0.008,0.044-0.009l2.487,6.684C16.604,29.87,16.791,30,17,30h4c0.164,0,0.317-0.08,0.41-0.215 C21.504,29.65,21.525,29.479,21.469,29.325z M4.003,22L8.986,9.396L13.676,22H4.003z M17.348,29L9.72,8.5h2.933L20.28,29H17.348z"/><path fill="#4788c7" d="M38.512,17.309c-1.107-1.354-3.235-2.852-7.082-2.304c-0.04,0.006-0.078,0.016-0.113,0.03 c-1.539,0.168-3.374,1.214-5.24,4.2c-0.146,0.233-0.075,0.542,0.159,0.688c0.232,0.146,0.542,0.076,0.688-0.159 c1.932-3.091,3.959-4.282,6.033-3.544c1.326,0.474,2.541,1.967,2.915,3.418C35.439,20.339,33.944,22,29.5,22 c-2.481,0-4.5,1.794-4.5,4s2.019,4,4.5,4c3.349,0,5.417-1.668,6.5-2.922V29.5c0,0.276,0.224,0.5,0.5,0.5h3 c0.276,0,0.5-0.224,0.5-0.5v-7.76C40,20.055,39.472,18.481,38.512,17.309z M26,26c0-1.509,1.306-2.761,3-2.97v5.939 C27.306,28.761,26,27.509,26,26z M30,28.976v-5.99c3.282-0.094,5.065-1.085,6-1.965v4.352C35.684,25.938,33.906,28.747,30,28.976z M39,29h-2v-8.5c0-1.604-0.979-3.368-2.329-4.439c1.248,0.284,2.274,0.912,3.067,1.881C38.552,18.937,39,20.285,39,21.74V29z"/></svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 40" width="80px" height="80px"><path fill="#b6dcfe" d="M34.5 6.5H37.5V32.5H34.5z"/><path fill="#4788c7" d="M37,7v25h-2V7H37 M38,6h-4v27h4V6L38,6z"/><path fill="#b6dcfe" d="M1.5 6.5H4.5V32.5H1.5z"/><path fill="#4788c7" d="M4 7v25H2V7H4M5 6H1v27h4V6L5 6zM30.5 32A.5.5 0 1 0 30.5 33 .5.5 0 1 0 30.5 32zM28.5 32A.5.5 0 1 0 28.5 33 .5.5 0 1 0 28.5 32zM26.5 32A.5.5 0 1 0 26.5 33 .5.5 0 1 0 26.5 32zM24.5 32A.5.5 0 1 0 24.5 33 .5.5 0 1 0 24.5 32zM22.5 32A.5.5 0 1 0 22.5 33 .5.5 0 1 0 22.5 32zM20.5 32A.5.5 0 1 0 20.5 33 .5.5 0 1 0 20.5 32zM18.5 32A.5.5 0 1 0 18.5 33 .5.5 0 1 0 18.5 32zM16.5 32A.5.5 0 1 0 16.5 33 .5.5 0 1 0 16.5 32zM14.5 32A.5.5 0 1 0 14.5 33 .5.5 0 1 0 14.5 32zM12.5 32A.5.5 0 1 0 12.5 33 .5.5 0 1 0 12.5 32zM10.5 32A.5.5 0 1 0 10.5 33 .5.5 0 1 0 10.5 32zM8.5 32A.5.5 0 1 0 8.5 33 .5.5 0 1 0 8.5 32zM6.5 32A.5.5 0 1 0 6.5 33 .5.5 0 1 0 6.5 32zM30.5 6A.5.5 0 1 0 30.5 7 .5.5 0 1 0 30.5 6zM32.5 32A.5.5 0 1 0 32.5 33 .5.5 0 1 0 32.5 32zM32.5 6A.5.5 0 1 0 32.5 7 .5.5 0 1 0 32.5 6zM28.5 6A.5.5 0 1 0 28.5 7 .5.5 0 1 0 28.5 6zM26.5 6A.5.5 0 1 0 26.5 7 .5.5 0 1 0 26.5 6zM24.5 6A.5.5 0 1 0 24.5 7 .5.5 0 1 0 24.5 6zM22.5 6A.5.5 0 1 0 22.5 7 .5.5 0 1 0 22.5 6zM20.5 6A.5.5 0 1 0 20.5 7 .5.5 0 1 0 20.5 6zM18.5 6A.5.5 0 1 0 18.5 7 .5.5 0 1 0 18.5 6zM16.5 6A.5.5 0 1 0 16.5 7 .5.5 0 1 0 16.5 6zM14.5 6A.5.5 0 1 0 14.5 7 .5.5 0 1 0 14.5 6zM12.5 6A.5.5 0 1 0 12.5 7 .5.5 0 1 0 12.5 6zM10.5 6A.5.5 0 1 0 10.5 7 .5.5 0 1 0 10.5 6zM8.5 6A.5.5 0 1 0 8.5 7 .5.5 0 1 0 8.5 6zM6.5 6A.5.5 0 1 0 6.5 7 .5.5 0 1 0 6.5 6z"/><g><path fill="#4788c7" d="M12 21L20 21 20 18 12 18 12 13 5 19.5 12 26z"/></g><g><path fill="#4788c7" d="M27 18L19 18 19 21 27 21 27 26 34 19.5 27 13z"/></g></svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

@ -46,9 +46,8 @@ module.exports = configure(function (/* ctx */) {
'fontawesome-v6',
// 'eva-icons',
// 'themify',
'line-awesome',
'roboto-font-latin-ext' // this or either 'roboto-font', NEVER both!
'line-awesome'
// 'roboto-font-latin-ext' // this or either 'roboto-font', NEVER both!
// 'roboto-font', // optional, you are not bound to it
// 'material-icons' // optional, you are not bound to it
],

@ -3,15 +3,42 @@ router-view
</template>
<script setup>
import { nextTick, onMounted } from 'vue'
import { nextTick, onMounted, reactive } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { useSiteStore } from 'src/stores/site'
import { setCssVar, useQuasar } from 'quasar'
/* global siteConfig */
// QUASAR
const $q = useQuasar()
// STORES
const siteStore = useSiteStore()
// ROUTER
const router = useRouter()
// STATE
const state = reactive({
isInitialized: false
})
// THEME
function applyTheme () {
$q.dark.set(siteStore.theme.dark)
setCssVar('primary', siteStore.theme.colorPrimary)
setCssVar('secondary', siteStore.theme.colorSecondary)
setCssVar('accent', siteStore.theme.colorAccent)
setCssVar('header', siteStore.theme.colorHeader)
setCssVar('sidebar', siteStore.theme.colorSidebar)
}
// INIT SITE STORE
if (typeof siteConfig !== 'undefined') {
@ -19,15 +46,21 @@ if (typeof siteConfig !== 'undefined') {
id: siteConfig.id,
title: siteConfig.title
})
} else {
siteStore.loadSite(window.location.hostname)
applyTheme()
}
// MOUNTED
onMounted(async () => {
nextTick(() => {
router.beforeEach(async (to, from) => {
if (!siteStore.id) {
console.info('No pre-cached site config. Loading site info...')
await siteStore.loadSite(window.location.hostname)
console.info(`Using Site ID ${siteStore.id}`)
applyTheme()
}
})
router.afterEach(() => {
if (!state.isInitialized) {
state.isInitialized = true
document.querySelector('.init-loading').remove()
})
}
})
</script>

@ -6,7 +6,6 @@ export default boot(({ app }) => {
const i18n = createI18n({
legacy: false,
locale: 'en-US',
allowComposition: true,
messages
})

@ -17,14 +17,14 @@ q-header.bg-header.text-white.site-header(
square
)
img(src='/_assets/logo-wikijs.svg')
q-toolbar-title.text-h6.font-poppins {{siteTitle}}
q-toolbar-title.text-h6 {{siteStore.title}}
q-toolbar.gt-sm(
style='height: 64px;'
dark
)
q-input(
dark
v-model='search'
v-model='state.search'
standout='bg-white text-dark'
dense
rounded
@ -37,8 +37,8 @@ q-header.bg-header.text-white.site-header(
template(v-slot:append)
q-icon.cursor-pointer(
name='las la-times'
@click='search=``'
v-if='search.length > 0'
@click='state.search=``'
v-if='state.search.length > 0'
:color='$q.dark.isActive ? `blue` : `grey-4`'
)
q-btn.q-ml-md(
@ -47,7 +47,7 @@ q-header.bg-header.text-white.site-header(
dense
icon='las la-tags'
color='grey'
to='/t'
to='/_tags'
)
q-toolbar(
style='height: 64px;'
@ -56,7 +56,7 @@ q-header.bg-header.text-white.site-header(
q-space
transition(name='syncing')
q-spinner-rings.q-mr-sm(
v-show='isSyncing'
v-show='siteStore.routerLoading'
color='orange'
size='34px'
)
@ -83,26 +83,33 @@ q-header.bg-header.text-white.site-header(
account-menu
</template>
<script>
import { get } from 'vuex-pathify'
<script setup>
import AccountMenu from './AccountMenu.vue'
import NewMenu from './PageNewMenu.vue'
export default {
components: {
AccountMenu,
NewMenu
},
data () {
return {
search: ''
}
},
computed: {
isSyncing: get('isLoading', false),
siteTitle: get('site/title', false)
}
}
import { useI18n } from 'vue-i18n'
import { useQuasar } from 'quasar'
import { reactive } from 'vue'
import { useSiteStore } from 'src/stores/site'
// QUASAR
const $q = useQuasar()
// STORES
const siteStore = useSiteStore()
// I18N
const { t } = useI18n()
// DATA
const state = reactive({
search: ''
})
</script>
<style lang="scss">

@ -24,20 +24,32 @@ q-menu(
blueprint-icon(icon='advance')
q-item-section.q-pr-sm New Redirection
q-separator.q-my-sm(inset)
q-item(clickable, to='/f')
q-item(clickable, to='/_assets')
blueprint-icon(icon='add-image')
q-item-section.q-pr-sm Upload Media Asset
</template>
<script>
export default {
data () {
return { }
},
methods: {
create (editor) {
this.$store.dispatch('page/pageCreate', { editor })
}
}
<script setup>
import { useI18n } from 'vue-i18n'
import { useQuasar } from 'quasar'
import { usePageStore } from 'src/stores/page'
// QUASAR
const $q = useQuasar()
// STORES
const pageStore = usePageStore()
// I18N
const { t } = useI18n()
// METHODS
function create (editor) {
pageStore.pageCreate({ editor })
}
</script>

@ -27,18 +27,9 @@ body::-webkit-scrollbar-thumb {
// FONTS
// ------------------------------------------------------------------
@font-face {
font-family: 'Poppins';
src: url(./fonts/poppins-medium.woff2);
}
.font-poppins {
font-family: $font-poppins;
}
@font-face {
font-family: 'Roboto Mono';
src: url(./fonts/roboto-mono.woff2);
src: url(/_assets/fonts/roboto-mono/roboto-mono.woff2);
}
.font-robotomono {

@ -30,7 +30,3 @@ $dark-6: #070a0d;
$dark-5: #0d1117;
$dark-4: #161b22;
$dark-3: #1e232a;
// -- FONTS --
$font-poppins: 'Poppins', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;

@ -683,10 +683,10 @@
"admin.theme.headHtmlInjection": "Head HTML Injection",
"admin.theme.headHtmlInjectionHint": "HTML code to be injected just before the closing head tag. Usually for script tags.",
"admin.theme.headerColor": "Header Color",
"admin.theme.headerColorHint": "The background color for the site top header.",
"admin.theme.headerColorHint": "The background color for the site top header. Does not apply to the administration area.",
"admin.theme.iconset": "Icon Set",
"admin.theme.iconsetHint": "Set of icons to use for the sidebar navigation.",
"admin.theme.layout": "Theme Layout",
"admin.theme.layout": "Layout",
"admin.theme.options": "Theme Options",
"admin.theme.primaryColor": "Primary Color",
"admin.theme.primaryColorHint": "The main color for primary action buttons and most form elements.",
@ -701,7 +701,7 @@
"admin.theme.showSharingMenu": "Show Sharing Menu",
"admin.theme.showSharingMenuHint": "Should the sharing menu be displayed on all pages.",
"admin.theme.sidebarColor": "Sidebar Color",
"admin.theme.sidebarColorHint": "The background color for the side navigation menu on content pages.",
"admin.theme.sidebarColorHint": "The background color for the side navigation menu on content pages. Does not apply to the administration area.",
"admin.theme.sidebarPosition": "Sidebar Position",
"admin.theme.sidebarPositionHint": "On which side should the main site navigation sidebar be displayed.",
"admin.theme.siteTheme": "Site Theme",
@ -1429,5 +1429,13 @@
"admin.general.footerExtraHint": "Optionally add more content to the footer, such as additional copyright terms or mandatory regulatory info.",
"admin.general.urlHandling": "URL Handling",
"admin.general.pageExtensions": "Page Extensions",
"admin.general.pageExtensionsHint": "A comma-separated list of URL extensions that will be treated as pages. For example, adding md will treat /foobar.md the same as /foobar."
"admin.general.pageExtensionsHint": "A comma-separated list of URL extensions that will be treated as pages. For example, adding md will treat /foobar.md the same as /foobar.",
"admin.theme.appearance": "Appearance",
"admin.theme.fonts": "Fonts",
"admin.theme.baseFont": "Base Font",
"admin.theme.baseFontHint": "The font used across the site for the interface.",
"admin.theme.contentFont": "Content Font",
"admin.theme.contentFontHint": "The font used specifically for page content.",
"admin.theme.contentWidth": "Content Width",
"admin.theme.contentWidthHint": "Should the content use all available viewport space or stay centered."
}

@ -6,7 +6,7 @@ q-layout.admin(view='hHh Lpr lff')
q-btn(dense, flat, href='/')
q-avatar(size='34px', square)
img(src='/_assets/logo-wikijs.svg')
q-toolbar-title.text-h6.font-poppins Wiki.js
q-toolbar-title.text-h6 Wiki.js
q-toolbar.gt-sm.justify-center(style='height: 64px;', dark)
.text-overline.text-uppercase.text-grey {{t('admin.adminArea')}}
q-badge.q-ml-sm(
@ -267,10 +267,6 @@ watch(() => adminStore.currentSiteId, (newValue) => {
}
})
setCssVar('primary', '#1976D2')
setCssVar('secondary', '#02C39A')
setCssVar('accent', '#f03a47')
// MOUNTED
onMounted(async () => {

@ -12,7 +12,6 @@ q-layout(view='hHh lpr lff')
.auth {
background-color: #FFF;
display: flex;
font-family: 'Rubik', sans-serif;
@at-root .body--dark & {
background-color: $dark-6;

@ -9,7 +9,7 @@ q-layout(view='hHh Lpr lff')
v-for='navItem of sidenav'
:key='navItem.key'
clickable
:to='`/p/` + navItem.key'
:to='`/_profile/` + navItem.key'
active-class='is-active'
v-ripple
)
@ -21,7 +21,7 @@ q-layout(view='hHh Lpr lff')
q-item(
clickable
v-ripple
to='/p/me'
to='/_profile/me'
)
q-item-section(side)
q-icon(name='las la-id-card')
@ -42,67 +42,79 @@ q-layout(view='hHh Lpr lff')
span(style='font-size: 11px;') &copy; Cyberdyne Systems Corp. 2020 | Powered by #[strong Wiki.js]
</template>
<script>
<script setup>
import gql from 'graphql-tag'
import { useI18n } from 'vue-i18n'
import { useMeta, useQuasar } from 'quasar'
import { onMounted, reactive, watch } from 'vue'
import { useSiteStore } from 'src/stores/site'
import HeaderNav from '../components/HeaderNav.vue'
export default {
name: 'ProfileLayout',
components: {
HeaderNav
// QUASAR
const $q = useQuasar()
// STORES
const siteStore = useSiteStore()
// I18N
const { t } = useI18n()
// DATA
const sidenav = [
{
key: 'info',
label: 'Profile',
icon: 'las la-user-circle'
},
data () {
return {
sidenav: [
{
key: 'profile',
label: 'Profile',
icon: 'las la-user-circle'
},
{
key: 'avatar',
label: 'Avatar',
icon: 'las la-otter'
},
{
key: 'password',
label: 'Password',
icon: 'las la-key'
},
{
key: 'groups',
label: 'Groups',
icon: 'las la-users'
},
{
key: 'notifications',
label: 'Notifications',
icon: 'las la-bell'
},
{
key: 'pages',
label: 'My Pages',
icon: 'las la-file-alt'
},
{
key: 'activity',
label: 'Activity',
icon: 'las la-history'
}
],
thumbStyle: {
right: '2px',
borderRadius: '5px',
backgroundColor: '#FFF',
width: '5px',
opacity: 0.5
},
barStyle: {
backgroundColor: '#000',
width: '9px',
opacity: 0.1
}
}
{
key: 'avatar',
label: 'Avatar',
icon: 'las la-otter'
},
{
key: 'password',
label: 'Password',
icon: 'las la-key'
},
{
key: 'groups',
label: 'Groups',
icon: 'las la-users'
},
{
key: 'notifications',
label: 'Notifications',
icon: 'las la-bell'
},
{
key: 'pages',
label: 'My Pages',
icon: 'las la-file-alt'
},
{
key: 'activity',
label: 'Activity',
icon: 'las la-history'
}
]
const thumbStyle = {
right: '2px',
borderRadius: '5px',
backgroundColor: '#FFF',
width: '5px',
opacity: 0.5
}
const barStyle = {
backgroundColor: '#000',
width: '9px',
opacity: 0.1
}
</script>

@ -267,7 +267,7 @@ q-page.admin-general
src='https://m.media-amazon.com/images/G/01/audibleweb/arya/navigation/audible_logo._V517446980_.svg'
style='height: 34px;'
)
q-toolbar-title.text-h6.font-poppins(v-if='state.config.logoText') {{state.config.title}}
q-toolbar-title.text-h6(v-if='state.config.logoText') {{state.config.title}}
q-separator.q-my-sm(inset)
q-item(tag='label')
blueprint-icon(icon='information')

@ -38,7 +38,7 @@ q-page.admin-theme
//- -----------------------
q-card.shadow-1.q-pb-sm
q-card-section.flex.items-center
.text-subtitle1 {{t('admin.theme.options')}}
.text-subtitle1 {{t('admin.theme.appearance')}}
q-space
q-btn.acrylic-btn(
icon='las la-redo-alt'
@ -93,6 +93,21 @@ q-page.admin-theme
q-card.shadow-1.q-pb-sm.q-mt-md
q-card-section
.text-subtitle1 {{t('admin.theme.layout')}}
q-item
blueprint-icon(icon='width')
q-item-section
q-item-label {{t(`admin.theme.contentWidth`)}}
q-item-label(caption) {{t(`admin.theme.contentWidthHint`)}}
q-item-section.col-auto
q-btn-toggle(
v-model='state.config.contentWidth'
push
glossy
no-caps
toggle-color='primary'
:options='widthOptions'
)
q-separator.q-my-sm(inset)
q-item
blueprint-icon(icon='right-navigation-toolbar')
q-item-section
@ -153,9 +168,55 @@ q-page.admin-theme
.col-6
//- -----------------------
//- Code Injection
//- Fonts
//- -----------------------
q-card.shadow-1.q-pb-sm
q-card-section.flex.items-center
.text-subtitle1 {{t('admin.theme.fonts')}}
q-space
q-btn.acrylic-btn(
icon='las la-redo-alt'
:label='t(`admin.theme.resetDefaults`)'
flat
size='sm'
color='pink'
@click='resetFonts'
)
q-item
blueprint-icon(icon='fonts-app')
q-item-section
q-item-label {{t(`admin.theme.baseFont`)}}
q-item-label(caption) {{t(`admin.theme.baseFontHint`)}}
q-item-section
q-select(
outlined
v-model='state.config.baseFont'
:options='fonts'
emit-value
map-options
dense
:aria-label='t(`admin.theme.baseFont`)'
)
q-item
blueprint-icon(icon='fonts-app')
q-item-section
q-item-label {{t(`admin.theme.contentFont`)}}
q-item-label(caption) {{t(`admin.theme.contentFontHint`)}}
q-item-section
q-select(
outlined
v-model='state.config.contentFont'
:options='fonts'
emit-value
map-options
dense
:aria-label='t(`admin.theme.contentFont`)'
)
//- -----------------------
//- Code Injection
//- -----------------------
q-card.shadow-1.q-pb-sm.q-mt-md
q-card-section
.text-subtitle1 {{t('admin.theme.codeInjection')}}
q-item
@ -205,7 +266,7 @@ q-page.admin-theme
import gql from 'graphql-tag'
import { cloneDeep, startCase } from 'lodash-es'
import { useI18n } from 'vue-i18n'
import { useMeta, useQuasar } from 'quasar'
import { setCssVar, useMeta, useQuasar } from 'quasar'
import { onMounted, reactive, watch } from 'vue'
import { useAdminStore } from 'src/stores/admin'
@ -246,10 +307,13 @@ const state = reactive({
colorAccent: '#f03a47',
colorHeader: '#000',
colorSidebar: '#1976D2',
contentWidth: 'full',
sidebarPosition: 'left',
tocPosition: 'right',
showSharingMenu: true,
showPrintBtn: true
showPrintBtn: true,
baseFont: '',
contentFont: ''
}
})
@ -261,11 +325,27 @@ const colorKeys = [
'sidebar'
]
const widthOptions = [
{ label: 'Full Width', value: 'full' },
{ label: 'Centered', value: 'centered' }
]
const rightLeftOptions = [
{ label: 'Hide', value: 'off' },
{ label: 'Left', value: 'left' },
{ label: 'Right', value: 'right' }
]
const fonts = [
{ label: 'Inter', value: 'inter' },
{ label: 'Open Sans', value: 'opensans' },
{ label: 'Montserrat', value: 'montserrat' },
{ label: 'Roboto', value: 'roboto' },
{ label: 'Rubik', value: 'rubik' },
{ label: 'Tajawal', value: 'tajawal' },
{ label: 'User System Defaults', value: 'user' }
]
// WATCHERS
watch(() => adminStore.currentSiteId, (newValue) => {
@ -283,6 +363,11 @@ function resetColors () {
state.config.colorSidebar = '#1976D2'
}
function resetFonts () {
state.config.baseFont = 'roboto'
state.config.contentFont = 'roboto'
}
async function load () {
state.loading++
$q.loading.show()
@ -296,15 +381,18 @@ async function load () {
id: $id
) {
theme {
dark
baseFont
contentFont
colorPrimary
colorSecondary
colorAccent
colorHeader
colorSidebar
dark
injectCSS
injectHead
injectBody
contentWidth
sidebarPosition
tocPosition
showSharingMenu
@ -345,10 +433,13 @@ async function save () {
injectCSS: state.config.injectCSS,
injectHead: state.config.injectHead,
injectBody: state.config.injectBody,
contentWidth: state.config.contentWidth,
sidebarPosition: state.config.sidebarPosition,
tocPosition: state.config.tocPosition,
showSharingMenu: state.config.showSharingMenu,
showPrintBtn: state.config.showPrintBtn
showPrintBtn: state.config.showPrintBtn,
baseFont: state.config.baseFont,
contentFont: state.config.contentFont
}
const respRaw = await APOLLO_CLIENT.mutate({
mutation: gql`
@ -381,6 +472,11 @@ async function save () {
theme: patchTheme
})
$q.dark.set(state.config.dark)
setCssVar('primary', state.config.colorPrimary)
setCssVar('secondary', state.config.colorSecondary)
setCssVar('accent', state.config.colorAccent)
setCssVar('header', state.config.colorHeader)
setCssVar('sidebar', state.config.colorSidebar)
}
$q.notify({
type: 'positive',

@ -55,7 +55,7 @@ q-page.column
q-input.no-height(
borderless
v-model='title'
input-class='font-poppins text-h4 text-grey-9'
input-class='text-h4 text-grey-9'
input-style='padding: 0;'
placeholder='Untitled Page'
hide-hint
@ -63,12 +63,12 @@ q-page.column
q-input.no-height(
borderless
v-model='description'
input-class='font-poppins text-subtitle2 text-grey-7'
input-class='text-subtitle2 text-grey-7'
input-style='padding: 0;'
placeholder='Enter a short description'
hide-hint
)
.col.q-pa-md.font-poppins(v-else)
.col.q-pa-md(v-else)
.text-h4.page-header-title {{title}}
.text-subtitle2.page-header-subtitle {{description}}

@ -5,40 +5,40 @@
img(src='/_assets/logo-wikijs.svg' :alt='siteStore.title')
h2.auth-site-title {{ siteStore.title }}
p.text-grey-7 Login to continue
template(v-if='state.strategies?.length > 1')
p Sign in with
.auth-strategies
q-btn(
label='GitHub'
icon='lab la-github'
push
no-caps
:color='$q.dark.isActive ? `blue-grey-9` : `grey-1`'
:text-color='$q.dark.isActive ? `white` : `blue-grey-9`'
)
q-btn(
label='Google'
icon='lab la-google-plus'
push
no-caps
:color='$q.dark.isActive ? `blue-grey-9` : `grey-1`'
:text-color='$q.dark.isActive ? `white` : `blue-grey-9`'
)
q-btn(
label='Twitter'
icon='lab la-twitter'
push
no-caps
:color='$q.dark.isActive ? `blue-grey-9` : `grey-1`'
:text-color='$q.dark.isActive ? `white` : `blue-grey-9`'
)
q-btn(
label='Local'
icon='las la-seedling'
push
color='primary'
no-caps
)
template(v-if='state.strategies?.length > 1 || true')
p Sign in with
.auth-strategies
q-btn(
label='GitHub'
icon='lab la-github'
push
no-caps
:color='$q.dark.isActive ? `blue-grey-9` : `grey-1`'
:text-color='$q.dark.isActive ? `white` : `blue-grey-9`'
)
q-btn(
label='Google'
icon='lab la-google-plus'
push
no-caps
:color='$q.dark.isActive ? `blue-grey-9` : `grey-1`'
:text-color='$q.dark.isActive ? `white` : `blue-grey-9`'
)
q-btn(
label='Twitter'
icon='lab la-twitter'
push
no-caps
:color='$q.dark.isActive ? `blue-grey-9` : `grey-1`'
:text-color='$q.dark.isActive ? `white` : `blue-grey-9`'
)
q-btn(
label='Local'
icon='las la-seedling'
push
color='primary'
no-caps
)
q-form.q-mt-md
q-input(
outlined
@ -62,21 +62,22 @@
no-caps
icon='las la-sign-in-alt'
)
q-separator.q-my-md
q-btn.acrylic-btn.full-width(
flat
color='primary'
label='Create an Account'
no-caps
icon='las la-user-plus'
)
q-btn.acrylic-btn.full-width.q-mt-sm(
flat
color='primary'
label='Forgot Password'
no-caps
icon='las la-life-ring'
)
template(v-if='true')
q-separator.q-my-md
q-btn.acrylic-btn.full-width(
flat
color='primary'
label='Create an Account'
no-caps
icon='las la-user-plus'
)
q-btn.acrylic-btn.full-width.q-mt-sm(
flat
color='primary'
label='Forgot Password'
no-caps
icon='las la-life-ring'
)
.auth-bg(aria-hidden="true")
img(src='https://docs.requarks.io/_assets/img/splash/1.jpg' alt='')
</template>
@ -189,9 +190,9 @@ async function fetchStrategies () {
const resp = await APOLLO_CLIENT.query({
query: gql`
query loginFetchSiteStrategies(
$siteId: UUID
$siteId: UUID!
) {
authStrategies(
authSiteStrategies(
siteId: $siteId
enabledOnly: true
) {

@ -1,190 +1,210 @@
<template lang="pug">
q-page.q-py-md(:style-fn='pageStyle')
.text-header {{$t('profile.myInfo')}}
.text-header {{t('profile.myInfo')}}
q-item
blueprint-icon(icon='contact')
q-item-section
q-item-label {{$t(`profile.displayName`)}}
q-item-label(caption) {{$t(`profile.displayNameHint`)}}
q-item-label {{t(`profile.displayName`)}}
q-item-label(caption) {{t(`profile.displayNameHint`)}}
q-item-section
q-input(
outlined
v-model='config.name'
v-model='state.config.name'
dense
hide-bottom-space
:aria-label='$t(`profile.displayName`)'
:aria-label='t(`profile.displayName`)'
)
q-separator.q-my-sm(inset)
q-item
blueprint-icon(icon='envelope')
q-item-section
q-item-label {{$t(`profile.email`)}}
q-item-label(caption) {{$t(`profile.emailHint`)}}
q-item-label {{t(`profile.email`)}}
q-item-label(caption) {{t(`profile.emailHint`)}}
q-item-section
q-input(
outlined
v-model='config.email'
v-model='state.config.email'
dense
:aria-label='$t(`profile.email`)'
:aria-label='t(`profile.email`)'
readonly
)
q-separator.q-my-sm(inset)
q-item
blueprint-icon(icon='address')
q-item-section
q-item-label {{$t(`profile.location`)}}
q-item-label(caption) {{$t(`profile.locationHint`)}}
q-item-label {{t(`profile.location`)}}
q-item-label(caption) {{t(`profile.locationHint`)}}
q-item-section
q-input(
outlined
v-model='config.location'
v-model='state.config.location'
dense
hide-bottom-space
:aria-label='$t(`profile.location`)'
:aria-label='t(`profile.location`)'
)
q-separator.q-my-sm(inset)
q-item
blueprint-icon(icon='new-job')
q-item-section
q-item-label {{$t(`profile.jobTitle`)}}
q-item-label(caption) {{$t(`profile.jobTitleHint`)}}
q-item-label {{t(`profile.jobTitle`)}}
q-item-label(caption) {{t(`profile.jobTitleHint`)}}
q-item-section
q-input(
outlined
v-model='config.jobTitle'
v-model='state.config.jobTitle'
dense
hide-bottom-space
:aria-label='$t(`profile.jobTitle`)'
:aria-label='t(`profile.jobTitle`)'
)
q-separator.q-my-sm(inset)
q-item
blueprint-icon(icon='gender')
q-item-section
q-item-label {{$t(`profile.pronouns`)}}
q-item-label(caption) {{$t(`profile.pronounsHint`)}}
q-item-label {{t(`profile.pronouns`)}}
q-item-label(caption) {{t(`profile.pronounsHint`)}}
q-item-section
q-input(
outlined
v-model='config.pronouns'
v-model='state.config.pronouns'
dense
hide-bottom-space
:aria-label='$t(`profile.pronouns`)'
:aria-label='t(`profile.pronouns`)'
)
.text-header.q-mt-lg {{$t('profile.preferences')}}
.text-header.q-mt-lg {{t('profile.preferences')}}
q-item
blueprint-icon(icon='timezone')
q-item-section
q-item-label {{$t(`profile.timezone`)}}
q-item-label(caption) {{$t(`profile.timezoneHint`)}}
q-item-label {{t(`profile.timezone`)}}
q-item-label(caption) {{t(`profile.timezoneHint`)}}
q-item-section
q-select(
outlined
v-model='config.timezone'
:options='timezones'
v-model='state.config.timezone'
:options='dataStore.timezones'
option-value='value'
option-label='text'
emit-value
map-options
dense
options-dense
:aria-label='$t(`admin.general.defaultTimezone`)'
:aria-label='t(`admin.general.defaultTimezone`)'
)
q-separator.q-my-sm(inset)
q-item
blueprint-icon(icon='calendar')
q-item-section
q-item-label {{$t(`profile.dateFormat`)}}
q-item-label(caption) {{$t(`profile.dateFormatHint`)}}
q-item-label {{t(`profile.dateFormat`)}}
q-item-label(caption) {{t(`profile.dateFormatHint`)}}
q-item-section
q-select(
outlined
v-model='config.dateFormat'
v-model='state.config.dateFormat'
emit-value
map-options
dense
:aria-label='$t(`admin.general.defaultDateFormat`)'
:options=`[
{ label: $t('profile.localeDefault'), value: '' },
{ label: 'DD/MM/YYYY', value: 'DD/MM/YYYY' },
{ label: 'DD.MM.YYYY', value: 'DD.MM.YYYY' },
{ label: 'MM/DD/YYYY', value: 'MM/DD/YYYY' },
{ label: 'YYYY-MM-DD', value: 'YYYY-MM-DD' },
{ label: 'YYYY/MM/DD', value: 'YYYY/MM/DD' }
]`
:aria-label='t(`admin.general.defaultDateFormat`)'
:options='dateFormats'
)
q-separator.q-my-sm(inset)
q-item
blueprint-icon(icon='clock')
q-item-section
q-item-label {{$t(`profile.timeFormat`)}}
q-item-label(caption) {{$t(`profile.timeFormatHint`)}}
q-item-label {{t(`profile.timeFormat`)}}
q-item-label(caption) {{t(`profile.timeFormatHint`)}}
q-item-section.col-auto
q-btn-toggle(
v-model='config.timeFormat'
v-model='state.config.timeFormat'
push
glossy
no-caps
toggle-color='primary'
:options=`[
{ label: $t('admin.general.defaultTimeFormat12h'), value: '12h' },
{ label: $t('admin.general.defaultTimeFormat24h'), value: '24h' }
]`
:options='timeFormats'
)
q-separator.q-my-sm(inset)
q-item(tag='label', v-ripple)
blueprint-icon(icon='light-on')
q-item-section
q-item-label {{$t(`profile.darkMode`)}}
q-item-label(caption) {{$t(`profile.darkModeHint`)}}
q-item-label {{t(`profile.darkMode`)}}
q-item-label(caption) {{t(`profile.darkModeHint`)}}
q-item-section(avatar)
q-toggle(
v-model='config.darkMode'
v-model='state.config.darkMode'
color='primary'
checked-icon='las la-check'
unchecked-icon='las la-times'
:aria-label='$t(`profile.darkMode`)'
:aria-label='t(`profile.darkMode`)'
)
.actions-bar.q-mt-lg
q-btn(
icon='mdi-check'
icon='las la-check'
unelevated
label='Save Changes'
color='secondary'
)
</template>
<script>
import { get } from 'vuex-pathify'
export default {
data () {
return {
config: {
name: 'John Doe',
email: 'john.doe@company.com',
location: '',
jobTitle: '',
pronouns: '',
dateFormat: '',
timeFormat: '12h',
darkMode: false
}
}
},
computed: {
timezones: get('data/timezones', false)
},
watch: {
'config.darkMode' (newValue) {
this.$q.dark.set(newValue)
}
},
methods: {
pageStyle (offset, height) {
return {
'min-height': `${height - 100 - offset}px`
}
}
<script setup>
import gql from 'graphql-tag'
import { useI18n } from 'vue-i18n'
import { useMeta, useQuasar } from 'quasar'
import { onMounted, reactive, watch } from 'vue'
import { useSiteStore } from 'src/stores/site'
import { useDataStore } from 'src/stores/data'
// QUASAR
const $q = useQuasar()
// STORES
const siteStore = useSiteStore()
const dataStore = useDataStore()
// I18N
const { t } = useI18n()
// META
useMeta({
title: t('profile.title')
})
// DATA
const state = reactive({
config: {
name: 'John Doe',
email: 'john.doe@company.com',
location: '',
jobTitle: '',
pronouns: '',
dateFormat: '',
timeFormat: '12h',
darkMode: false
}
})
const dateFormats = [
{ value: '', label: t('profile.localeDefault') },
{ value: 'DD/MM/YYYY', label: 'DD/MM/YYYY' },
{ value: 'DD.MM.YYYY', label: 'DD.MM.YYYY' },
{ value: 'MM/DD/YYYY', label: 'MM/DD/YYYY' },
{ value: 'YYYY-MM-DD', label: 'YYYY-MM-DD' },
{ value: 'YYYY/MM/DD', label: 'YYYY/MM/DD' }
]
const timeFormats = [
{ value: '12h', label: t('admin.general.defaultTimeFormat12h') },
{ value: '24h', label: t('admin.general.defaultTimeFormat24h') }
]
// METHODS
function pageStyle (offset, height) {
return {
'min-height': `${height - 100 - offset}px`
}
}
</script>

@ -15,14 +15,14 @@ const routes = [
{ path: '', component: () => import('../pages/Login.vue') }
]
},
// {
// path: '/p',
// component: () => import('../layouts/ProfileLayout.vue'),
// children: [
// { path: '', redirect: '/p/profile' },
// { path: 'profile', component: () => import('../pages/Profile.vue') }
// ]
// },
{
path: '/_profile',
component: () => import('../layouts/ProfileLayout.vue'),
children: [
{ path: '', redirect: '/_profile/info' },
{ path: 'info', component: () => import('../pages/Profile.vue') }
]
},
{
path: '/_admin',
component: () => import('../layouts/AdminLayout.vue'),

@ -70,6 +70,20 @@ export const useSiteStore = defineStore('site', {
logoText
company
contentLicense
theme {
dark
colorPrimary
colorSecondary
colorAccent
colorHeader
colorSidebar
sidebarPosition
tocPosition
showSharingMenu
showPrintBtn
baseFont
contentFont
}
}
}
`,
@ -86,6 +100,10 @@ export const useSiteStore = defineStore('site', {
this.logoUrl = clone(siteInfo.logoUrl)
this.company = clone(siteInfo.company)
this.contentLicense = clone(siteInfo.contentLicense)
this.theme = {
...this.theme,
...clone(siteInfo.theme)
}
} else {
throw new Error('Invalid Site')
}

Loading…
Cancel
Save