feat: admin - groups single view

This commit is contained in:
NGPixel
2018-03-28 00:02:32 -04:00
parent 346493f845
commit e79dfc0822
25 changed files with 76 additions and 38 deletions

View File

@@ -0,0 +1,116 @@
<template lang='pug'>
v-card(flat)
v-card(flat, color='grey lighten-5').pa-3.pt-4
.headline.blue--text.text--darken-2 API
.subheading.grey--text Manage keys to access the API
v-card
v-card-title
v-btn(color='green', dark)
v-icon(left) power_settings_new
| Enable API
v-btn(color='primary', dark)
v-icon(left) add
| New API Key
v-btn(icon)
v-icon.grey--text refresh
v-spacer
v-text-field(append-icon='search', label='Search', single-line, hide-details, v-model='search')
v-data-table(
v-model='selected'
:items='items',
:headers='headers',
:search='search',
:pagination.sync='pagination',
:rows-per-page-items='[15]'
select-all,
hide-actions,
disable-initial-sort
)
template(slot='headers', slot-scope='props')
tr
th(width='50')
th.text-xs-right(
width='80'
:class='[`column sortable`, pagination.descending ? `desc` : `asc`, pagination.sortBy === `id` ? `active` : ``]'
@click='changeSort(`id`)'
)
v-icon(small) arrow_upward
| ID
th.text-xs-left(
v-for='header in props.headers'
:key='header.text'
:width='header.width'
:class='[`column sortable`, pagination.descending ? `desc` : `asc`, header.value === pagination.sortBy ? `active` : ``]'
@click='changeSort(header.value)'
)
| {{ header.text }}
v-icon(small) arrow_upward
template(slot='items', slot-scope='props')
tr(:active='props.selected')
td
v-checkbox(hide-details, :input-value='props.selected', color='blue darken-2', @click='props.selected = !props.selected')
td.text-xs-right {{ props.item.id }}
td {{ props.item.name }}
td {{ props.item.key }}
td {{ props.item.createdOn }}
td {{ props.item.updatedOn }}
td: v-btn(icon): v-icon.grey--text.text--darken-1 more_horiz
template(slot='no-data')
v-alert(icon='warning', :value='true') No users to display!
.text-xs-center.py-2
v-pagination(v-model='pagination.page', :length='pages')
</template>
<script>
export default {
data() {
return {
selected: [],
pagination: {},
items: [
{ id: 1, key: 'xxxxxxxxxxxxx' },
{ id: 2, key: 'xxxxxxxxxxxxy' },
{ id: 3, key: 'xxxxxxxxxxxxz' }
],
headers: [
{ text: 'Name', value: 'name' },
{ text: 'Key', value: 'key' },
{ text: 'Created On', value: 'createdOn' },
{ text: 'Updated On', value: 'updatedOn' },
{ text: '', value: 'actions', sortable: false, width: 50 }
],
search: ''
}
},
computed: {
pages () {
if (this.pagination.rowsPerPage == null || this.pagination.totalItems == null) {
return 0
}
return Math.ceil(this.pagination.totalItems / this.pagination.rowsPerPage)
}
},
methods: {
changeSort (column) {
if (this.pagination.sortBy === column) {
this.pagination.descending = !this.pagination.descending
} else {
this.pagination.sortBy = column
this.pagination.descending = false
}
},
toggleAll () {
if (this.selected.length) {
this.selected = []
} else {
this.selected = this.items.slice()
}
}
}
}
</script>
<style lang='scss'>
</style>

View File

@@ -0,0 +1,117 @@
<template lang='pug'>
v-card(flat)
v-card(color='grey lighten-5')
.pa-3.pt-4
.headline.primary--text Authentication
.subheading.grey--text Configure the authentication settings of your wiki
v-tabs(color='grey lighten-4', fixed-tabs, slider-color='primary', show-arrows)
v-tab(key='settings'): v-icon settings
v-tab(v-for='provider in activeProviders', :key='provider.key') {{ provider.title }}
v-tab-item(key='settings', :transition='false', :reverse-transition='false')
v-card.pa-3
.body-2.pb-2 Select which authentication providers to enable:
v-form
v-checkbox(
v-for='(provider, n) in providers',
v-model='auths',
:key='provider.key',
:label='provider.title',
:value='provider.key',
color='primary',
:disabled='provider.key === `local`'
hide-details
)
v-divider
v-btn(color='primary')
v-icon(left) chevron_right
| Set Providers
v-btn(color='black', dark)
v-icon(left) layers_clear
| Flush Sessions
v-btn(icon, @click='refresh')
v-icon.grey--text refresh
v-tab-item(v-for='(provider, n) in activeProviders', :key='provider.key', :transition='false', :reverse-transition='false')
v-card.pa-3
v-form
v-subheader Provider Configuration
.body-1(v-if='!provider.props || provider.props.length < 1') This provider has no configuration options you can modify.
v-text-field(v-else, v-for='prop in provider.props', :key='prop', :label='prop', prepend-icon='mode_edit')
v-divider
v-subheader Registration
v-switch.ml-3(
v-model='auths',
label='Allow self-registration',
:value='true',
color='primary',
hint='Allow any user successfully authorized by the provider to access the wiki.',
persistent-hint
)
v-text-field(label='Limit to specific email domains', prepend-icon='mail_outline')
v-text-field(label='Assign to group', prepend-icon='people')
v-divider
v-btn(color='primary')
v-icon(left) chevron_right
| Save Configuration
v-snackbar(
color='success'
top
v-model='refreshCompleted'
)
v-icon.mr-3(dark) cached
| List of providers has been refreshed.
</template>
<script>
import _ from 'lodash'
import gql from 'graphql-tag'
export default {
data() {
return {
providers: [],
auths: ['local'],
refreshCompleted: false
}
},
computed: {
activeProviders() {
return _.filter(this.providers, 'isEnabled')
}
},
apollo: {
providers: {
query: gql`
query {
authentication {
providers {
isEnabled
key
props
title
useForm
config {
key
value
}
}
}
}
`,
update: (data) => data.authentication.providers
}
},
methods: {
async refresh() {
await this.$apollo.queries.providers.refetch()
this.refreshCompleted = true
}
}
}
</script>
<style lang='scss'>
</style>

View File

@@ -0,0 +1,19 @@
<template lang='pug'>
v-container(fluid, fill-height)
v-layout(row wrap)
v-flex(xs12)
.headline.primary--text Dashboard
.subheading.grey--text Coming soon
</template>
<script>
export default {
data() {
return {}
}
}
</script>
<style lang='scss'>
</style>

View File

@@ -0,0 +1,138 @@
<template lang='pug'>
v-card(flat)
v-card(color='grey lighten-5')
.pa-3.pt-4
.headline.primary--text Developer Tools
.subheading.grey--text ¯\_()_/¯
v-tabs(v-model='selectedTab', color='grey lighten-4', fixed-tabs, slider-color='primary', show-arrows, @input='tabChanged')
v-tab(key='0') Graph API Playground
v-tab(key='1') Graph API Map
v-tabs-items(v-model='selectedTab')
v-tab-item(key='0', :transition='false', :reverse-transition='false')
#graphiql
v-tab-item(key='1', :transition='false', :reverse-transition='false')
#voyager
</template>
<script>
import _ from 'lodash'
import React from 'react'
import ReactDOM from 'react-dom'
import GraphiQL from 'graphiql'
import { Voyager } from 'graphql-voyager'
import 'graphiql/graphiql.css'
import 'graphql-voyager/dist/voyager.css'
const fetcher = (qry, respType) => {
return fetch('/graphql', {
method: 'post',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(qry),
credentials: 'include'
}).then(response => {
if (respType === 'json') {
return response.json()
} else {
return response.text()
}
}).then(responseBody => {
try {
return JSON.parse(responseBody)
} catch (error) {
return responseBody
}
})
}
let graphiQLInstance
export default {
data() {
return {
selectedTab: '0'
}
},
mounted() {
this.renderGraphiQL()
},
methods: {
tabChanged (tabId) {
switch (tabId) {
case '1':
this.renderVoyager()
break
}
},
renderGraphiQL() {
ReactDOM.render(
React.createElement(GraphiQL, {
ref(el) { graphiQLInstance = el },
async fetcher(qry) {
let resp = await fetcher(qry, 'text')
_.delay(() => {
graphiQLInstance.resultComponent.viewer.refresh()
}, 500)
return resp
},
query: '',
response: null,
variables: null,
operationName: null,
websocketConnectionParams: null
}),
document.getElementById('graphiql')
)
graphiQLInstance.queryEditorComponent.editor.refresh()
graphiQLInstance.variableEditorComponent.editor.refresh()
graphiQLInstance.state.variableEditorOpen = true
},
renderVoyager() {
ReactDOM.render(
React.createElement(Voyager, {
introspection: qry => fetcher({ query: qry }, 'json'),
workerURI: '/js/voyager.worker.js'
}),
document.getElementById('voyager')
)
}
}
}
</script>
<style lang='scss'>
#graphiql {
height: calc(100vh - 230px);
.topBar {
background-color: mc('grey', '200');
background-image: none;
padding: 1.5rem 0;
> .title {
display: none;
}
}
.toolbar {
background-color: initial;
box-shadow: initial;
}
}
#voyager {
height: calc(100vh - 250px);
.title-area {
display: none;
}
.type-doc {
margin-top: 5px;
}
}
</style>

View File

@@ -0,0 +1,45 @@
<template lang='pug'>
v-card(flat)
v-card(color='grey lighten-5')
.pa-3.pt-4
.headline.primary--text Editor
.subheading.grey--text Configure the content editor
v-tabs(color='grey lighten-4', fixed-tabs, slider-color='primary', show-arrows)
v-tab(key='settings'): v-icon settings
v-tab(key='code') Markdown
v-tab-item(key='settings', :transition='false', :reverse-transition='false')
v-card.pa-3
v-form
v-radio-group(v-model='selectedEditor')
v-radio(v-for='(editor, n) in editors', :key='n', :label='editor.text', :value='editor.value', color='primary')
v-divider
v-btn(color='primary')
v-icon(left) chevron_right
| Set Editor
v-btn(icon)
v-icon.grey--text refresh
v-tab-item(key='code', :transition='false', :reverse-transition='false')
v-card.pa-3
v-form
v-subheader Editor Configuration
.body-1 This editor has no configuration options you can modify.
</template>
<script>
export default {
data() {
return {
editors: [
{ text: 'Markdown (default)', value: 'code' }
],
selectedEditor: 'code'
}
}
}
</script>
<style lang='scss'>
</style>

View File

@@ -0,0 +1,64 @@
<template lang='pug'>
v-container(fluid, fill-height, grid-list-lg)
v-layout(row wrap)
v-flex(xs12)
.headline.primary--text General
.subheading.grey--text Main settings of your wiki
v-form.pt-3
v-layout(row wrap)
v-flex(lg6 xs12)
v-form
v-card
v-toolbar(color='primary', dark, dense, flat)
v-toolbar-title
.subheading Site Info
v-subheader General
.px-3
v-text-field(label='Site Title', required, :counter='50', v-model='siteTitle', prepend-icon='public')
v-divider
v-subheader SEO
.px-3
v-text-field(label='Site Description', :counter='255', prepend-icon='public')
v-text-field(label='Site Keywords', :counter='255', prepend-icon='public')
v-select(label='Meta Robots', chips, tags, :items='metaRobots', v-model='metaRobotsSelection', prepend-icon='public')
v-divider
.px-3.pb-3
v-btn(color='primary') Save
v-flex(lg6 xs12)
v-card
v-toolbar(color='primary', dark, dense, flat)
v-toolbar-title
.subheading Site Branding
v-card-text ---
v-card.mt-3
v-toolbar(color='primary', dark, dense, flat)
v-toolbar-title
.subheading Maintenance Mode
v-card-text
.body-1 Maintenance mode restrict access to the site to administrators only, regarless of current permissions.
v-btn.mt-3(color='orange darken-2', dark)
icon-home-alert.mr-2(fillColor='#FFFFFF')
| Turn On Maintenance Mode
</template>
<script>
import IconHomeAlert from 'mdi/home-alert'
export default {
components: {
IconHomeAlert
},
data() {
return {
siteTitle: 'Wiki.js',
metaRobotsSelection: ['Index', 'Follow'],
metaRobots: ['Index', 'Follow', 'No Index', 'No Follow']
}
}
}
</script>
<style lang='scss'>
</style>

View File

@@ -0,0 +1,187 @@
<template lang='pug'>
v-card
v-card(flat, color='grey lighten-5').pa-3.pt-4
.headline.blue--text.text--darken-2 Edit Group
.subheading.grey--text {{group.name}}
v-btn(color='primary', fab, absolute, bottom, right, small, to='/groups'): v-icon arrow_upward
v-tabs(v-model='tab', color='grey lighten-4', fixed-tabs, slider-color='primary', show-arrows)
v-tab(key='properties') Properties
v-tab(key='rights') Permissions
v-tab(key='users') Users
v-tab-item(key='properties', :transition='false', :reverse-transition='false')
v-card
v-card-text
v-text-field(v-model='group.name', label='Group Name', counter='255', prepend-icon='people')
v-card-actions.pa-3
v-btn(color='primary', @click='')
v-icon(left) check
| Save Changes
.caption.ml-4.grey--text ID: {{group.id}}
v-spacer
v-dialog(v-model='deleteGroupDialog', max-width='500')
v-btn(color='red', flat, @click='', slot='activator')
v-icon(left) delete
| Delete Group
v-card
.dialog-header.is-red Delete Group?
v-card-text Are you sure you want to delete group #[strong {{ group.name }}]? All users will be unassigned from this group.
v-card-actions
v-spacer
v-btn(flat, @click='deleteGroupDialog = false') Cancel
v-btn(color='red', dark, @click='deleteGroup') Delete
v-tab-item(key='rights', :transition='false', :reverse-transition='false')
v-card
v-card-title.pb-0
v-btn(color='primary')
v-icon(left) add
| Add Rule
v-spacer
v-btn(flat)
v-icon(left) vertical_align_bottom
| Import Rules
v-list(dense, two-line)
v-list-tile.grey.lighten-5.px-2
v-list-tile-avatar(color='red'): v-icon(color='white') remove_circle
v-list-tile-content
v-list-tile-title /javascript/*
v-list-tile-sub-title.caption #[strong WRITE]
v-list-tile-action
v-btn(icon): v-icon(color='grey') delete
v-divider(inset).my-0
v-list-tile.grey.lighten-5.px-2
v-list-tile-avatar(color='green'): v-icon(color='white') check
v-list-tile-content
v-list-tile-title /javascript/*
v-list-tile-sub-title.caption #[strong WRITE]
v-list-tile-action
v-btn(icon): v-icon(color='grey') delete
v-divider(inset).my-0
v-list-tile.grey.lighten-5.px-2
v-list-tile-avatar(color='green'): v-icon(color='white') check
v-list-tile-content
v-list-tile-title /javascript/*
v-list-tile-sub-title.caption #[strong READ]
v-list-tile-action
v-btn(icon): v-icon(color='grey') delete
v-tab-item(key='users', :transition='false', :reverse-transition='false')
v-card
v-card-title.pb-0
v-btn(color='primary')
v-icon(left) assignment_ind
| Assign User
v-data-table(
:items='group.users',
:headers='headers',
:search='search',
:pagination.sync='pagination',
:rows-per-page-items='[15]'
hide-actions
)
template(slot='items', slot-scope='props')
tr(:active='props.selected')
td.text-xs-right {{ props.item.id }}
td {{ props.item.name }}
td {{ props.item.email }}
td
v-menu(bottom, right, min-width='200')
v-btn(icon, slot='activator'): v-icon.grey--text.text--darken-1 more_horiz
v-list
v-list-tile(@click='deleteGroupConfirm(props.item)')
v-list-tile-action: v-icon(color='orange') highlight_off
v-list-tile-content
v-list-tile-title Unassign
template(slot='no-data')
v-alert.ma-3(icon='warning', :value='true', outline) No users to display.
.text-xs-center.py-2(v-if='users.length > 15')
v-pagination(v-model='pagination.page', :length='pages')
</template>
<script>
import groupQuery from 'gql/admin-groups-query-single.gql'
import deleteGroupMutation from 'gql/admin-groups-mutation-delete.gql'
export default {
data() {
return {
group: {
id: 7,
name: 'Editors',
users: []
},
deleteGroupDialog: false,
pagination: {},
users: [],
headers: [
{ text: 'ID', value: 'id', width: 50, align: 'right' },
{ text: 'Name', value: 'name' },
{ text: 'Email', value: 'email' },
{ text: '', value: 'actions', sortable: false, width: 50 }
],
search: '',
tab: '1'
}
},
computed: {
pages () {
if (this.pagination.rowsPerPage == null || this.pagination.totalItems == null) {
return 0
}
return Math.ceil(this.pagination.totalItems / this.pagination.rowsPerPage)
}
},
methods: {
async deleteGroupConfirm(group) {
this.deleteGroupDialog = true
this.selectedGroup = group
},
async deleteGroup() {
this.deleteGroupDialog = false
try {
await this.$apollo.mutate({
mutation: deleteGroupMutation,
variables: {
id: this.group.id
},
watchLoading (isLoading) {
this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-groups-delete')
}
})
this.$store.commit('showNotification', {
style: 'success',
message: `Group ${this.group.name} has been deleted.`,
icon: 'delete'
})
this.$router.replace('/groups')
} catch (err) {
this.$store.commit('showNotification', {
style: 'red',
message: err.message,
icon: 'warning'
})
}
}
},
apollo: {
group: {
query: groupQuery,
variables() {
return {
id: this.$route.params.id
}
},
update: (data) => data.groups.single,
watchLoading (isLoading) {
this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-groups-refresh')
}
}
}
}
</script>
<style lang='scss'>
</style>

View File

@@ -0,0 +1,170 @@
<template lang='pug'>
v-card(flat)
v-card(flat, color='grey lighten-5').pa-3.pt-4
.headline.blue--text.text--darken-2 Groups
.subheading.grey--text Manage groups and their permissions
v-card
v-card-title
v-dialog(v-model='newGroupDialog', max-width='500')
v-btn(color='primary', dark, slot='activator')
v-icon(left) add
| New Group
v-card
.dialog-header.is-short New Group
v-card-text
v-text-field(v-model='newGroupName', label='Group Name', autofocus, counter='255', @keyup.enter='createGroup')
v-card-actions
v-spacer
v-btn(flat, @click='newGroupDialog = false') Cancel
v-btn(color='primary', @click='createGroup') Create
v-btn(icon, @click='refresh')
v-icon.grey--text refresh
v-spacer
v-text-field(solo, append-icon='search', label='Search', single-line, hide-details, v-model='search')
v-data-table(
:items='groups',
:headers='headers',
:search='search',
:pagination.sync='pagination',
:rows-per-page-items='[15]'
hide-actions
)
template(slot='items', slot-scope='props')
tr.is-clickable(:active='props.selected', @click='$router.push("/groups/" + props.item.id)')
td.text-xs-right {{ props.item.id }}
td {{ props.item.name }}
td {{ props.item.userCount }}
td {{ props.item.createdAt | moment('calendar') }}
td {{ props.item.updatedAt | moment('calendar') }}
template(slot='no-data')
v-alert.ma-3(icon='warning', :value='true', outline) No groups to display.
.text-xs-center.py-2(v-if='groups.length > 15')
v-pagination(v-model='pagination.page', :length='pages')
</template>
<script>
import _ from 'lodash'
import groupsQuery from 'gql/admin-groups-query-list.gql'
import createGroupMutation from 'gql/admin-groups-mutation-create.gql'
import deleteGroupMutation from 'gql/admin-groups-mutation-delete.gql'
export default {
data() {
return {
newGroupDialog: false,
newGroupName: '',
selectedGroup: {},
pagination: {},
groups: [],
headers: [
{ text: 'ID', value: 'id', width: 50, align: 'right' },
{ text: 'Name', value: 'name' },
{ text: 'Users', value: 'userCount', width: 200 },
{ text: 'Created', value: 'createdAt', width: 250 },
{ text: 'Last Updated', value: 'updatedAt', width: 250 }
],
search: ''
}
},
computed: {
pages () {
if (this.pagination.rowsPerPage == null || this.pagination.totalItems == null) {
return 0
}
return Math.ceil(this.pagination.totalItems / this.pagination.rowsPerPage)
}
},
methods: {
async refresh() {
await this.$apollo.queries.groups.refetch()
this.$store.commit('showNotification', {
message: 'Groups have been refreshed.',
style: 'success',
icon: 'cached'
})
},
async createGroup() {
this.newGroupDialog = false
try {
await this.$apollo.mutate({
mutation: createGroupMutation,
variables: {
name: this.newGroupName
},
update (store, resp) {
const data = _.get(resp, 'data.groups.create', { responseResult: {} })
if (data.responseResult.succeeded === true) {
const apolloData = store.readQuery({ query: groupsQuery })
data.group.userCount = 0
apolloData.groups.list.push(data.group)
store.writeQuery({ query: groupsQuery, data: apolloData })
} else {
throw new Error(data.responseResult.message)
}
},
watchLoading (isLoading) {
this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-groups-create')
}
})
this.newGroupName = ''
this.$store.commit('showNotification', {
style: 'success',
message: `Group has been created successfully.`,
icon: 'check'
})
} catch (err) {
this.$store.commit('showNotification', {
style: 'red',
message: err.message,
icon: 'warning'
})
}
},
async deleteGroupConfirm(group) {
this.deleteGroupDialog = true
this.selectedGroup = group
},
async deleteGroup() {
this.deleteGroupDialog = false
try {
await this.$apollo.mutate({
mutation: deleteGroupMutation,
variables: {
id: this.selectedGroup.id
},
watchLoading (isLoading) {
this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-groups-delete')
}
})
await this.$apollo.queries.groups.refetch()
this.$store.commit('showNotification', {
style: 'success',
message: `Group ${this.selectedGroup.name} has been deleted.`,
icon: 'delete'
})
} catch (err) {
this.$store.commit('showNotification', {
style: 'red',
message: err.message,
icon: 'warning'
})
}
}
},
apollo: {
groups: {
query: groupsQuery,
update: (data) => data.groups.list,
watchLoading (isLoading) {
this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-groups-refresh')
}
}
}
}
</script>
<style lang='scss'>
</style>

View File

@@ -0,0 +1,86 @@
<template lang='pug'>
v-container(fluid, fill-height, grid-list-lg)
v-layout(row wrap)
v-flex(xs12)
.headline.primary--text Locale
.subheading.grey--text Set localization options for your wiki
v-form.pt-3
v-layout(row wrap)
v-flex(lg6 xs12)
v-card
v-toolbar(color='primary', dark, dense, flat)
v-toolbar-title
.subheading Locale Settings
v-card-text
v-select(:items='locales', prepend-icon='public', v-model='selectedLocale', label='Site Locale', persistent-hint, hint='All UI text elements will be displayed in selected language.')
template(slot='item', slot-scope='data')
v-list-tile-avatar
v-avatar.blue.white--text(tile, size='40', v-html='data.item.value.toUpperCase()')
v-list-tile-content
v-list-tile-title(v-html='data.item.text')
v-list-tile-sub-title(v-html='data.item.original')
v-divider
v-switch(v-model='rtlEnabled', label='RTL Display Mode', color='primary', persistent-hint, hint='For Right-to-Left languages, e.g. Arabic')
v-divider
.px-3.pb-3
v-btn(color='primary') Save
v-flex(lg6 xs12)
v-card
v-toolbar(color='teal', dark, dense, flat)
v-toolbar-title
.subheading Download Locale
v-list
v-list-tile(@click='')
v-list-tile-avatar
v-avatar.teal.white--text(tile, size='40') ZH
v-list-tile-content
v-list-tile-title Chinese
v-list-tile-sub-title 中文
v-list-tile-action
v-btn(icon)
v-icon.grey--text cloud_download
v-list-tile(@click='')
v-list-tile-avatar
v-avatar.teal.white--text(tile, size='40') EN
v-list-tile-content
v-list-tile-title English
v-list-tile-sub-title English
v-list-tile-action
v-icon.green--text check
v-list-tile(@click='')
v-list-tile-avatar
v-avatar.teal.white--text(tile, size='40') FR
v-list-tile-content
v-list-tile-title French
v-list-tile-sub-title Français
v-list-tile-action
v-icon.green--text check
v-list-tile(@click='')
v-list-tile-avatar
v-avatar.teal.white--text(tile, size='40') RU
v-list-tile-content
v-list-tile-title Russian
v-list-tile-sub-title Русский
v-list-tile-action
v-btn(icon)
v-icon.blue--text update
</template>
<script>
export default {
data() {
return {
locales: [
{ text: 'English', original: 'English', value: 'en' },
{ text: 'French', original: 'Français', value: 'fr' }
],
selectedLocale: 'en',
rtlEnabled: false
}
}
}
</script>
<style lang='scss'>
</style>

View File

@@ -0,0 +1,91 @@
<template lang='pug'>
v-card(flat)
v-card(color='grey lighten-5')
.pa-3.pt-4
.headline.primary--text Logging
.subheading.grey--text Configure the system logger(s)
v-tabs(color='grey lighten-4', fixed-tabs, slider-color='primary', show-arrows)
v-tab(key='settings'): v-icon settings
v-tab(v-for='svc in activeServices', :key='svc.key') {{ svc.title }}
v-tab-item(key='settings', :transition='false', :reverse-transition='false')
v-card.pa-3
.body-2.pb-2 Select which logging service to enable:
v-form
v-checkbox(
v-for='(svc, n) in services',
v-model='selectedServices',
:key='svc.key',
:label='svc.title',
:value='svc.key',
color='primary',
:disabled='svc.key === `console`'
hide-details
)
v-divider
v-btn(color='primary')
v-icon(left) chevron_right
| Set Services
v-btn(color='black', dark)
v-icon(left) keyboard
| View Console
v-btn(color='black', dark)
v-icon(left) layers_clear
| Purge Logs
v-btn(icon, @click='refresh')
v-icon.grey--text refresh
v-tab-item(v-for='(svc, n) in activeServices', :key='svc.key', :transition='false', :reverse-transition='false')
v-card.pa-3
v-form
v-subheader Service Configuration
.body-1(v-if='!svc.props || svc.props.length < 1') This logging service has no configuration options you can modify.
v-text-field(v-else, v-for='prop in svc.props', :key='prop', :label='prop', prepend-icon='mode_edit')
v-divider
v-btn(color='primary')
v-icon(left) chevron_right
| Save Configuration
v-snackbar(
color='success'
top
v-model='refreshCompleted'
)
v-icon.mr-3(dark) cached
| List of logging services has been refreshed.
</template>
<script>
import _ from 'lodash'
export default {
data() {
return {
services: [],
selectedServices: ['console'],
refreshCompleted: false
}
},
computed: {
activeServices() {
return _.filter(this.services, 'isEnabled')
}
},
// apollo: {
// services: {
// query: CONSTANTS.GRAPH.AUTHENTICATION.QUERY_PROVIDERS,
// update: (data) => data.authentication.providers
// }
// },
methods: {
async refresh() {
await this.$apollo.queries.services.refetch()
this.refreshCompleted = true
}
}
}
</script>
<style lang='scss'>
</style>

View File

@@ -0,0 +1,19 @@
<template lang='pug'>
v-container(fluid, fill-height)
v-layout(row wrap)
v-flex(xs12)
.headline.primary--text Content Rendering
.subheading.grey--text Configure how content is rendered
</template>
<script>
export default {
data() {
return {}
}
}
</script>
<style lang='scss'>
</style>

View File

@@ -0,0 +1,57 @@
<template lang='pug'>
v-card(flat)
v-card(color='grey lighten-5')
.pa-3.pt-4
.headline.primary--text Search Engine
.subheading.grey--text Configure the search capabilities of your wiki
v-tabs(color='grey lighten-4', fixed-tabs, slider-color='primary', show-arrows)
v-tab(key='settings'): v-icon settings
v-tab(key='db') Database
v-tab(key='algolia') Algolia
v-tab(key='elasticsearch') Elasticsearch
v-tab(key='solr') Solr
v-tab-item(key='settings')
v-card.pa-3
v-form
v-radio-group(v-model='selectedEngine')
v-radio(v-for='(engine, n) in engines', :key='n', :label='engine.text', :value='engine.value', color='primary')
v-divider
v-btn(color='primary')
v-icon(left) chevron_right
| Set Engine
v-btn(color='black', dark)
v-icon(left) refresh
| Rebuild Index
v-tab-item(key='db')
v-card.pa-3 TODO
v-tab-item(key='algolia')
v-card.pa-3 TODO
v-tab-item(key='elasticsearch')
v-card.pa-3 TODO
v-tab-item(key='solr')
v-card.pa-3 TODO
</template>
<script>
export default {
data() {
return {
engines: [
{ text: 'Disabled', value: 'disabled' },
{ text: 'Database (built-in)', value: 'db' },
{ text: 'Algolia', value: 'algolia' },
{ text: 'Elasticsearch', value: 'elasticsearch' },
{ text: 'Solr', value: 'solr' }
],
selectedEngine: 'db',
darkMode: false
}
}
}
</script>
<style lang='scss'>
</style>

View File

@@ -0,0 +1,19 @@
<template lang='pug'>
v-container(fluid, fill-height)
v-layout(row wrap)
v-flex(xs12)
.headline.primary--text Statistics
.subheading.grey--text Useful information about your wiki
</template>
<script>
export default {
data() {
return {}
}
}
</script>
<style lang='scss'>
</style>

View File

@@ -0,0 +1,61 @@
<template lang='pug'>
v-card(flat)
v-card(color='grey lighten-5')
.pa-3.pt-4
.headline.primary--text Storage
.subheading.grey--text Set backup and sync targets for your content
v-tabs(color='grey lighten-4', fixed-tabs, slider-color='primary', show-arrows)
v-tab(key='settings'): v-icon settings
v-tab(key='local') Local FS
v-tab(key='git') Git
v-tab(key='s3') Amazon S3
v-tab(key='azure') Azure Blob Storage
v-tab(key='digitalocean') DigitalOcean Spaces
v-tab(key='dropbox') Dropbox
v-tab(key='gdrive') Google Drive
v-tab(key='onedrive') OneDrive
v-tab(key='scp') SCP (SSH)
v-tab-item(key='settings')
v-card.pa-3
v-form
v-checkbox(
v-for='(target, n) in targets',
v-model='auths',
:key='n',
:label='target.text',
:value='target.value',
color='primary',
hide-details
)
v-divider
v-btn(color='primary')
v-icon(left) chevron_right
| Set Backup Targets
</template>
<script>
export default {
data() {
return {
targets: [
{ text: 'Local Filesystem', value: 'local' },
{ text: 'Git', value: 'auth0' },
{ text: 'Amazon S3', value: 'algolia' },
{ text: 'Azure Blob Storage', value: 'elasticsearch' },
{ text: 'DigitalOcean Spaces', value: 'solr' },
{ text: 'Dropbox', value: 'solr' },
{ text: 'Google Drive', value: 'solr' },
{ text: 'OneDrive', value: 'solr' },
{ text: 'SCP (SSH)', value: 'solr' }
],
auths: ['local']
}
}
}
</script>
<style lang='scss'>
</style>

View File

@@ -0,0 +1,140 @@
<template lang='pug'>
v-container(fluid, fill-height, grid-list-lg)
v-layout(row, wrap)
v-flex(xs12)
.headline.primary--text System Info
.subheading.grey--text Information about your system
v-layout.mt-3(row wrap)
v-flex(lg6 xs12)
v-card
v-btn(fab, absolute, right, top, small, light, @click='refresh'): v-icon refresh
v-list(two-line, dense)
v-subheader Wiki.js
v-list-tile(avatar)
v-list-tile-avatar
v-icon.blue.white--text system_update_alt
v-list-tile-content
v-list-tile-title Current Version
v-list-tile-sub-title {{ info.currentVersion }}
v-list-tile(avatar)
v-list-tile-avatar
v-icon.blue.white--text open_in_browser
v-list-tile-content
v-list-tile-title Latest Version
v-list-tile-sub-title {{ info.latestVersion }}
v-list-tile-action
v-list-tile-action-text Published X days ago
v-divider
v-subheader Host Information
v-list-tile(avatar)
v-list-tile-avatar
v-icon.blue-grey.white--text bubble_chart
v-list-tile-content
v-list-tile-title Operating System
v-list-tile-sub-title {{ info.operatingSystem }}
v-list-tile(avatar)
v-list-tile-avatar
v-icon.blue-grey.white--text computer
v-list-tile-content
v-list-tile-title Hostname
v-list-tile-sub-title {{ info.hostname }}
v-list-tile(avatar)
v-list-tile-avatar
v-icon.blue-grey.white--text nfc
v-list-tile-content
v-list-tile-title CPU Cores
v-list-tile-sub-title {{ info.cpuCores }}
v-list-tile(avatar)
v-list-tile-avatar
v-icon.blue-grey.white--text memory
v-list-tile-content
v-list-tile-title Total RAM
v-list-tile-sub-title {{ info.ramTotal }}
v-list-tile(avatar)
v-list-tile-avatar
v-icon.blue-grey.white--text last_page
v-list-tile-content
v-list-tile-title Working Directory
v-list-tile-sub-title {{ info.workingDirectory }}
v-flex(lg6 xs12)
v-card.pb-3
v-list(dense)
v-subheader Node.js
v-list-tile(avatar)
v-list-tile-avatar
v-avatar.light-green(size='40')
icon-node-js(fillColor='#FFFFFF')
v-list-tile-content
v-list-tile-title {{ info.nodeVersion }}
v-divider
v-subheader Redis
v-list-tile(avatar)
v-list-tile-avatar
v-avatar.red(size='40')
icon-cube(fillColor='#FFFFFF')
v-list-tile-content
v-list-tile-title {{ info.redisVersion }}
v-list-tile-sub-title {{ info.redisHost }}
v-list-tile-action
v-list-tile-action-text RAM Usage: {{ info.redisUsedRAM }} / {{ info.redisTotalRAM }}
v-divider
v-subheader PostgreSQL
v-list-tile(avatar)
v-list-tile-avatar
v-avatar.indigo.darken-1(size='40')
icon-database(fillColor='#FFFFFF')
v-list-tile-content
v-list-tile-title {{ info.postgreVersion }}
v-list-tile-sub-title {{ info.postgreHost }}
</template>
<script>
import IconCube from 'mdi/cube'
import IconDatabase from 'mdi/database'
import IconNodeJs from 'mdi/nodejs'
import systemInfoQuery from 'gql/admin-system-query-info.gql'
export default {
components: {
IconCube,
IconDatabase,
IconNodeJs
},
data() {
return {
info: {}
}
},
methods: {
async refresh() {
await this.$apollo.queries.info.refetch()
this.$store.commit('showNotification', {
message: 'System Info has been refreshed.',
style: 'success',
icon: 'cached'
})
}
},
apollo: {
info: {
query: systemInfoQuery,
update: (data) => data.system.info,
watchLoading (isLoading) {
this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-system-refresh')
}
}
}
}
</script>
<style lang='scss'>
</style>

View File

@@ -0,0 +1,51 @@
<template lang='pug'>
v-container(fluid, fill-height, grid-list-lg)
v-layout(row wrap)
v-flex(xs12)
.headline.primary--text Theme
.subheading.grey--text Modify the look &amp; feel of your wiki
v-form.pt-3
v-layout(row wrap)
v-flex(lg6 xs12)
v-card
v-toolbar(color='primary', dark, dense, flat)
v-toolbar-title
.subheading Theme
v-card-text
v-select(:items='themes', prepend-icon='palette', v-model='selectedTheme', label='Site Theme', persistent-hint, hint='Themes affect how content pages are displayed. Other site sections (such as the editor or admin area) are not affected.')
template(slot='item', slot-scope='data')
v-list-tile-avatar
v-icon.blue--text(dark) filter_frames
v-list-tile-content
v-list-tile-title(v-html='data.item.text')
v-list-tile-sub-title(v-html='data.item.author')
v-divider
v-switch(v-model='darkMode', label='Dark Mode', color='primary', persistent-hint, hint='Not recommended for accessibility')
v-divider
.px-3.pb-3
v-btn(color='primary') Save
v-flex(lg6 xs12)
v-card
v-toolbar(color='primary', dark, dense, flat)
v-toolbar-title
.subheading ---
v-card-text ---
</template>
<script>
export default {
data() {
return {
themes: [
{ text: 'Default', author: 'requarks.io', value: 'default' }
],
selectedTheme: 'default',
darkMode: false
}
}
}
</script>
<style lang='scss'>
</style>

View File

@@ -0,0 +1,135 @@
<template lang='pug'>
v-card(flat)
v-card(flat, color='grey lighten-5').pa-3.pt-4
.headline.blue--text.text--darken-2 Users
.subheading.grey--text Manage users
v-card
v-card-title
v-btn(color='primary', dark)
v-icon(left) add
| New User
v-btn(color='primary', dark)
v-icon(left) lock_outline
| Authorize User
v-btn(icon)
v-icon.grey--text refresh
v-spacer
v-text-field(append-icon='search', label='Search', single-line, hide-details, v-model='search')
v-data-table(
v-model='selected'
:items='items',
:headers='headers',
:search='search',
:pagination.sync='pagination',
:rows-per-page-items='[15]'
select-all,
hide-actions,
disable-initial-sort
)
template(slot='headers', slot-scope='props')
tr
th(width='50')
th.text-xs-right(
width='80'
:class='[`column sortable`, pagination.descending ? `desc` : `asc`, pagination.sortBy === `id` ? `active` : ``]'
@click='changeSort(`id`)'
)
v-icon(small) arrow_upward
| ID
th.text-xs-left(
v-for='header in props.headers'
:key='header.text'
:width='header.width'
:class='[`column sortable`, pagination.descending ? `desc` : `asc`, header.value === pagination.sortBy ? `active` : ``]'
@click='changeSort(header.value)'
)
| {{ header.text }}
v-icon(small) arrow_upward
template(slot='items', slot-scope='props')
tr(:active='props.selected')
td
v-checkbox(hide-details, :input-value='props.selected', color='blue darken-2', @click='props.selected = !props.selected')
td.text-xs-right {{ props.item.id }}
td {{ props.item.email }}
td {{ props.item.name }}
td {{ props.item.provider }}
td {{ props.item.createdOn }}
td {{ props.item.updatedOn }}
td: v-btn(icon): v-icon.grey--text.text--darken-1 more_horiz
template(slot='no-data')
v-alert(icon='warning', :value='true') No users to display!
.text-xs-center.py-2
v-pagination(v-model='pagination.page', :length='pages')
</template>
<script>
export default {
data() {
return {
selected: [],
pagination: {},
items: [
{ id: 1, email: 'user@test.com', name: 'John Doe', provider: 'local' },
{ id: 2, email: 'dude@test.com', name: 'John Doe', provider: 'local' },
{ id: 3, email: 'dude@test.com', name: 'John Doe', provider: 'local' },
{ id: 4, email: 'dude@test.com', name: 'John Doe', provider: 'local' },
{ id: 5, email: 'dude@test.com', name: 'John Doe', provider: 'local' },
{ id: 6, email: 'dude@test.com', name: 'John Doe', provider: 'local' },
{ id: 7, email: 'dude@test.com', name: 'John Doe', provider: 'local' },
{ id: 8, email: 'dude@test.com', name: 'John Doe', provider: 'local' },
{ id: 9, email: 'dude@test.com', name: 'John Doe', provider: 'local' },
{ id: 10, email: 'dude@test.com', name: 'John Doe', provider: 'local' },
{ id: 11, email: 'dude@test.com', name: 'John Doe', provider: 'local' },
{ id: 12, email: 'dude@test.com', name: 'John Doe', provider: 'local' },
{ id: 13, email: 'dude@test.com', name: 'John Doe', provider: 'local' },
{ id: 14, email: 'dude@test.com', name: 'John Doe', provider: 'local' },
{ id: 15, email: 'dude@test.com', name: 'John Doe', provider: 'local' },
{ id: 16, email: 'dude@test.com', name: 'John Doe', provider: 'local' },
{ id: 17, email: 'dude@test.com', name: 'John Doe', provider: 'local' },
{ id: 18, email: 'dude@test.com', name: 'John Doe', provider: 'local' },
{ id: 19, email: 'dude@test.com', name: 'John Doe', provider: 'local' },
{ id: 20, email: 'dude@test.com', name: 'John Doe', provider: 'local' }
],
headers: [
{ text: 'Email', value: 'email' },
{ text: 'Name', value: 'name' },
{ text: 'Provider', value: 'provider' },
{ text: 'Created On', value: 'createdOn' },
{ text: 'Updated On', value: 'updatedOn' },
{ text: '', value: 'actions', sortable: false, width: 50 }
],
search: ''
}
},
computed: {
pages () {
if (this.pagination.rowsPerPage == null || this.pagination.totalItems == null) {
return 0
}
return Math.ceil(this.pagination.totalItems / this.pagination.rowsPerPage)
}
},
methods: {
changeSort (column) {
if (this.pagination.sortBy === column) {
this.pagination.descending = !this.pagination.descending
} else {
this.pagination.sortBy = column
this.pagination.descending = false
}
},
toggleAll () {
if (this.selected.length) {
this.selected = []
} else {
this.selected = this.items.slice()
}
}
}
}
</script>
<style lang='scss'>
</style>

View File

@@ -0,0 +1,19 @@
<template lang='pug'>
v-container(fluid, fill-height)
v-layout(row wrap)
v-flex(xs12)
.headline.primary--text Utilities
.subheading.grey--text Maintenance and troubleshooting tools
</template>
<script>
export default {
data() {
return {}
}
}
</script>
<style lang='scss'>
</style>