Parcourir la source

feat: admin create user + markdown help fix

Nick il y a 5 ans
Parent
commit
2d2cf90514

+ 1 - 1
client/client-app.js

@@ -64,7 +64,7 @@ const graphQLLink = ApolloLink.from([
       )
       store.commit('showNotification', {
         style: 'red',
-        message: `An expected error occured.`,
+        message: `An unexpected error occured.`,
         icon: 'warning'
       })
     }

+ 6 - 6
client/components/admin.vue

@@ -26,8 +26,8 @@
             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-title {{ $t('admin:pages.title') }}
-              v-list-item-action
-                v-chip(x-small, disabled, :color='darkMode ? `grey darken-3-d4` : `grey lighten-4`')
+              v-list-item-action.pr-3
+                v-chip(x-small, :color='darkMode ? `grey darken-3-d4` : `grey lighten-5`')
                   .caption.grey--text {{ info.pagesTotal }}
             v-list-item(to='/theme', v-if='hasPermission([`manage:system`, `manage:theme`])')
               v-list-item-avatar(size='24'): v-icon mdi-palette-outline
@@ -38,14 +38,14 @@
             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-title {{ $t('admin:groups.title') }}
-              v-list-item-action
-                v-chip(x-small, disabled, :color='darkMode ? `grey darken-3-d4` : `grey lighten-4`')
+              v-list-item-action.pr-3
+                v-chip(x-small, :color='darkMode ? `grey darken-3-d4` : `grey lighten-4`')
                   .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-avatar(size='24'): v-icon mdi-account-box
               v-list-item-title {{ $t('admin:users.title') }}
-              v-list-item-action
-                v-chip(x-small, disabled, :color='darkMode ? `grey darken-3-d4` : `grey lighten-4`')
+              v-list-item-action.pr-3
+                v-chip(x-small, :color='darkMode ? `grey darken-3-d4` : `grey lighten-4`')
                   .caption.grey--text {{ info.usersTotal }}
           template(v-if='hasPermission(`manage:system`)')
             v-divider.my-2

+ 8 - 9
client/components/admin/admin-groups-edit-permissions.vue

@@ -1,27 +1,26 @@
 <template lang="pug">
-  v-card.wiki-form(flat)
+  v-card(flat)
     v-card-text
       v-text-field(
-        outline
-        background-color='grey lighten-3'
+        outlined
         v-model='group.name'
         label='Group Name'
         counter='255'
-        prepend-icon='people'
+        prepend-icon='mdi-account-group'
         )
       v-alert.radius-7(
         v-if='group.isSystem'
         color='orange darken-2'
         :class='$vuetify.theme.dark ? "grey darken-4" : "orange lighten-5"'
-        outline
+        outlined
         :value='true'
-        icon='lock_outline'
+        icon='mdi-lock-outline'
         ) This is a system group. Some permissions cannot be modified.
     v-container.px-3.pb-3.pt-0(fluid, grid-list-md)
       v-layout(row, wrap)
         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-subheader {{pmGroup.category}}
+          v-card.md2(flat, :class='$vuetify.theme.dark ? "grey darken-3-d5" : "grey lighten-5"')
+            .overline.px-5.pt-5.pb-3.grey--text.text--darken-2 {{pmGroup.category}}
             v-card-text.pt-0
               template(v-for='(pm, idx) in pmGroup.items')
                 v-checkbox.pt-0(
@@ -32,7 +31,7 @@
                   color='primary'
                   v-model='group.permissions'
                   :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'
                 )
                 v-divider.mt-3(v-if='idx < pmGroup.items.length - 1')

+ 53 - 57
client/components/admin/admin-groups-edit-rules.vue

@@ -1,66 +1,66 @@
 <template lang="pug">
-  v-card.wiki-form
+  v-card
     v-card-text(v-if='group.id === 1')
       v-alert.radius-7(
         :class='$vuetify.theme.dark ? "grey darken-4" : "orange lighten-5"'
         color='orange darken-2'
-        outline
-        :value='true'
-        icon='lock_outline'
+        outlined
+        icon='mdi-lock-outline'
         ) This group has access to everything.
     template(v-else)
-      v-card-title(:class='$vuetify.theme.dark ? `grey darken-3-d5` : `grey lighten-5`')
-        v-alert.radius-7(
-          :class='$vuetify.theme.dark ? `grey darken-3-d3` : `white`'
-          :value='true'
+      v-card-title(:class='$vuetify.theme.dark ? `grey darken-3-d5` : ``')
+        v-alert.radius-7.caption(
+          :class='$vuetify.theme.dark ? `grey darken-3-d3` : `grey lighten-4`'
           color='grey'
-          outline
-          icon='info'
+          outlined
+          icon='mdi-information'
           ) You must enable global content permissions (under Permissions tab) for page rules to have any effect.
         v-spacer
-        v-btn(depressed, color='primary', @click='addRule')
-          v-icon(left) add
+        v-btn.mx-2(depressed, color='primary', @click='addRule')
+          v-icon(left) mdi-plus
           | Add Rule
         v-menu(
           right
           offset-y
           nudge-left='115'
           )
-          v-btn.is-icon(slot='activator', flat, outline, color='primary')
-            v-icon more_horiz
+          template(v-slot:activator='{ on }')
+            v-btn.is-icon(v-on='on', outlined, color='primary')
+              v-icon mdi-dots-horizontal
           v-list(dense)
             v-list-item(@click='comingSoon')
               v-list-item-avatar
-                v-icon keyboard_capslock
+                v-icon mdi-application-import
               v-list-item-title Load Preset
             v-divider
             v-list-item(@click='comingSoon')
               v-list-item-avatar
-                v-icon publish
+                v-icon mdi-application-export
               v-list-item-title Save As Preset
             v-divider
             v-list-item(@click='comingSoon')
               v-list-item-avatar
-                v-icon cloud_upload
+                v-icon mdi-cloud-upload
               v-list-item-title Import Rules
             v-divider
             v-list-item(@click='comingSoon')
               v-list-item-avatar
-                v-icon cloud_download
+                v-icon mdi-cloud-download
               v-list-item-title Export Rules
       v-card-text(:class='$vuetify.theme.dark ? `grey darken-4-l5` : `white`')
         .rules
           .caption(v-if='group.pageRules.length === 0')
             em(:class='$vuetify.theme.dark ? `grey--text` : `blue-grey--text`') This group has no page rules yet.
           .rule(v-for='rule of group.pageRules', :key='rule.id')
-            v-btn.ma-0.rule-deny-btn(
+            v-btn.ma-0.radius-4.rule-deny-btn(
               solo
               :color='rule.deny ? "red" : "green"'
               dark
               @click='rule.deny = !rule.deny'
+              height='48'
               )
-              v-icon(v-if='rule.deny') block
-              v-icon(v-else) check_circle
+              v-icon(v-if='rule.deny') mdi-cancel
+              v-icon(v-else) mdi-check-circle
             //- Roles
             v-select.ml-1(
               solo
@@ -83,7 +83,7 @@
               template(slot='item', slot-scope='props')
                 v-list-item-action(style='min-width: 30px;')
                   v-checkbox(
-                    v-model='props.tile.props.value'
+                    v-model='props.attrs.inputValue'
                     hide-details
                     color='primary'
                   )
@@ -103,7 +103,7 @@
               dense
               )
               template(slot='selection', slot-scope='{ item, index }')
-                .body-1 {{item.text}}
+                .body-2 {{item.text}}
               template(slot='item', slot-scope='data')
                 v-list-item-avatar
                   v-avatar.white--text.radius-4(color='blue', size='30', tile) {{ data.item.icon }}
@@ -133,18 +133,18 @@
                     color='primary'
                     readonly
                   )
-                v-icon.mr-2(:color='rule.deny ? `red` : `green`') public
+                v-icon.mr-2(:color='rule.deny ? `red` : `green`') mdi-earth
                 v-list-item-content
                   v-list-item-title.body-2 Any Locale
               v-divider(slot='prepend-item')
               template(slot='item', slot-scope='props')
                 v-list-item-action(style='min-width: 30px;')
                   v-checkbox(
-                    v-model='props.tile.props.value'
+                    v-model='props.attrs.inputValue'
                     hide-details
                     color='primary'
                   )
-                v-icon.mr-2(:color='rule.deny ? `red` : `green`') language
+                v-icon.mr-2(:color='rule.deny ? `red` : `green`') mdi-web
                 v-list-item-content
                   v-list-item-title.body-2 {{props.item.text}}
                 v-chip.mr-2.grey--text(label, small, :color='$vuetify.theme.dark ? `grey darken-4` : `grey lighten-4`').caption {{props.item.value.toUpperCase()}}
@@ -161,15 +161,15 @@
               :color='$vuetify.theme.dark ? `grey` : `blue-grey`'
               )
 
-            v-btn(icon, @click='removeRule(rule.id)')
-              v-icon(:color='$vuetify.theme.dark ? `grey` : `blue-grey`') clear
+            v-btn.ml-2.mt-1(icon, @click='removeRule(rule.id)', small)
+              v-icon(:color='$vuetify.theme.dark ? `grey` : `blue-grey`') mdi-close
 
         v-divider.mt-3
-        v-subheader.pl-0 Rules Order
-        .body-1.pl-3 Rules are applied in order of path specificity. A more precise path will always override a less defined path.
-        .body-1.pl-4 For example, #[span.teal--text /geography/countries] will override #[span.teal--text /geography].
-        .body-1.pl-3.pt-2 When 2 rules have the same specificity, the priority is given from lowest to highest as follows:
-        .body-1.pl-3.pt-1
+        .overline.py-3 Rules Order
+        .body-2.pl-3 Rules are applied in order of path specificity. A more precise path will always override a less defined path.
+        .body-2.pl-5 For example, #[span.teal--text /geography/countries] will override #[span.teal--text /geography].
+        .body-2.pl-3.pt-2 When 2 rules have the same specificity, the priority is given from lowest to highest as follows:
+        .body-2.pl-3.pt-1
           ul
             li
               strong Path Starts With...
@@ -181,9 +181,9 @@
             li
               strong Path Is Exactly...
               em.caption.pl-1 (highest)
-        .body-1.pl-3.pt-2 When 2 rules have the same path specificity AND the same match type, #[strong.red--text DENY] will always override an #[strong.green--text ALLOW] rule.
+        .body-2.pl-3.pt-2 When 2 rules have the same path specificity AND the same match type, #[strong.red--text DENY] will always override an #[strong.green--text ALLOW] rule.
         v-divider.mt-3
-        v-subheader.pl-0 Regular Expressions
+        .overline.py-3 Regular Expressions
         span Expressions that are deemed unsafe or could result in exponential time processing will be rejected upon saving.
 
 </template>
@@ -202,18 +202,18 @@ export default {
   data() {
     return {
       roles: [
-        { text: 'Read Pages', value: 'read:pages', icon: 'insert_drive_file' },
-        { text: 'Create Pages', value: 'write:pages', icon: 'insert_drive_file' },
-        { text: 'Edit + Move Pages', value: 'manage:pages', icon: 'insert_drive_file' },
-        { text: 'Delete Pages', value: 'delete:pages', icon: 'insert_drive_file' },
-        { text: 'View Pages Source', value: 'read:source', icon: 'code' },
-        { text: 'View Pages History', value: 'read:history', icon: 'restore' },
-        { text: 'Read / Use Assets', value: 'read:assets', icon: 'camera' },
-        { text: 'Upload Assets', value: 'write:assets', icon: 'camera' },
-        { text: 'Edit + Delete Assets', value: 'manage:assets', icon: 'camera' },
-        { text: 'Read Comments', value: 'read:comments', icon: 'insert_comment' },
-        { text: 'Create Comments', value: 'write:comments', icon: 'insert_comment' },
-        { text: 'Edit + Delete Comments', value: 'manage:comments', icon: 'insert_comment' }
+        { text: 'Read Pages', value: 'read:pages', icon: 'mdi-file-document-box-search-outline' },
+        { text: 'Create Pages', value: 'write:pages', icon: 'mdi-file-document-box-plus-outline' },
+        { text: 'Edit + Move Pages', value: 'manage:pages', icon: 'mdi-file-document-edit-outline' },
+        { text: 'Delete Pages', value: 'delete:pages', icon: 'mdi-file-document-box-remove-outline' },
+        { text: 'View Pages Source', value: 'read:source', icon: 'mdi-code-tags' },
+        { text: 'View Pages History', value: 'read:history', icon: 'mdi-history' },
+        { text: 'Read / Use Assets', value: 'read:assets', icon: 'mdi-image-search-outline' },
+        { text: 'Upload Assets', value: 'write:assets', icon: 'mdi-image-plus' },
+        { text: 'Edit + Delete Assets', value: 'manage:assets', icon: 'mdi-image-size-select-large' },
+        { text: 'Read Comments', value: 'read:comments', icon: 'mdi-comment-search-outline' },
+        { text: 'Create Comments', value: 'write:comments', icon: 'mdi-comment-plus-outline' },
+        { text: 'Edit + Delete Comments', value: 'manage:comments', icon: 'mdi-comment-remove-outline' }
       ],
       matches: [
         { text: 'Path Starts With...', value: 'START', icon: '/...' },
@@ -252,6 +252,9 @@ export default {
         message: `Coming soon...`,
         icon: 'directions_boat'
       })
+    },
+    dude (stuff) {
+      console.info(stuff)
     }
   }
 }
@@ -264,7 +267,7 @@ export default {
   padding: 1rem;
   position: relative;
 
-  @at-root .theme--dark & {
+  @at-root .v-application.theme--dark & {
     background-color: mc('grey', '800');
   }
 }
@@ -282,7 +285,7 @@ export default {
     opacity: 0;
   }
 
-  @at-root .theme--dark & {
+  @at-root .v-application.theme--dark & {
     background-color: mc('grey', '700');
   }
 
@@ -306,7 +309,7 @@ export default {
       left: -2rem;
       top: -1.3rem;
 
-      @at-root .theme--dark & {
+      @at-root .v-application.theme--dark & {
         background-color: mc('grey', '800');
         color: mc('grey', '600');
       }
@@ -316,12 +319,5 @@ export default {
   .input-group + * {
     margin-left: .5rem;
   }
-
-  &-deny-btn {
-    height: 48px;
-    border-radius: 2px 0 0 2px;
-    min-width: 0;
-  }
-
 }
 </style>

+ 22 - 18
client/components/admin/admin-groups-edit-users.vue

@@ -1,49 +1,52 @@
 <template lang="pug">
-  v-card.wiki-form
-    v-card-title(:class='$vuetify.theme.dark ? `grey darken-3-d3` : `grey lighten-5`')
+  v-card
+    v-card-title.pb-4(:class='$vuetify.theme.dark ? `grey darken-3-d3` : `grey lighten-5`')
       v-text-field(
-        outline
+        outlined
         flat
-        prepend-inner-icon='search'
+        prepend-inner-icon='mdi-magnify'
         v-model='search'
         label='Search Group Users...'
         hide-details
       )
       v-spacer
       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
     v-data-table(
       :items='group.users',
       :headers='headers',
       :search='search'
-      :pagination.sync='pagination',
-      :rows-per-page-items='[15]'
-      hide-actions
+      :page.sync='pagination'
+      :items-per-page='15'
+      @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')
           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
+              template(v-slot:activator='{ on }')
+                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-action: v-icon(color='primary') person
+                  v-list-item-action: v-icon(color='primary') mdi-account-outline
                   v-list-item-content
                     v-list-item-title View User Profile
                 template(v-if='props.item.id !== 2')
-                  v-divider
                   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-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='group.users.length > 15')
-      v-pagination(v-model='pagination.page', :length='pages')
+        v-alert.ma-3(icon='warning', outlined) No users to display.
+    .text-center.py-2(v-if='group.users.length > 15')
+      v-pagination(v-model='pagination', :length='pageCount')
 
     user-search(v-model='searchUserDialog', @select='assignUser')
 </template>
@@ -73,7 +76,8 @@ export default {
         { text: '', value: 'actions', sortable: false, width: 50 }
       ],
       searchUserDialog: false,
-      pagination: {},
+      pagination: 1,
+      pageCount: 0,
       search: ''
     }
   },

+ 9 - 8
client/components/admin/admin-groups-edit.vue

@@ -10,11 +10,12 @@
           v-spacer
           .caption.grey--text ID #[strong {{group.id}}]
           v-divider.mx-3(vertical)
-          v-btn(color='grey', large, outline, to='/groups')
-            v-icon arrow_back
+          v-btn(color='grey', large, outlined, to='/groups')
+            v-icon mdi-arrow-left
           v-dialog(v-model='deleteGroupDialog', max-width='500', v-if='!group.isSystem')
-            v-btn(color='red', large, outline, slot='activator')
-              v-icon(color='red') delete
+            template(v-slot:activator='{ on }')
+              v-btn(color='red', large, outlined, v-on='{ on }')
+                v-icon(color='red') mdi-trash-can-outline
             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.
@@ -22,11 +23,11 @@
                 v-spacer
                 v-btn(flat, @click='deleteGroupDialog = false') Cancel
                 v-btn(color='red', dark, @click='deleteGroup') Delete
-          v-btn(color='success', large, depressed, @click='updateGroup')
-            v-icon(left) check
+          v-btn.ml-2(color='success', large, depressed, @click='updateGroup')
+            v-icon(left) mdi-check
             span Update Group
         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='rules') Page Rules
             v-tab(key='users') Users
@@ -69,7 +70,7 @@ export default {
         users: []
       },
       deleteGroupDialog: false,
-      tab: '1'
+      tab: null
     }
   },
   methods: {

+ 17 - 19
client/components/admin/admin-groups.vue

@@ -37,25 +37,29 @@
             :items='groups'
             :headers='headers'
             :search='search'
-            :pagination.sync='pagination'
-            :rows-per-page-items='[15]'
-            hide-actions
+            :page.sync='pagination'
+            :items-per-page='15'
+            :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)')
-                td.text-xs-right {{ props.item.id }}
+                td {{ props.item.id }}
                 td: strong {{ props.item.name }}
                 td {{ props.item.userCount }}
                 td {{ props.item.createdAt | moment('calendar') }}
                 td {{ props.item.updatedAt | moment('calendar') }}
                 td
                   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
             template(slot='no-data')
               v-alert.ma-3(icon='warning', :value='true', outline) No groups to display.
-          .text-xs-center.py-2(v-if='this.pages > 0')
-            v-pagination(v-model='pagination.page', :length='pages')
+          .text-xs-center.py-2(v-if='pageCount > 1')
+            v-pagination(v-model='pagination', :length='pageCount')
 </template>
 
 <script>
@@ -70,7 +74,8 @@ export default {
       newGroupDialog: false,
       newGroupName: '',
       selectedGroup: {},
-      pagination: {},
+      pagination: 1,
+      pageCount: 0,
       groups: [],
       headers: [
         { text: 'ID', value: 'id', width: 50, align: 'right' },
@@ -80,16 +85,8 @@ export default {
         { text: 'Last Updated', value: 'updatedAt', width: 250 },
         { text: '', value: 'isSystem', width: 20, sortable: false }
       ],
-      search: ''
-    }
-  },
-  computed: {
-    pages () {
-      if (this.pagination.rowsPerPage == null || this.pagination.totalItems == null) {
-        return 0
-      }
-
-      return Math.ceil(this.pagination.totalItems / this.pagination.rowsPerPage)
+      search: '',
+      loading: false
     }
   },
   watch: {
@@ -158,6 +155,7 @@ export default {
       fetchPolicy: 'network-only',
       update: (data) => data.groups.list,
       watchLoading (isLoading) {
+        this.loading = isLoading
         this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-groups-refresh')
       }
     }

+ 4 - 4
client/components/admin/admin-storage.vue

@@ -46,7 +46,7 @@
               v-list-item(:key='tgt.key')
                 template(v-if='tgt.status === `pending`')
                   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-title.body-2 {{tgt.title}}
                     v-list-item-sub-title.purple--text.caption {{tgt.status}}
@@ -54,20 +54,20 @@
                     v-progress-circular(indeterminate, :size='20', :width='2', color='purple')
                 template(v-else-if='tgt.status === `operational`')
                   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-title.body-2 {{tgt.title}}
                     v-list-item-sub-title.green--text.caption {{$t('admin:storage.lastSync', { time: $options.filters.moment(tgt.lastAttempt, 'from') })}}
                 template(v-else)
                   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-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-action
                     v-menu
                       v-btn(slot='activator', icon)
-                        v-icon(color='red') info
+                        v-icon(color='red') mdi-information
                       v-card(width='450')
                         v-toolbar(flat, color='red', dark, dense) {{$t('admin:storage.errorMsg')}}
                         v-card-text {{tgt.message}}

+ 13 - 7
client/components/admin/admin-users-create.vue

@@ -1,6 +1,6 @@
 <template lang="pug">
   v-dialog(v-model='isShown', max-width='650', persistent)
-    v-card.wiki-form
+    v-card
       .dialog-header.is-short
         v-icon.mr-3(color='white') mdi-plus
         span New User
@@ -59,13 +59,14 @@
           data-vv-as='Name',
           data-vv-scope='newUser',
           :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'
           persistent-hint
           )
-        v-select(
+        v-select.mt-2(
           :items='groups'
           item-text='name'
-          item-value='key'
+          item-value='id'
           outlined
           prepend-icon='mdi-account-group'
           v-model='group'
@@ -90,8 +91,12 @@
       v-card-chin
         v-spacer
         v-btn(text, @click='isShown = false') Cancel
-        v-btn(depressed, color='primary', @click='newUser(false)', :disabled='errors.any(`newUser`)') Create
-        v-btn(depressed, color='primary', @click='newUser(true)', :disabled='errors.any(`newUser`)') Create and Close
+        v-btn.px-3(depressed, color='primary', @click='newUser(false)', :disabled='errors.any(`newUser`)')
+          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>
 
 <script>
@@ -116,7 +121,7 @@ export default {
       password: '',
       name: '',
       groups: [],
-      group: '',
+      group: [],
       mustChangePwd: false,
       sendWelcomeEmail: false
     }
@@ -155,7 +160,7 @@ export default {
             email: this.email,
             passwordRaw: this.password,
             name: this.name,
-            groups: this.groups,
+            groups: this.group,
             mustChangePassword: this.mustChangePwd,
             sendWelcomeEmail: this.sendWelcomeEmail
           },
@@ -176,6 +181,7 @@ export default {
 
           if (close) {
             this.isShown = false
+            this.$emit('refresh')
           } else {
             this.$refs.emailInput.focus()
           }

+ 78 - 66
client/components/admin/admin-users-edit.vue

@@ -9,150 +9,162 @@
             .subtitle-1.grey--text.animated.fadeInLeft.wait-p2s {{user.name}}
           v-spacer
           .caption.grey--text.animated.fadeInRight.wait-p5s ID #[strong {{user.id}}]
-          v-divider.animated.fadeInRight.wait-p3s.mx-3(vertical)
-          v-btn.animated.fadeInDown.wait-p2s(color='grey', large, outline, to='/users')
-            v-icon arrow_back
+          v-divider.animated.fadeInRight.wait-p3s.ml-3(vertical)
+          v-btn.ml-3.animated.fadeInDown.wait-p2s(color='grey', large, outlined, to='/users')
+            v-icon mdi-arrow-left
           v-dialog(v-model='deleteUserDialog', max-width='500', v-if='user.id !== currentUserId && !user.isSystem')
-            v-btn.animated.fadeInDown.wait-p1s(color='red', large, outline, slot='activator')
-              v-icon(color='red') delete
+            template(v-slot:activator='{ on }')
+              v-btn.ml-3.animated.fadeInDown.wait-p1s(color='red', large, outlined, v-on='on')
+                v-icon(color='red') mdi-trash-can-outline
             v-card
               .dialog-header.is-red Delete User?
               v-card-text Are you sure you want to delete user #[strong {{ user.name }}]?
               v-card-actions
                 v-spacer
-                v-btn(flat, @click='deleteUserDialog = false') Cancel
+                v-btn(text, @click='deleteUserDialog = false') Cancel
                 v-btn(color='red', dark, @click='deleteUser') Delete
-          v-btn.animated.fadeInDown(color='primary', large, depressed, @click='updateUser')
-            v-icon(left) check
+          v-btn.ml-3.animated.fadeInDown(color='primary', large, depressed, @click='updateUser')
+            v-icon(left) mdi-check
             span Update User
       v-flex(xs5)
         v-card.animated.fadeInUp
           v-toolbar(color='primary', dense, dark, flat)
-            v-icon.mr-2 directions_run
+            v-icon.mr-2 mdi-information-variant
             span Basic Info
           v-list.py-0(two-line, dense)
             v-list-item
-              v-list-item-avatar
-                v-icon alternate_email
+              v-list-item-avatar(size='32')
+                v-icon mdi-email-variant
               v-list-item-content
                 v-list-item-title Email
-                v-list-item-sub-title {{ user.email }}
+                v-list-item-subtitle {{ user.email }}
               v-list-item-action(v-if='!user.isSystem')
-                  v-btn(icon, color='grey', flat)
-                    v-icon edit
+                  v-btn(icon, color='grey', flat, x-small)
+                    v-icon mdi-pencil
             v-divider
             v-list-item
-              v-list-item-avatar
-                v-icon person
+              v-list-item-avatar(size='32')
+                v-icon mdi-account
               v-list-item-content
                 v-list-item-title Display Name
-                v-list-item-sub-title {{ user.name }}
+                v-list-item-subtitle {{ user.name }}
               v-list-item-action
-                  v-btn(icon, color='grey', flat)
-                    v-icon edit
+                  v-btn(icon, color='grey', flat, x-small)
+                    v-icon mdi-pencil
         v-card.mt-3.animated.fadeInUp.wait-p2s(v-if='!user.isSystem')
           v-toolbar(color='primary', dense, dark, flat)
-            v-icon.mr-2 lock_outline
+            v-icon.mr-2 mdi-lock-outline
             span Authentication
           v-list.py-0(two-line, dense)
             v-list-item
-              v-list-item-avatar
-                v-icon business
+              v-list-item-avatar(size='32')
+                v-icon mdi-domain
               v-list-item-content
                 v-list-item-title Provider
-                v-list-item-sub-title {{ user.providerKey }}
-              v-list-item-action
-                v-img(src='https://static.requarks.io/logo/wikijs.svg', alt='')
+                v-list-item-subtitle {{ user.providerKey }}
+              //- v-list-item-action
+              //-   v-img(src='https://static.requarks.io/logo/wikijs.svg', alt='', contain, max-height='32', position='center right')
             template(v-if='user.providerKey === `local`')
               v-divider
               v-list-item
-                v-list-item-avatar
-                  v-icon security
+                v-list-item-avatar(size='32')
+                  v-icon mdi-textbox-password
                 v-list-item-content
                   v-list-item-title Password
-                  v-list-item-sub-title ********
+                  v-list-item-subtitle &bull;&bull;&bull;&bull;&bull;&bull;&bull;&bull;
                 v-list-item-action
-                    v-btn(icon, color='grey', flat)
-                      v-icon cached
+                  v-tooltip(top)
+                    template(v-slot:activator='{ on }')
+                      v-btn(icon, color='grey', flat, x-small, v-on='on')
+                        v-icon mdi-cached
+                    span Change Password
                 v-list-item-action
-                    v-btn(icon, color='grey', flat)
-                      v-icon email
+                  v-tooltip(top)
+                    template(v-slot:activator='{ on }')
+                      v-btn(icon, color='grey', flat, x-small, v-on='on')
+                        v-icon mdi-email
+                    span Send Password Reset Email
               v-divider
               v-list-item
-                v-list-item-avatar
-                  v-icon screen_lock_portrait
+                v-list-item-avatar(size='32')
+                  v-icon mdi-two-factor-authentication
                 v-list-item-content
                   v-list-item-title Two Factor Authentication (2FA)
-                  v-list-item-sub-title.red--text Inactive
+                  v-list-item-subtitle.red--text Inactive
                 v-list-item-action
-                    v-btn(icon, color='grey', flat)
-                      v-icon power_settings_new
+                  v-tooltip(top)
+                    template(v-slot:activator='{ on }')
+                      v-btn(icon, color='grey', flat, x-small, v-on='on')
+                        v-icon mdi-power
+                    span Toggle 2FA
               template(v-if='user.providerId')
                 v-divider
                 v-list-item
-                  v-list-item-avatar
-                    v-icon person
+                  v-list-item-avatar(size='32')
+                    v-icon mdi-account
                   v-list-item-content
                     v-list-item-title Provider Id
-                    v-list-item-sub-title {{ user.providerId }}
+                    v-list-item-subtitle {{ user.providerId }}
         v-card.mt-3.animated.fadeInUp.wait-p4s
           v-toolbar(color='primary', dense, dark, flat)
-            v-icon.mr-2 people
+            v-icon.mr-2 mdi-account-group
             span User Groups
           v-list(dense)
             template(v-for='(group, idx) in user.groups')
               v-list-item
-                v-list-item-avatar
-                  v-icon people_outline
+                v-list-item-avatar(size='32')
+                  v-icon mdi-account-group-outline
                 v-list-item-content
                   v-list-item-title {{group.name}}
-                v-list-item-action
-                  v-btn(icon, color='red', flat)
-                    v-icon clear
+                v-list-item-action(v-if='!user.isSystem')
+                  v-btn(icon, color='red', x-small)
+                    v-icon mdi-close
               v-divider(v-if='idx < user.groups.length - 1')
-          v-card-chin
+          v-alert.mx-3(v-if='user.groups.length < 1', outlined, color='grey darken-1', icon='mdi-alert')
+            .caption This user is not assigned to any group yet. You must assign at least 1 group to a user.
+          v-card-chin(v-if='!user.isSystem')
             v-spacer
-            v-btn(small, color='primary', flat)
-              v-icon(left) how_to_reg
+            v-btn(color='primary', text)
+              v-icon(left) mdi-clipboard-account
               span Assign to group
       v-flex(xs7)
         v-card.animated.fadeInUp.wait-p2s
           v-toolbar(color='primary', dense, dark, flat)
-            v-icon.mr-2 directions_walk
+            v-icon.mr-2 mdi-account-badge-outline
             span Extended Metadata
           v-list.py-0(two-line, dense)
             v-list-item
-              v-list-item-avatar
-                v-icon public
+              v-list-item-avatar(size='32')
+                v-icon mdi-map-marker
               v-list-item-content
                 v-list-item-title Location
-                v-list-item-sub-title {{ user.location }}
+                v-list-item-subtitle {{ user.location }}
               v-list-item-action
-                  v-btn(icon, color='grey', flat)
-                    v-icon edit
+                  v-btn(icon, color='grey', flat, x-small)
+                    v-icon mdi-pencil
             v-divider
             v-list-item
-              v-list-item-avatar
-                v-icon local_library
+              v-list-item-avatar(size='32')
+                v-icon mdi-account-badge-horizontal-outline
               v-list-item-content
                 v-list-item-title Job Title
-                v-list-item-sub-title {{ user.jobTitle }}
+                v-list-item-subtitle {{ user.jobTitle }}
               v-list-item-action
-                  v-btn(icon, color='grey', flat)
-                    v-icon edit
+                  v-btn(icon, color='grey', flat, x-small)
+                    v-icon mdi-pencil
             v-divider
             v-list-item
-              v-list-item-avatar
-                v-icon map
+              v-list-item-avatar(size='32')
+                v-icon mdi-map-clock-outline
               v-list-item-content
                 v-list-item-title Timezone
-                v-list-item-sub-title {{ user.timezone }}
+                v-list-item-subtitle {{ user.timezone }}
               v-list-item-action
-                  v-btn(icon, color='grey', flat)
-                    v-icon edit
+                  v-btn(icon, color='grey', flat, x-small)
+                    v-icon mdi-pencil
         v-card.mt-3.animated.fadeInUp.wait-p4s
           v-toolbar(color='primary', dense, dark, flat)
-            v-icon.mr-2 insert_drive_file
+            v-icon.mr-2 mdi-file-document-box-multiple-outline
             span Content
           v-card-text
             em.caption.grey--text Coming soon

+ 23 - 51
client/components/admin/admin-users.vue

@@ -39,45 +39,36 @@
             :items='usersFiltered',
             :headers='headers',
             :search='search',
-            :pagination.sync='pagination',
-            :rows-per-page-items='[15]'
+            :page.sync='pagination'
+            :items-per-page='15'
             :loading='loading'
-            hide-actions,
-            disable-initial-sort
-          )
-            template(slot='headers', 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')
+            @page-count='pageCount = $event'
+            hide-default-footer
+            )
+            template(slot='item', slot-scope='props')
               tr.is-clickable(:active='props.selected', @click='$router.push("/users/" + props.item.id)')
                 //- 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.id }}
                 td: strong {{ props.item.name }}
                 td {{ props.item.email }}
                 td {{ props.item.providerKey }}
                 td {{ props.item.createdAt | moment('from') }}
                 td
                   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
             template(slot='no-data')
               .pa-3
-                v-alert(icon='warning', :value='true', outline) No users to display!
-          v-card-chin(v-if='this.pages > 1')
+                v-alert.text-left(icon='mdi-alert', outlined, color='grey')
+                  em.body-2 No users to display!
+          v-card-chin(v-if='pageCount > 1')
             v-spacer
-            v-pagination(v-model='pagination.page', :length='pages')
+            v-pagination(v-model='pagination', :length='pageCount')
             v-spacer
 
-    user-create(v-model='isCreateDialogShown')
+    user-create(v-model='isCreateDialogShown', @refresh='refresh(false)')
 </template>
 
 <script>
@@ -95,7 +86,8 @@ export default {
   data() {
     return {
       selected: [],
-      pagination: {},
+      pagination: 1,
+      pageCount: 0,
       users: [],
       headers: [
         { text: 'ID', value: 'id', width: 80, sortable: true },
@@ -116,40 +108,20 @@ export default {
     usersFiltered () {
       const all = this.filterStrategy === 'all' || 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: {
     createUser() {
       this.isCreateDialogShown = true
     },
-    async refresh() {
+    async refresh(notify = true) {
       await this.$apollo.queries.users.refetch()
-      this.$store.commit('showNotification', {
-        message: 'Users list has been refreshed.',
-        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()
+      if (notify) {
+        this.$store.commit('showNotification', {
+          message: 'Users list has been refreshed.',
+          style: 'success',
+          icon: 'cached'
+        })
       }
     }
   },

+ 5 - 5
client/components/common/user-search.vue

@@ -16,10 +16,10 @@
           )
       v-card-text
         v-text-field(
-          outline
+          outlined
           :label='$t(`common:user.searchPlaceholder`)'
           v-model='search'
-          prepend-inner-icon='search'
+          prepend-inner-icon='mdi-account-search-outline'
           color='primary'
           ref='searchIpt'
           hide-details
@@ -35,14 +35,14 @@
                 span.body-1.white--text {{usr.name | initials}}
               v-list-item-content
                 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-icon(color='primary') arrow_forward
+                v-icon(color='primary') mdi-arrow-right
             v-divider.my-0(v-if='idx < items.length - 1')
       v-card-chin
         v-spacer
         v-btn(
-          flat
+          text
           @click='close'
           :disabled='loading'
           ) {{$t('common:actions.cancel')}}

+ 38 - 41
client/components/editor/markdown/help.vue

@@ -7,7 +7,7 @@
             v-card-text
               .d-flex
                 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.mt-3 Bold
               v-layout(row)
@@ -15,8 +15,8 @@
                   v-card.editor-markdown-help-source(flat)
                     v-card-text
                       div **Lorem ipsum**
-                v-icon chevron_right
-                v-flex(xs6)
+                v-icon mdi-chevron-right
+                v-flex
                   v-card.editor-markdown-help-result(flat)
                     v-card-text
                       .caption: strong Lorem ipsum
@@ -26,8 +26,8 @@
                   v-card.editor-markdown-help-source(flat)
                     v-card-text
                       div *Lorem ipsum*
-                v-icon chevron_right
-                v-flex(xs6)
+                v-icon mdi-chevron-right
+                v-flex
                   v-card.editor-markdown-help-result(flat)
                     v-card-text
                       .caption: em Lorem ipsum
@@ -37,8 +37,8 @@
                   v-card.editor-markdown-help-source(flat)
                     v-card-text
                       div ~~Lorem ipsum~~
-                v-icon chevron_right
-                v-flex(xs6)
+                v-icon mdi-chevron-right
+                v-flex
                   v-card.editor-markdown-help-result(flat)
                     v-card-text
                       .caption(style='text-decoration: line-through;') Lorem ipsum
@@ -53,8 +53,8 @@
                       div #### Header 4
                       div ##### Header 5
                       div ###### Header 6
-                v-icon chevron_right
-                v-flex(xs6)
+                v-icon mdi-chevron-right
+                v-flex
                   v-card.editor-markdown-help-result(flat)
                     v-card-text
                       div(style='font-weight: 700; font-size: 24px;') Header 1
@@ -72,8 +72,8 @@
                       div - Unordered List Item 1
                       div - Unordered List Item 2
                       div - Unordered List Item 3
-                v-icon chevron_right
-                v-flex(xs6)
+                v-icon mdi-chevron-right
+                v-flex
                   v-card.editor-markdown-help-result(flat)
                     v-card-text
                       ul
@@ -89,8 +89,8 @@
                       div 1. Ordered List Item 1
                       div 1. Ordered List Item 2
                       div 1. Ordered List Item 3
-                v-icon chevron_right
-                v-flex(xs6)
+                v-icon mdi-chevron-right
+                v-flex
                   v-card.editor-markdown-help-result(flat)
                     v-card-text
                       ol
@@ -103,8 +103,8 @@
                   v-card.editor-markdown-help-source(flat)
                     v-card-text
                       div ![Caption Text](/path/to/image.jpg)
-                v-icon chevron_right
-                v-flex(xs6)
+                v-icon mdi-chevron-right
+                v-flex
                   v-card.editor-markdown-help-result(flat)
                     v-card-text
                       img(src='https://via.placeholder.com/150x50.png')
@@ -113,7 +113,7 @@
             v-card-text
               .d-flex
                 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.mt-3 Links
               v-layout(row)
@@ -121,8 +121,8 @@
                   v-card.editor-markdown-help-source(flat)
                     v-card-text
                       div [Link Text](https://wiki.js.org)
-                v-icon chevron_right
-                v-flex(xs6)
+                v-icon mdi-chevron-right
+                v-flex
                   v-card.editor-markdown-help-result(flat)
                     v-card-text
                       .caption: a(href='https://wiki.js.org', target='_blank') Link Text
@@ -132,8 +132,8 @@
                   v-card.editor-markdown-help-source(flat)
                     v-card-text
                       div Lorem ^ipsum^
-                v-icon chevron_right
-                v-flex(xs6)
+                v-icon mdi-chevron-right
+                v-flex
                   v-card.editor-markdown-help-result(flat)
                     v-card-text
                       .caption Lorem #[sup ipsum]
@@ -143,8 +143,8 @@
                   v-card.editor-markdown-help-source(flat)
                     v-card-text
                       div Lorem ~ipsum~
-                v-icon chevron_right
-                v-flex(xs6)
+                v-icon mdi-chevron-right
+                v-flex
                   v-card.editor-markdown-help-result(flat)
                     v-card-text
                       .caption: em Lorem #[sub ipsum]
@@ -156,8 +156,8 @@
                       div Lorem ipsum
                       div ---
                       div Dolor sit amet
-                v-icon chevron_right
-                v-flex(xs6)
+                v-icon mdi-chevron-right
+                v-flex
                   v-card.editor-markdown-help-result(flat)
                     v-card-text
                       .caption Lorem ipsum
@@ -169,8 +169,8 @@
                   v-card.editor-markdown-help-source(flat)
                     v-card-text
                       div Lorem `ipsum dolor sit` amet
-                v-icon chevron_right
-                v-flex(xs6)
+                v-icon mdi-chevron-right
+                v-flex
                   v-card.editor-markdown-help-result(flat)
                     v-card-text
                       .caption Lorem #[code ipsum dolor sit] amet
@@ -185,8 +185,8 @@
                       div.pl-3 echo 'Lorem ipsum'
                       div }
                       div ```
-                v-icon chevron_right
-                v-flex(xs6)
+                v-icon mdi-chevron-right
+                v-flex
                   v-card.editor-markdown-help-result(flat)
                     v-card-text.contents
                       pre.prismjs.line-numbers.language-js
@@ -211,8 +211,8 @@
                       div &gt; Lorem ipsum
                       div &gt; dolor sit amet
                       div &gt; consectetur adipiscing elit
-                v-icon chevron_right
-                v-flex(xs6)
+                v-icon mdi-chevron-right
+                v-flex
                   v-card.editor-markdown-help-result(flat)
                     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
@@ -221,7 +221,7 @@
           v-card.radius-7.animated.fadeInUp.wait-p2s(light)
             v-card-text
               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
               v-list.editor-markdown-help-kbd(two-line, dense)
                 v-list-item
@@ -255,13 +255,13 @@
                 v-list-item
                   v-list-item-content
                     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-card.radius-7.animated.fadeInUp.wait-p3s.mt-3(light)
             v-card-text
               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
               v-list.editor-markdown-help-kbd(two-line, dense)
                 v-list-item
@@ -303,6 +303,10 @@ export default {
     font-family: 'Roboto Mono', monospace;
     font-size: 14px;
     color: #FFF !important;
+
+    .v-card__text {
+      color: #FFF !important;
+    }
   }
 
   &-result {
@@ -327,7 +331,7 @@ export default {
   }
 
   &-kbd {
-    .v-list__tile__action {
+    .v-list-item__action {
       flex-direction: row;
       align-items: center;
 
@@ -345,12 +349,5 @@ export default {
       }
     }
   }
-
-  &-ref {
-    .v-list__tile__action {
-      flex-direction: row;
-      align-items: center;
-    }
-  }
 }
 </style>

+ 1 - 1
client/components/login.vue

@@ -109,7 +109,7 @@
                   v-spacer
                 template(v-if='screen === "login" && isSocialShown')
                   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') }}
                     v-tooltip(top, v-for='strategy in strategies', :key='strategy.key')
                       .social-login-btn.mr-2(

+ 1 - 1
client/scss/components/v-data-table.scss

@@ -1,4 +1,4 @@
-.v-datatable {
+.v-data-table {
   .is-clickable {
     cursor: pointer;
   }

+ 11 - 2
server/graph/resolvers/user.js

@@ -1,3 +1,4 @@
+const graphHelper = require('../../helpers/graph')
 
 /* global WIKI */
 
@@ -28,8 +29,16 @@ module.exports = {
     }
   },
   UserMutation: {
-    create(obj, args) {
-      return WIKI.models.users.createNewUser(args)
+    async create(obj, args) {
+      try {
+        await WIKI.models.users.createNewUser(args)
+
+        return {
+          responseResult: graphHelper.generateSuccess('User created successfully')
+        }
+      } catch (err) {
+        return graphHelper.generateError(err)
+      }
     },
     delete(obj, args) {
       return WIKI.models.users.query().deleteById(args.id)

+ 75 - 30
server/models/users.js

@@ -180,6 +180,20 @@ module.exports = class User extends Model {
     }
     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
     let displayName = ''
     if (_.isString(profile.displayName) && profile.displayName.length > 0) {
@@ -365,35 +379,60 @@ module.exports = class User extends Model {
     email = _.toLower(email)
 
     // Input validation
-    const validation = validate({
-      email,
-      passwordRaw,
-      name
-    }, {
-      email: {
-        email: true,
-        length: {
-          maximum: 255
-        }
-      },
-      passwordRaw: {
-        presence: {
-          allowEmpty: false
+    let validation = null
+    if (providerKey === 'local') {
+      validation = validate({
+        email,
+        passwordRaw,
+        name
+      }, {
+        email: {
+          email: true,
+          length: {
+            maximum: 255
+          }
         },
-        length: {
-          minimum: 6
+        passwordRaw: {
+          presence: {
+            allowEmpty: false
+          },
+          length: {
+            minimum: 6
+          }
+        },
+        name: {
+          presence: {
+            allowEmpty: false
+          },
+          length: {
+            minimum: 2,
+            maximum: 255
+          }
         }
-      },
-      name: {
-        presence: {
-          allowEmpty: false
+      }, { format: 'flat' })
+    } else {
+      validation = validate({
+        email,
+        name
+      }, {
+        email: {
+          email: true,
+          length: {
+            maximum: 255
+          }
         },
-        length: {
-          minimum: 2,
-          maximum: 255
+        name: {
+          presence: {
+            allowEmpty: false
+          },
+          length: {
+            minimum: 2,
+            maximum: 255
+          }
         }
-      }
-    }, { format: 'flat' })
+      }, { format: 'flat' })
+    }
+
     if (validation && validation.length > 0) {
       throw new WIKI.Error.InputInvalid(validation[0])
     }
@@ -402,19 +441,25 @@ module.exports = class User extends Model {
     const usr = await WIKI.models.users.query().findOne({ email, providerKey })
     if (!usr) {
       // Create the account
-      const newUsr = await WIKI.models.users.query().insert({
-        provider: providerKey,
+      let newUsrData = {
+        providerKey,
         email,
         name,
-        password: passwordRaw,
         locale: 'en',
         defaultEditor: 'markdown',
         tfaIsActive: false,
         isSystem: false,
         isActive: 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)
       if (groups.length > 0) {