Commit 2d2cf905 authored by Nick's avatar Nick

feat: admin create user + markdown help fix

parent 577a0610
...@@ -64,7 +64,7 @@ const graphQLLink = ApolloLink.from([ ...@@ -64,7 +64,7 @@ const graphQLLink = ApolloLink.from([
) )
store.commit('showNotification', { store.commit('showNotification', {
style: 'red', style: 'red',
message: `An expected error occured.`, message: `An unexpected error occured.`,
icon: 'warning' icon: 'warning'
}) })
} }
......
...@@ -26,8 +26,8 @@ ...@@ -26,8 +26,8 @@
v-list-item(to='/pages', v-if='hasPermission([`manage:system`, `write:pages`, `manage:pages`, `delete:pages`])') v-list-item(to='/pages', v-if='hasPermission([`manage:system`, `write:pages`, `manage:pages`, `delete:pages`])')
v-list-item-avatar(size='24'): v-icon mdi-file-document-outline v-list-item-avatar(size='24'): v-icon mdi-file-document-outline
v-list-item-title {{ $t('admin:pages.title') }} v-list-item-title {{ $t('admin:pages.title') }}
v-list-item-action v-list-item-action.pr-3
v-chip(x-small, disabled, :color='darkMode ? `grey darken-3-d4` : `grey lighten-4`') v-chip(x-small, :color='darkMode ? `grey darken-3-d4` : `grey lighten-5`')
.caption.grey--text {{ info.pagesTotal }} .caption.grey--text {{ info.pagesTotal }}
v-list-item(to='/theme', v-if='hasPermission([`manage:system`, `manage:theme`])') v-list-item(to='/theme', v-if='hasPermission([`manage:system`, `manage:theme`])')
v-list-item-avatar(size='24'): v-icon mdi-palette-outline v-list-item-avatar(size='24'): v-icon mdi-palette-outline
...@@ -38,14 +38,14 @@ ...@@ -38,14 +38,14 @@
v-list-item(to='/groups', v-if='hasPermission([`manage:system`, `manage:groups`, `write:groups`])') v-list-item(to='/groups', v-if='hasPermission([`manage:system`, `manage:groups`, `write:groups`])')
v-list-item-avatar(size='24'): v-icon mdi-account-group v-list-item-avatar(size='24'): v-icon mdi-account-group
v-list-item-title {{ $t('admin:groups.title') }} v-list-item-title {{ $t('admin:groups.title') }}
v-list-item-action v-list-item-action.pr-3
v-chip(x-small, disabled, :color='darkMode ? `grey darken-3-d4` : `grey lighten-4`') v-chip(x-small, :color='darkMode ? `grey darken-3-d4` : `grey lighten-4`')
.caption.grey--text {{ info.groupsTotal }} .caption.grey--text {{ info.groupsTotal }}
v-list-item(to='/users', v-if='hasPermission([`manage:system`, `manage:groups`, `write:groups`, `manage:users`, `write:users`])') v-list-item(to='/users', v-if='hasPermission([`manage:system`, `manage:groups`, `write:groups`, `manage:users`, `write:users`])')
v-list-item-avatar(size='24'): v-icon mdi-account-box v-list-item-avatar(size='24'): v-icon mdi-account-box
v-list-item-title {{ $t('admin:users.title') }} v-list-item-title {{ $t('admin:users.title') }}
v-list-item-action v-list-item-action.pr-3
v-chip(x-small, disabled, :color='darkMode ? `grey darken-3-d4` : `grey lighten-4`') v-chip(x-small, :color='darkMode ? `grey darken-3-d4` : `grey lighten-4`')
.caption.grey--text {{ info.usersTotal }} .caption.grey--text {{ info.usersTotal }}
template(v-if='hasPermission(`manage:system`)') template(v-if='hasPermission(`manage:system`)')
v-divider.my-2 v-divider.my-2
......
<template lang="pug"> <template lang="pug">
v-card.wiki-form(flat) v-card(flat)
v-card-text v-card-text
v-text-field( v-text-field(
outline outlined
background-color='grey lighten-3'
v-model='group.name' v-model='group.name'
label='Group Name' label='Group Name'
counter='255' counter='255'
prepend-icon='people' prepend-icon='mdi-account-group'
) )
v-alert.radius-7( v-alert.radius-7(
v-if='group.isSystem' v-if='group.isSystem'
color='orange darken-2' color='orange darken-2'
:class='$vuetify.theme.dark ? "grey darken-4" : "orange lighten-5"' :class='$vuetify.theme.dark ? "grey darken-4" : "orange lighten-5"'
outline outlined
:value='true' :value='true'
icon='lock_outline' icon='mdi-lock-outline'
) This is a system group. Some permissions cannot be modified. ) This is a system group. Some permissions cannot be modified.
v-container.px-3.pb-3.pt-0(fluid, grid-list-md) v-container.px-3.pb-3.pt-0(fluid, grid-list-md)
v-layout(row, wrap) v-layout(row, wrap)
v-flex(xs12, md6, lg4, v-for='pmGroup in permissions', :key='pmGroup.category') v-flex(xs12, md6, lg4, v-for='pmGroup in permissions', :key='pmGroup.category')
v-card.md2(flat, :class='$vuetify.theme.dark ? "grey darken-3-d5" : "white"') v-card.md2(flat, :class='$vuetify.theme.dark ? "grey darken-3-d5" : "grey lighten-5"')
v-subheader {{pmGroup.category}} .overline.px-5.pt-5.pb-3.grey--text.text--darken-2 {{pmGroup.category}}
v-card-text.pt-0 v-card-text.pt-0
template(v-for='(pm, idx) in pmGroup.items') template(v-for='(pm, idx) in pmGroup.items')
v-checkbox.pt-0( v-checkbox.pt-0(
...@@ -32,7 +31,7 @@ ...@@ -32,7 +31,7 @@
color='primary' color='primary'
v-model='group.permissions' v-model='group.permissions'
:value='pm.permission' :value='pm.permission'
:append-icon='pm.warning ? "warning" : null', :append-icon='pm.warning ? "mdi-alert" : null',
:disabled='(group.isSystem && pm.restrictedForSystem) || group.id === 1 || pm.disabled' :disabled='(group.isSystem && pm.restrictedForSystem) || group.id === 1 || pm.disabled'
) )
v-divider.mt-3(v-if='idx < pmGroup.items.length - 1') v-divider.mt-3(v-if='idx < pmGroup.items.length - 1')
......
<template lang="pug"> <template lang="pug">
v-card.wiki-form v-card
v-card-title(:class='$vuetify.theme.dark ? `grey darken-3-d3` : `grey lighten-5`') v-card-title.pb-4(:class='$vuetify.theme.dark ? `grey darken-3-d3` : `grey lighten-5`')
v-text-field( v-text-field(
outline outlined
flat flat
prepend-inner-icon='search' prepend-inner-icon='mdi-magnify'
v-model='search' v-model='search'
label='Search Group Users...' label='Search Group Users...'
hide-details hide-details
) )
v-spacer v-spacer
v-btn(color='primary', depressed, @click='searchUserDialog = true', :disabled='group.id === 2') v-btn(color='primary', depressed, @click='searchUserDialog = true', :disabled='group.id === 2')
v-icon(left) assignment_ind v-icon(left) mdi-clipboard-account
| Assign User | Assign User
v-data-table( v-data-table(
:items='group.users', :items='group.users',
:headers='headers', :headers='headers',
:search='search' :search='search'
:pagination.sync='pagination', :page.sync='pagination'
:rows-per-page-items='[15]' :items-per-page='15'
hide-actions @page-count='pageCount = $event'
must-sort,
hide-default-footer
) )
template(slot='items', slot-scope='props') template(slot='item', slot-scope='props')
tr(:active='props.selected') tr(:active='props.selected')
td.text-xs-right {{ props.item.id }} td.text-xs-right {{ props.item.id }}
td {{ props.item.name }} td {{ props.item.name }}
td {{ props.item.email }} td {{ props.item.email }}
td td
v-menu(bottom, right, min-width='200') v-menu(bottom, right, min-width='200')
v-btn(icon, slot='activator'): v-icon.grey--text.text--darken-1 more_horiz template(v-slot:activator='{ on }')
v-list v-btn(icon, v-on='on', small)
v-icon.grey--text.text--darken-1 mdi-dots-horizontal
v-list(dense, nav)
v-list-item(:to='`/users/` + props.item.id') v-list-item(:to='`/users/` + props.item.id')
v-list-item-action: v-icon(color='primary') person v-list-item-action: v-icon(color='primary') mdi-account-outline
v-list-item-content v-list-item-content
v-list-item-title View User Profile v-list-item-title View User Profile
template(v-if='props.item.id !== 2') template(v-if='props.item.id !== 2')
v-divider
v-list-item(@click='unassignUser(props.item.id)') v-list-item(@click='unassignUser(props.item.id)')
v-list-item-action: v-icon(color='orange') highlight_off v-list-item-action: v-icon(color='orange') mdi-account-remove-outline
v-list-item-content v-list-item-content
v-list-item-title Unassign v-list-item-title Unassign
template(slot='no-data') template(slot='no-data')
v-alert.ma-3(icon='warning', :value='true', outline) No users to display. v-alert.ma-3(icon='warning', outlined) No users to display.
.text-xs-center.py-2(v-if='group.users.length > 15') .text-center.py-2(v-if='group.users.length > 15')
v-pagination(v-model='pagination.page', :length='pages') v-pagination(v-model='pagination', :length='pageCount')
user-search(v-model='searchUserDialog', @select='assignUser') user-search(v-model='searchUserDialog', @select='assignUser')
</template> </template>
...@@ -73,7 +76,8 @@ export default { ...@@ -73,7 +76,8 @@ export default {
{ text: '', value: 'actions', sortable: false, width: 50 } { text: '', value: 'actions', sortable: false, width: 50 }
], ],
searchUserDialog: false, searchUserDialog: false,
pagination: {}, pagination: 1,
pageCount: 0,
search: '' search: ''
} }
}, },
......
...@@ -10,11 +10,12 @@ ...@@ -10,11 +10,12 @@
v-spacer v-spacer
.caption.grey--text ID #[strong {{group.id}}] .caption.grey--text ID #[strong {{group.id}}]
v-divider.mx-3(vertical) v-divider.mx-3(vertical)
v-btn(color='grey', large, outline, to='/groups') v-btn(color='grey', large, outlined, to='/groups')
v-icon arrow_back v-icon mdi-arrow-left
v-dialog(v-model='deleteGroupDialog', max-width='500', v-if='!group.isSystem') v-dialog(v-model='deleteGroupDialog', max-width='500', v-if='!group.isSystem')
v-btn(color='red', large, outline, slot='activator') template(v-slot:activator='{ on }')
v-icon(color='red') delete v-btn(color='red', large, outlined, v-on='{ on }')
v-icon(color='red') mdi-trash-can-outline
v-card v-card
.dialog-header.is-red Delete Group? .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-text Are you sure you want to delete group #[strong {{ group.name }}]? All users will be unassigned from this group.
...@@ -22,11 +23,11 @@ ...@@ -22,11 +23,11 @@
v-spacer v-spacer
v-btn(flat, @click='deleteGroupDialog = false') Cancel v-btn(flat, @click='deleteGroupDialog = false') Cancel
v-btn(color='red', dark, @click='deleteGroup') Delete v-btn(color='red', dark, @click='deleteGroup') Delete
v-btn(color='success', large, depressed, @click='updateGroup') v-btn.ml-2(color='success', large, depressed, @click='updateGroup')
v-icon(left) check v-icon(left) mdi-check
span Update Group span Update Group
v-card.mt-3 v-card.mt-3
v-tabs(v-model='tab', :color='$vuetify.theme.dark ? "primary" : "grey darken-2"', fixed-tabs, slider-color='white', show-arrows, dark) v-tabs(v-model='tab', :background-color='$vuetify.theme.dark ? "primary" : "grey darken-2"', fixed-tabs, slider-color='white', show-arrows, dark)
v-tab(key='permissions') Permissions v-tab(key='permissions') Permissions
v-tab(key='rules') Page Rules v-tab(key='rules') Page Rules
v-tab(key='users') Users v-tab(key='users') Users
...@@ -69,7 +70,7 @@ export default { ...@@ -69,7 +70,7 @@ export default {
users: [] users: []
}, },
deleteGroupDialog: false, deleteGroupDialog: false,
tab: '1' tab: null
} }
}, },
methods: { methods: {
......
...@@ -37,25 +37,29 @@ ...@@ -37,25 +37,29 @@
:items='groups' :items='groups'
:headers='headers' :headers='headers'
:search='search' :search='search'
:pagination.sync='pagination' :page.sync='pagination'
:rows-per-page-items='[15]' :items-per-page='15'
hide-actions :loading='loading'
@page-count='pageCount = $event'
must-sort,
hide-default-footer
) )
template(slot='items', slot-scope='props') template(slot='item', slot-scope='props')
tr.is-clickable(:active='props.selected', @click='$router.push("/groups/" + props.item.id)') tr.is-clickable(:active='props.selected', @click='$router.push("/groups/" + props.item.id)')
td.text-xs-right {{ props.item.id }} td {{ props.item.id }}
td: strong {{ props.item.name }} td: strong {{ props.item.name }}
td {{ props.item.userCount }} td {{ props.item.userCount }}
td {{ props.item.createdAt | moment('calendar') }} td {{ props.item.createdAt | moment('calendar') }}
td {{ props.item.updatedAt | moment('calendar') }} td {{ props.item.updatedAt | moment('calendar') }}
td td
v-tooltip(left, v-if='props.item.isSystem') v-tooltip(left, v-if='props.item.isSystem')
v-icon(slot='activator') lock_outline template(v-slot:activator='{ on }')
v-icon(v-on='on') mdi-lock-outline
span System Group span System Group
template(slot='no-data') template(slot='no-data')
v-alert.ma-3(icon='warning', :value='true', outline) No groups to display. v-alert.ma-3(icon='warning', :value='true', outline) No groups to display.
.text-xs-center.py-2(v-if='this.pages > 0') .text-xs-center.py-2(v-if='pageCount > 1')
v-pagination(v-model='pagination.page', :length='pages') v-pagination(v-model='pagination', :length='pageCount')
</template> </template>
<script> <script>
...@@ -70,7 +74,8 @@ export default { ...@@ -70,7 +74,8 @@ export default {
newGroupDialog: false, newGroupDialog: false,
newGroupName: '', newGroupName: '',
selectedGroup: {}, selectedGroup: {},
pagination: {}, pagination: 1,
pageCount: 0,
groups: [], groups: [],
headers: [ headers: [
{ text: 'ID', value: 'id', width: 50, align: 'right' }, { text: 'ID', value: 'id', width: 50, align: 'right' },
...@@ -80,16 +85,8 @@ export default { ...@@ -80,16 +85,8 @@ export default {
{ text: 'Last Updated', value: 'updatedAt', width: 250 }, { text: 'Last Updated', value: 'updatedAt', width: 250 },
{ text: '', value: 'isSystem', width: 20, sortable: false } { text: '', value: 'isSystem', width: 20, sortable: false }
], ],
search: '' search: '',
} loading: false
},
computed: {
pages () {
if (this.pagination.rowsPerPage == null || this.pagination.totalItems == null) {
return 0
}
return Math.ceil(this.pagination.totalItems / this.pagination.rowsPerPage)
} }
}, },
watch: { watch: {
...@@ -158,6 +155,7 @@ export default { ...@@ -158,6 +155,7 @@ export default {
fetchPolicy: 'network-only', fetchPolicy: 'network-only',
update: (data) => data.groups.list, update: (data) => data.groups.list,
watchLoading (isLoading) { watchLoading (isLoading) {
this.loading = isLoading
this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-groups-refresh') this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-groups-refresh')
} }
} }
......
...@@ -46,7 +46,7 @@ ...@@ -46,7 +46,7 @@
v-list-item(:key='tgt.key') v-list-item(:key='tgt.key')
template(v-if='tgt.status === `pending`') template(v-if='tgt.status === `pending`')
v-list-item-avatar(color='purple') v-list-item-avatar(color='purple')
v-icon(color='white') schedule v-icon(color='white') mdi-clock-outline
v-list-item-content v-list-item-content
v-list-item-title.body-2 {{tgt.title}} v-list-item-title.body-2 {{tgt.title}}
v-list-item-sub-title.purple--text.caption {{tgt.status}} v-list-item-sub-title.purple--text.caption {{tgt.status}}
...@@ -54,20 +54,20 @@ ...@@ -54,20 +54,20 @@
v-progress-circular(indeterminate, :size='20', :width='2', color='purple') v-progress-circular(indeterminate, :size='20', :width='2', color='purple')
template(v-else-if='tgt.status === `operational`') template(v-else-if='tgt.status === `operational`')
v-list-item-avatar(color='green') v-list-item-avatar(color='green')
v-icon(color='white') check_circle v-icon(color='white') mdi-check-circle
v-list-item-content v-list-item-content
v-list-item-title.body-2 {{tgt.title}} v-list-item-title.body-2 {{tgt.title}}
v-list-item-sub-title.green--text.caption {{$t('admin:storage.lastSync', { time: $options.filters.moment(tgt.lastAttempt, 'from') })}} v-list-item-sub-title.green--text.caption {{$t('admin:storage.lastSync', { time: $options.filters.moment(tgt.lastAttempt, 'from') })}}
template(v-else) template(v-else)
v-list-item-avatar(color='red') v-list-item-avatar(color='red')
v-icon(color='white') highlight_off v-icon(color='white') mdi-close-circle-outline
v-list-item-content v-list-item-content
v-list-item-title.body-2 {{tgt.title}} v-list-item-title.body-2 {{tgt.title}}
v-list-item-sub-title.red--text.caption {{$t('admin:storage.lastSyncAttempt', { time: $options.filters.moment(tgt.lastAttempt, 'from') })}} v-list-item-sub-title.red--text.caption {{$t('admin:storage.lastSyncAttempt', { time: $options.filters.moment(tgt.lastAttempt, 'from') })}}
v-list-item-action v-list-item-action
v-menu v-menu
v-btn(slot='activator', icon) v-btn(slot='activator', icon)
v-icon(color='red') info v-icon(color='red') mdi-information
v-card(width='450') v-card(width='450')
v-toolbar(flat, color='red', dark, dense) {{$t('admin:storage.errorMsg')}} v-toolbar(flat, color='red', dark, dense) {{$t('admin:storage.errorMsg')}}
v-card-text {{tgt.message}} v-card-text {{tgt.message}}
......
<template lang="pug"> <template lang="pug">
v-dialog(v-model='isShown', max-width='650', persistent) v-dialog(v-model='isShown', max-width='650', persistent)
v-card.wiki-form v-card
.dialog-header.is-short .dialog-header.is-short
v-icon.mr-3(color='white') mdi-plus v-icon.mr-3(color='white') mdi-plus
span New User span New User
...@@ -59,13 +59,14 @@ ...@@ -59,13 +59,14 @@
data-vv-as='Name', data-vv-as='Name',
data-vv-scope='newUser', data-vv-scope='newUser',
:error-messages='errors.collect(`newUser.name`)' :error-messages='errors.collect(`newUser.name`)'
:hint='provider === `local` ? `Can be changed by the user.` : `May be overwritten by the provider during login.`'
key='newUserName' key='newUserName'
persistent-hint persistent-hint
) )
v-select( v-select.mt-2(
:items='groups' :items='groups'
item-text='name' item-text='name'
item-value='key' item-value='id'
outlined outlined
prepend-icon='mdi-account-group' prepend-icon='mdi-account-group'
v-model='group' v-model='group'
...@@ -90,8 +91,12 @@ ...@@ -90,8 +91,12 @@
v-card-chin v-card-chin
v-spacer v-spacer
v-btn(text, @click='isShown = false') Cancel v-btn(text, @click='isShown = false') Cancel
v-btn(depressed, color='primary', @click='newUser(false)', :disabled='errors.any(`newUser`)') Create v-btn.px-3(depressed, color='primary', @click='newUser(false)', :disabled='errors.any(`newUser`)')
v-btn(depressed, color='primary', @click='newUser(true)', :disabled='errors.any(`newUser`)') Create and Close v-icon(left) mdi-chevron-right
span Create
v-btn.px-3(depressed, color='primary', @click='newUser(true)', :disabled='errors.any(`newUser`)')
v-icon(left) mdi-chevron-double-right
span Create and Close
</template> </template>
<script> <script>
...@@ -116,7 +121,7 @@ export default { ...@@ -116,7 +121,7 @@ export default {
password: '', password: '',
name: '', name: '',
groups: [], groups: [],
group: '', group: [],
mustChangePwd: false, mustChangePwd: false,
sendWelcomeEmail: false sendWelcomeEmail: false
} }
...@@ -155,7 +160,7 @@ export default { ...@@ -155,7 +160,7 @@ export default {
email: this.email, email: this.email,
passwordRaw: this.password, passwordRaw: this.password,
name: this.name, name: this.name,
groups: this.groups, groups: this.group,
mustChangePassword: this.mustChangePwd, mustChangePassword: this.mustChangePwd,
sendWelcomeEmail: this.sendWelcomeEmail sendWelcomeEmail: this.sendWelcomeEmail
}, },
...@@ -176,6 +181,7 @@ export default { ...@@ -176,6 +181,7 @@ export default {
if (close) { if (close) {
this.isShown = false this.isShown = false
this.$emit('refresh')
} else { } else {
this.$refs.emailInput.focus() this.$refs.emailInput.focus()
} }
......
...@@ -39,45 +39,36 @@ ...@@ -39,45 +39,36 @@
:items='usersFiltered', :items='usersFiltered',
:headers='headers', :headers='headers',
:search='search', :search='search',
:pagination.sync='pagination', :page.sync='pagination'
:rows-per-page-items='[15]' :items-per-page='15'
:loading='loading' :loading='loading'
hide-actions, @page-count='pageCount = $event'
disable-initial-sort hide-default-footer
) )
template(slot='headers', slot-scope='props') template(slot='item', slot-scope='props')
tr
th.text-xs-left(
v-for='header in props.headers'
:key='header.text'
:width='header.width'
:class='[`column`, header.sortable ? `sortable` : ``, pagination.descending ? `desc` : `asc`, header.value === pagination.sortBy ? `active` : ``]'
@click='changeSort(header.value)'
)
| {{ header.text }}
v-icon(small, v-if='header.sortable') arrow_upward
template(slot='items', slot-scope='props')
tr.is-clickable(:active='props.selected', @click='$router.push("/users/" + props.item.id)') tr.is-clickable(:active='props.selected', @click='$router.push("/users/" + props.item.id)')
//- td //- td
v-checkbox(hide-details, :input-value='props.selected', color='blue darken-2', @click='props.selected = !props.selected') 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.id }}
td: strong {{ props.item.name }} td: strong {{ props.item.name }}
td {{ props.item.email }} td {{ props.item.email }}
td {{ props.item.providerKey }} td {{ props.item.providerKey }}
td {{ props.item.createdAt | moment('from') }} td {{ props.item.createdAt | moment('from') }}
td td
v-tooltip(left, v-if='props.item.isSystem') v-tooltip(left, v-if='props.item.isSystem')
v-icon(slot='activator') lock_outline template(v-slot:activator='{ on }')
v-icon(v-on='{ on }') mdi-lock-outline
span System User span System User
template(slot='no-data') template(slot='no-data')
.pa-3 .pa-3
v-alert(icon='warning', :value='true', outline) No users to display! v-alert.text-left(icon='mdi-alert', outlined, color='grey')
v-card-chin(v-if='this.pages > 1') em.body-2 No users to display!
v-card-chin(v-if='pageCount > 1')
v-spacer v-spacer
v-pagination(v-model='pagination.page', :length='pages') v-pagination(v-model='pagination', :length='pageCount')
v-spacer v-spacer
user-create(v-model='isCreateDialogShown') user-create(v-model='isCreateDialogShown', @refresh='refresh(false)')
</template> </template>
<script> <script>
...@@ -95,7 +86,8 @@ export default { ...@@ -95,7 +86,8 @@ export default {
data() { data() {
return { return {
selected: [], selected: [],
pagination: {}, pagination: 1,
pageCount: 0,
users: [], users: [],
headers: [ headers: [
{ text: 'ID', value: 'id', width: 80, sortable: true }, { text: 'ID', value: 'id', width: 80, sortable: true },
...@@ -116,40 +108,20 @@ export default { ...@@ -116,40 +108,20 @@ export default {
usersFiltered () { usersFiltered () {
const all = this.filterStrategy === 'all' || this.filterStrategy === '' const all = this.filterStrategy === 'all' || this.filterStrategy === ''
return _.filter(this.users, u => all || u.providerKey === this.filterStrategy) return _.filter(this.users, u => all || u.providerKey === this.filterStrategy)
},
pages () {
if (this.pagination.rowsPerPage == null || this.usersFiltered.length < 1) {
return 0
}
return Math.ceil(this.usersFiltered.length / this.pagination.rowsPerPage)
} }
}, },
methods: { methods: {
createUser() { createUser() {
this.isCreateDialogShown = true this.isCreateDialogShown = true
}, },
async refresh() { async refresh(notify = true) {
await this.$apollo.queries.users.refetch() await this.$apollo.queries.users.refetch()
this.$store.commit('showNotification', { if (notify) {
message: 'Users list has been refreshed.', this.$store.commit('showNotification', {
style: 'success', message: 'Users list has been refreshed.',
icon: 'cached' style: 'success',
}) icon: 'cached'
}, })
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()
} }
} }
}, },
......
...@@ -16,10 +16,10 @@ ...@@ -16,10 +16,10 @@
) )
v-card-text v-card-text
v-text-field( v-text-field(
outline outlined
:label='$t(`common:user.searchPlaceholder`)' :label='$t(`common:user.searchPlaceholder`)'
v-model='search' v-model='search'
prepend-inner-icon='search' prepend-inner-icon='mdi-account-search-outline'
color='primary' color='primary'
ref='searchIpt' ref='searchIpt'
hide-details hide-details
...@@ -35,14 +35,14 @@ ...@@ -35,14 +35,14 @@
span.body-1.white--text {{usr.name | initials}} span.body-1.white--text {{usr.name | initials}}
v-list-item-content v-list-item-content
v-list-item-title.body-2 {{usr.name}} v-list-item-title.body-2 {{usr.name}}
v-list-item-sub-title {{usr.email}} v-list-item-subtitle {{usr.email}}
v-list-item-action v-list-item-action
v-icon(color='primary') arrow_forward v-icon(color='primary') mdi-arrow-right
v-divider.my-0(v-if='idx < items.length - 1') v-divider.my-0(v-if='idx < items.length - 1')
v-card-chin v-card-chin
v-spacer v-spacer
v-btn( v-btn(
flat text
@click='close' @click='close'
:disabled='loading' :disabled='loading'
) {{$t('common:actions.cancel')}} ) {{$t('common:actions.cancel')}}
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
v-card-text v-card-text
.d-flex .d-flex
v-toolbar.radius-7(color='teal lighten-5', dense, flat, height='44') v-toolbar.radius-7(color='teal lighten-5', dense, flat, height='44')
v-icon.mr-3(color='teal') info v-icon.mr-3(color='teal') mdi-information-variant
.body-2.teal--text Markdown Reference .body-2.teal--text Markdown Reference
.body-2.mt-3 Bold .body-2.mt-3 Bold
v-layout(row) v-layout(row)
...@@ -15,8 +15,8 @@ ...@@ -15,8 +15,8 @@
v-card.editor-markdown-help-source(flat) v-card.editor-markdown-help-source(flat)
v-card-text v-card-text
div **Lorem ipsum** div **Lorem ipsum**
v-icon chevron_right v-icon mdi-chevron-right
v-flex(xs6) v-flex
v-card.editor-markdown-help-result(flat) v-card.editor-markdown-help-result(flat)
v-card-text v-card-text
.caption: strong Lorem ipsum .caption: strong Lorem ipsum
...@@ -26,8 +26,8 @@ ...@@ -26,8 +26,8 @@
v-card.editor-markdown-help-source(flat) v-card.editor-markdown-help-source(flat)
v-card-text v-card-text
div *Lorem ipsum* div *Lorem ipsum*
v-icon chevron_right v-icon mdi-chevron-right
v-flex(xs6) v-flex
v-card.editor-markdown-help-result(flat) v-card.editor-markdown-help-result(flat)
v-card-text v-card-text
.caption: em Lorem ipsum .caption: em Lorem ipsum
...@@ -37,8 +37,8 @@ ...@@ -37,8 +37,8 @@
v-card.editor-markdown-help-source(flat) v-card.editor-markdown-help-source(flat)
v-card-text v-card-text
div ~~Lorem ipsum~~ div ~~Lorem ipsum~~
v-icon chevron_right v-icon mdi-chevron-right
v-flex(xs6) v-flex
v-card.editor-markdown-help-result(flat) v-card.editor-markdown-help-result(flat)
v-card-text v-card-text
.caption(style='text-decoration: line-through;') Lorem ipsum .caption(style='text-decoration: line-through;') Lorem ipsum
...@@ -53,8 +53,8 @@ ...@@ -53,8 +53,8 @@
div #### Header 4 div #### Header 4
div ##### Header 5 div ##### Header 5
div ###### Header 6 div ###### Header 6
v-icon chevron_right v-icon mdi-chevron-right
v-flex(xs6) v-flex
v-card.editor-markdown-help-result(flat) v-card.editor-markdown-help-result(flat)
v-card-text v-card-text
div(style='font-weight: 700; font-size: 24px;') Header 1 div(style='font-weight: 700; font-size: 24px;') Header 1
...@@ -72,8 +72,8 @@ ...@@ -72,8 +72,8 @@
div - Unordered List Item 1 div - Unordered List Item 1
div - Unordered List Item 2 div - Unordered List Item 2
div - Unordered List Item 3 div - Unordered List Item 3
v-icon chevron_right v-icon mdi-chevron-right
v-flex(xs6) v-flex
v-card.editor-markdown-help-result(flat) v-card.editor-markdown-help-result(flat)
v-card-text v-card-text
ul ul
...@@ -89,8 +89,8 @@ ...@@ -89,8 +89,8 @@
div 1. Ordered List Item 1 div 1. Ordered List Item 1
div 1. Ordered List Item 2 div 1. Ordered List Item 2
div 1. Ordered List Item 3 div 1. Ordered List Item 3
v-icon chevron_right v-icon mdi-chevron-right
v-flex(xs6) v-flex
v-card.editor-markdown-help-result(flat) v-card.editor-markdown-help-result(flat)
v-card-text v-card-text
ol ol
...@@ -103,8 +103,8 @@ ...@@ -103,8 +103,8 @@
v-card.editor-markdown-help-source(flat) v-card.editor-markdown-help-source(flat)
v-card-text v-card-text
div ![Caption Text](/path/to/image.jpg) div ![Caption Text](/path/to/image.jpg)
v-icon chevron_right v-icon mdi-chevron-right
v-flex(xs6) v-flex
v-card.editor-markdown-help-result(flat) v-card.editor-markdown-help-result(flat)
v-card-text v-card-text
img(src='https://via.placeholder.com/150x50.png') img(src='https://via.placeholder.com/150x50.png')
...@@ -113,7 +113,7 @@ ...@@ -113,7 +113,7 @@
v-card-text v-card-text
.d-flex .d-flex
v-toolbar.radius-7(color='teal lighten-5', dense, flat, height='44') v-toolbar.radius-7(color='teal lighten-5', dense, flat, height='44')
v-icon.mr-3(color='teal') info v-icon.mr-3(color='teal') mdi-information-variant
.body-2.teal--text Markdown Reference (continued) .body-2.teal--text Markdown Reference (continued)
.body-2.mt-3 Links .body-2.mt-3 Links
v-layout(row) v-layout(row)
...@@ -121,8 +121,8 @@ ...@@ -121,8 +121,8 @@
v-card.editor-markdown-help-source(flat) v-card.editor-markdown-help-source(flat)
v-card-text v-card-text
div [Link Text](https://wiki.js.org) div [Link Text](https://wiki.js.org)
v-icon chevron_right v-icon mdi-chevron-right
v-flex(xs6) v-flex
v-card.editor-markdown-help-result(flat) v-card.editor-markdown-help-result(flat)
v-card-text v-card-text
.caption: a(href='https://wiki.js.org', target='_blank') Link Text .caption: a(href='https://wiki.js.org', target='_blank') Link Text
...@@ -132,8 +132,8 @@ ...@@ -132,8 +132,8 @@
v-card.editor-markdown-help-source(flat) v-card.editor-markdown-help-source(flat)
v-card-text v-card-text
div Lorem ^ipsum^ div Lorem ^ipsum^
v-icon chevron_right v-icon mdi-chevron-right
v-flex(xs6) v-flex
v-card.editor-markdown-help-result(flat) v-card.editor-markdown-help-result(flat)
v-card-text v-card-text
.caption Lorem #[sup ipsum] .caption Lorem #[sup ipsum]
...@@ -143,8 +143,8 @@ ...@@ -143,8 +143,8 @@
v-card.editor-markdown-help-source(flat) v-card.editor-markdown-help-source(flat)
v-card-text v-card-text
div Lorem ~ipsum~ div Lorem ~ipsum~
v-icon chevron_right v-icon mdi-chevron-right
v-flex(xs6) v-flex
v-card.editor-markdown-help-result(flat) v-card.editor-markdown-help-result(flat)
v-card-text v-card-text
.caption: em Lorem #[sub ipsum] .caption: em Lorem #[sub ipsum]
...@@ -156,8 +156,8 @@ ...@@ -156,8 +156,8 @@
div Lorem ipsum div Lorem ipsum
div --- div ---
div Dolor sit amet div Dolor sit amet
v-icon chevron_right v-icon mdi-chevron-right
v-flex(xs6) v-flex
v-card.editor-markdown-help-result(flat) v-card.editor-markdown-help-result(flat)
v-card-text v-card-text
.caption Lorem ipsum .caption Lorem ipsum
...@@ -169,8 +169,8 @@ ...@@ -169,8 +169,8 @@
v-card.editor-markdown-help-source(flat) v-card.editor-markdown-help-source(flat)
v-card-text v-card-text
div Lorem `ipsum dolor sit` amet div Lorem `ipsum dolor sit` amet
v-icon chevron_right v-icon mdi-chevron-right
v-flex(xs6) v-flex
v-card.editor-markdown-help-result(flat) v-card.editor-markdown-help-result(flat)
v-card-text v-card-text
.caption Lorem #[code ipsum dolor sit] amet .caption Lorem #[code ipsum dolor sit] amet
...@@ -185,8 +185,8 @@ ...@@ -185,8 +185,8 @@
div.pl-3 echo 'Lorem ipsum' div.pl-3 echo 'Lorem ipsum'
div } div }
div ``` div ```
v-icon chevron_right v-icon mdi-chevron-right
v-flex(xs6) v-flex
v-card.editor-markdown-help-result(flat) v-card.editor-markdown-help-result(flat)
v-card-text.contents v-card-text.contents
pre.prismjs.line-numbers.language-js pre.prismjs.line-numbers.language-js
...@@ -211,8 +211,8 @@ ...@@ -211,8 +211,8 @@
div &gt; Lorem ipsum div &gt; Lorem ipsum
div &gt; dolor sit amet div &gt; dolor sit amet
div &gt; consectetur adipiscing elit div &gt; consectetur adipiscing elit
v-icon chevron_right v-icon mdi-chevron-right
v-flex(xs6) v-flex
v-card.editor-markdown-help-result(flat) v-card.editor-markdown-help-result(flat)
v-card-text v-card-text
blockquote(style='border: 1px solid #263238; border-radius: .5rem; padding: 1rem 24px;') Lorem ipsum#[br]dolor sit amet#[br]consectetur adipiscing elit blockquote(style='border: 1px solid #263238; border-radius: .5rem; padding: 1rem 24px;') Lorem ipsum#[br]dolor sit amet#[br]consectetur adipiscing elit
...@@ -221,7 +221,7 @@ ...@@ -221,7 +221,7 @@
v-card.radius-7.animated.fadeInUp.wait-p2s(light) v-card.radius-7.animated.fadeInUp.wait-p2s(light)
v-card-text v-card-text
v-toolbar.radius-7(color='teal lighten-5', dense, flat) v-toolbar.radius-7(color='teal lighten-5', dense, flat)
v-icon.mr-3(color='teal') keyboard v-icon.mr-3(color='teal') mdi-keyboard
.body-2.teal--text Keyboard Shortcuts .body-2.teal--text Keyboard Shortcuts
v-list.editor-markdown-help-kbd(two-line, dense) v-list.editor-markdown-help-kbd(two-line, dense)
v-list-item v-list-item
...@@ -255,13 +255,13 @@ ...@@ -255,13 +255,13 @@
v-list-item v-list-item
v-list-item-content v-list-item-content
v-list-item-title.body-2 Distraction Free Mode v-list-item-title.body-2 Distraction Free Mode
v-list-item-sub-title Press <kbd>Esc</kbd> to exit. v-list-item-subtitle Press <kbd>Esc</kbd> to exit.
v-list-item-action #[kbd F11] v-list-item-action #[kbd F11]
v-card.radius-7.animated.fadeInUp.wait-p3s.mt-3(light) v-card.radius-7.animated.fadeInUp.wait-p3s.mt-3(light)
v-card-text v-card-text
v-toolbar.radius-7(color='teal lighten-5', dense, flat) v-toolbar.radius-7(color='teal lighten-5', dense, flat)
v-icon.mr-3(color='teal') mouse v-icon.mr-3(color='teal') mdi-mouse
.body-2.teal--text Multi-Selection .body-2.teal--text Multi-Selection
v-list.editor-markdown-help-kbd(two-line, dense) v-list.editor-markdown-help-kbd(two-line, dense)
v-list-item v-list-item
...@@ -303,6 +303,10 @@ export default { ...@@ -303,6 +303,10 @@ export default {
font-family: 'Roboto Mono', monospace; font-family: 'Roboto Mono', monospace;
font-size: 14px; font-size: 14px;
color: #FFF !important; color: #FFF !important;
.v-card__text {
color: #FFF !important;
}
} }
&-result { &-result {
...@@ -327,7 +331,7 @@ export default { ...@@ -327,7 +331,7 @@ export default {
} }
&-kbd { &-kbd {
.v-list__tile__action { .v-list-item__action {
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
...@@ -345,12 +349,5 @@ export default { ...@@ -345,12 +349,5 @@ export default {
} }
} }
} }
&-ref {
.v-list__tile__action {
flex-direction: row;
align-items: center;
}
}
} }
</style> </style>
...@@ -109,7 +109,7 @@ ...@@ -109,7 +109,7 @@
v-spacer v-spacer
template(v-if='screen === "login" && isSocialShown') template(v-if='screen === "login" && isSocialShown')
v-divider v-divider
v-card-text.grey.lighten-4.text-xs-center v-card-text.grey.lighten-4.text-center
.pb-2.body-2.text-xs-center.grey--text.text--darken-2 {{ $t('auth:orLoginUsingStrategy') }} .pb-2.body-2.text-xs-center.grey--text.text--darken-2 {{ $t('auth:orLoginUsingStrategy') }}
v-tooltip(top, v-for='strategy in strategies', :key='strategy.key') v-tooltip(top, v-for='strategy in strategies', :key='strategy.key')
.social-login-btn.mr-2( .social-login-btn.mr-2(
......
.v-datatable { .v-data-table {
.is-clickable { .is-clickable {
cursor: pointer; cursor: pointer;
} }
......
const graphHelper = require('../../helpers/graph')
/* global WIKI */ /* global WIKI */
...@@ -28,8 +29,16 @@ module.exports = { ...@@ -28,8 +29,16 @@ module.exports = {
} }
}, },
UserMutation: { UserMutation: {
create(obj, args) { async create(obj, args) {
return WIKI.models.users.createNewUser(args) try {
await WIKI.models.users.createNewUser(args)
return {
responseResult: graphHelper.generateSuccess('User created successfully')
}
} catch (err) {
return graphHelper.generateError(err)
}
}, },
delete(obj, args) { delete(obj, args) {
return WIKI.models.users.query().deleteById(args.id) return WIKI.models.users.query().deleteById(args.id)
......
...@@ -180,6 +180,20 @@ module.exports = class User extends Model { ...@@ -180,6 +180,20 @@ module.exports = class User extends Model {
} }
primaryEmail = _.toLower(primaryEmail) primaryEmail = _.toLower(primaryEmail)
// Find pending social user
if (!user) {
user = await WIKI.models.users.query().findOne({
email: primaryEmail,
providerId: null,
providerKey
})
if (user) {
user = await user.$query().patchAndFetch({
providerId: _.toString(profile.id)
})
}
}
// Parse display name // Parse display name
let displayName = '' let displayName = ''
if (_.isString(profile.displayName) && profile.displayName.length > 0) { if (_.isString(profile.displayName) && profile.displayName.length > 0) {
...@@ -365,35 +379,60 @@ module.exports = class User extends Model { ...@@ -365,35 +379,60 @@ module.exports = class User extends Model {
email = _.toLower(email) email = _.toLower(email)
// Input validation // Input validation
const validation = validate({ let validation = null
email, if (providerKey === 'local') {
passwordRaw, validation = validate({
name email,
}, { passwordRaw,
email: { name
email: true, }, {
length: { email: {
maximum: 255 email: true,
} length: {
}, maximum: 255
passwordRaw: { }
presence: {
allowEmpty: false
}, },
length: { passwordRaw: {
minimum: 6 presence: {
allowEmpty: false
},
length: {
minimum: 6
}
},
name: {
presence: {
allowEmpty: false
},
length: {
minimum: 2,
maximum: 255
}
} }
}, }, { format: 'flat' })
name: { } else {
presence: { validation = validate({
allowEmpty: false email,
name
}, {
email: {
email: true,
length: {
maximum: 255
}
}, },
length: { name: {
minimum: 2, presence: {
maximum: 255 allowEmpty: false
},
length: {
minimum: 2,
maximum: 255
}
} }
} }, { format: 'flat' })
}, { format: 'flat' }) }
if (validation && validation.length > 0) { if (validation && validation.length > 0) {
throw new WIKI.Error.InputInvalid(validation[0]) throw new WIKI.Error.InputInvalid(validation[0])
} }
...@@ -402,19 +441,25 @@ module.exports = class User extends Model { ...@@ -402,19 +441,25 @@ module.exports = class User extends Model {
const usr = await WIKI.models.users.query().findOne({ email, providerKey }) const usr = await WIKI.models.users.query().findOne({ email, providerKey })
if (!usr) { if (!usr) {
// Create the account // Create the account
const newUsr = await WIKI.models.users.query().insert({ let newUsrData = {
provider: providerKey, providerKey,
email, email,
name, name,
password: passwordRaw,
locale: 'en', locale: 'en',
defaultEditor: 'markdown', defaultEditor: 'markdown',
tfaIsActive: false, tfaIsActive: false,
isSystem: false, isSystem: false,
isActive: true, isActive: true,
isVerified: true, isVerified: true,
mustChangePwd: (mustChangePassword === true) mustChangePwd: false
}) }
if (providerKey === `local`) {
newUsrData.password = passwordRaw
newUsrData.mustChangePwd = (mustChangePassword === true)
}
const newUsr = await WIKI.models.users.query().insert(newUsrData)
// Assign to group(s) // Assign to group(s)
if (groups.length > 0) { if (groups.length > 0) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment