diff --git a/server/app/data.yml b/server/app/data.yml
index c0dc58f5..101e4e48 100644
--- a/server/app/data.yml
+++ b/server/app/data.yml
@@ -131,9 +131,9 @@ groups:
- 'read:assets'
- 'read:comments'
- 'write:comments'
- defaultPageRules:
- - id: default
- deny: false
+ defaultRules:
+ - name: Default Rule
+ mode: ALLOW
match: START
roles:
- 'read:pages'
@@ -142,6 +142,7 @@ groups:
- 'write:comments'
path: ''
locales: []
+ sites: []
reservedPaths:
- login
- logout
diff --git a/server/graph/resolvers/group.js b/server/graph/resolvers/group.js
index e03014e3..25c7fe6d 100644
--- a/server/graph/resolvers/group.js
+++ b/server/graph/resolvers/group.js
@@ -1,7 +1,7 @@
const graphHelper = require('../../helpers/graph')
const safeRegex = require('safe-regex')
const _ = require('lodash')
-const gql = require('graphql')
+const { v4: uuid } = require('uuid')
/* global WIKI */
@@ -30,13 +30,13 @@ module.exports = {
async assignUserToGroup (obj, args, { req }) {
// Check for guest user
if (args.userId === 2) {
- throw new gql.GraphQLError('Cannot assign the Guest user to a group.')
+ throw new Error('Cannot assign the Guest user to a group.')
}
// Check for valid group
const grp = await WIKI.models.groups.query().findById(args.groupId)
if (!grp) {
- throw new gql.GraphQLError('Invalid Group ID')
+ throw new Error('Invalid Group ID')
}
// Check assigned permissions for write:groups
@@ -47,13 +47,13 @@ module.exports = {
return ['users', 'groups', 'navigation', 'theme', 'api', 'system'].includes(resType)
})
) {
- throw new gql.GraphQLError('You are not authorized to assign a user to this elevated group.')
+ throw new Error('You are not authorized to assign a user to this elevated group.')
}
// Check for valid user
const usr = await WIKI.models.users.query().findById(args.userId)
if (!usr) {
- throw new gql.GraphQLError('Invalid User ID')
+ throw new Error('Invalid User ID')
}
// Check for existing relation
@@ -62,7 +62,7 @@ module.exports = {
groupId: args.groupId
}).first()
if (relExist) {
- throw new gql.GraphQLError('User is already assigned to group.')
+ throw new Error('User is already assigned to group.')
}
// Assign user to group
@@ -73,7 +73,7 @@ module.exports = {
WIKI.events.outbound.emit('addAuthRevoke', { id: usr.id, kind: 'u' })
return {
- responseResult: graphHelper.generateSuccess('User has been assigned to group.')
+ operation: graphHelper.generateSuccess('User has been assigned to group.')
}
},
/**
@@ -83,13 +83,16 @@ module.exports = {
const group = await WIKI.models.groups.query().insertAndFetch({
name: args.name,
permissions: JSON.stringify(WIKI.data.groups.defaultPermissions),
- pageRules: JSON.stringify(WIKI.data.groups.defaultPageRules),
+ rules: JSON.stringify(WIKI.data.groups.defaultRules.map(r => ({
+ id: uuid(),
+ ...r
+ }))),
isSystem: false
})
await WIKI.auth.reloadGroups()
WIKI.events.outbound.emit('reloadGroups')
return {
- responseResult: graphHelper.generateSuccess('Group created successfully.'),
+ operation: graphHelper.generateSuccess('Group created successfully.'),
group
}
},
@@ -98,7 +101,7 @@ module.exports = {
*/
async deleteGroup (obj, args) {
if (args.id === 1 || args.id === 2) {
- throw new gql.GraphQLError('Cannot delete this group.')
+ throw new Error('Cannot delete this group.')
}
await WIKI.models.groups.query().deleteById(args.id)
@@ -110,7 +113,7 @@ module.exports = {
WIKI.events.outbound.emit('reloadGroups')
return {
- responseResult: graphHelper.generateSuccess('Group has been deleted.')
+ operation: graphHelper.generateSuccess('Group has been deleted.')
}
},
/**
@@ -118,18 +121,18 @@ module.exports = {
*/
async unassignUserFromGroup (obj, args) {
if (args.userId === 2) {
- throw new gql.GraphQLError('Cannot unassign Guest user')
+ throw new Error('Cannot unassign Guest user')
}
if (args.userId === 1 && args.groupId === 1) {
- throw new gql.GraphQLError('Cannot unassign Administrator user from Administrators group.')
+ throw new Error('Cannot unassign Administrator user from Administrators group.')
}
const grp = await WIKI.models.groups.query().findById(args.groupId)
if (!grp) {
- throw new gql.GraphQLError('Invalid Group ID')
+ throw new Error('Invalid Group ID')
}
const usr = await WIKI.models.users.query().findById(args.userId)
if (!usr) {
- throw new gql.GraphQLError('Invalid User ID')
+ throw new Error('Invalid User ID')
}
await grp.$relatedQuery('users').unrelate().where('userId', usr.id)
@@ -137,7 +140,7 @@ module.exports = {
WIKI.events.outbound.emit('addAuthRevoke', { id: usr.id, kind: 'u' })
return {
- responseResult: graphHelper.generateSuccess('User has been unassigned from group.')
+ operation: graphHelper.generateSuccess('User has been unassigned from group.')
}
},
/**
@@ -148,7 +151,7 @@ module.exports = {
if (_.some(args.pageRules, pr => {
return pr.match === 'REGEX' && !safeRegex(pr.path)
})) {
- throw new gql.GraphQLError('Some Page Rules contains unsafe or exponential time regex.')
+ throw new Error('Some Page Rules contains unsafe or exponential time regex.')
}
// Set default redirect on login value
@@ -164,7 +167,7 @@ module.exports = {
return ['users', 'groups', 'navigation', 'theme', 'api', 'system'].includes(resType)
})
) {
- throw new gql.GraphQLError('You are not authorized to manage this group or assign these permissions.')
+ throw new Error('You are not authorized to manage this group or assign these permissions.')
}
// Check assigned permissions for manage:groups
@@ -172,7 +175,7 @@ module.exports = {
WIKI.auth.checkExclusiveAccess(req.user, ['manage:groups'], ['manage:system']) &&
args.permissions.some(p => _.last(p.split(':')) === 'system')
) {
- throw new gql.GraphQLError('You are not authorized to manage this group or assign the manage:system permissions.')
+ throw new Error('You are not authorized to manage this group or assign the manage:system permissions.')
}
// Update group
@@ -192,7 +195,7 @@ module.exports = {
WIKI.events.outbound.emit('reloadGroups')
return {
- responseResult: graphHelper.generateSuccess('Group has been updated.')
+ operation: graphHelper.generateSuccess('Group has been updated.')
}
}
},
diff --git a/ux/jsconfig.json b/ux/jsconfig.json
index 456944a5..d4bfff02 100644
--- a/ux/jsconfig.json
+++ b/ux/jsconfig.json
@@ -1,6 +1,7 @@
{
"compilerOptions": {
"baseUrl": ".",
+ "jsx": "preserve",
"paths": {
"src/*": [
"src/*"
@@ -36,4 +37,4 @@
".quasar",
"node_modules"
]
-}
\ No newline at end of file
+}
diff --git a/ux/src/components/GroupCreateDialog.vue b/ux/src/components/GroupCreateDialog.vue
index cd11b627..727ac1bd 100644
--- a/ux/src/components/GroupCreateDialog.vue
+++ b/ux/src/components/GroupCreateDialog.vue
@@ -1,24 +1,21 @@
-q-dialog(ref='dialog', @hide='onDialogHide')
+q-dialog(ref='dialogRef', @hide='onDialogHide')
q-card(style='min-width: 450px;')
q-card-section.card-header
q-icon(name='img:/_assets/icons/fluent-plus-plus.svg', left, size='sm')
- span {{$t(`admin.groups.create`)}}
+ span {{t(`admin.groups.create`)}}
q-form.q-py-sm(ref='createGroupForm', @submit='create')
q-item
blueprint-icon(icon='team')
q-item-section
q-input(
outlined
- v-model='groupName'
+ v-model='state.groupName'
dense
- :rules=`[
- val => val.length > 0 || $t('admin.groups.nameMissing'),
- val => /^[^<>"]+$/.test(val) || $t('admin.groups.nameInvalidChars')
- ]`
+ :rules='groupNameValidation'
hide-bottom-space
- :label='$t(`common.field.name`)'
- :aria-label='$t(`common.field.name`)'
+ :label='t(`common.field.name`)'
+ :aria-label='t(`common.field.name`)'
lazy-rules='ondemand'
autofocus
)
@@ -26,86 +23,103 @@ q-dialog(ref='dialog', @hide='onDialogHide')
q-space
q-btn.acrylic-btn(
flat
- :label='$t(`common.actions.cancel`)'
+ :label='t(`common.actions.cancel`)'
color='grey'
padding='xs md'
- @click='hide'
+ @click='onDialogCancel'
)
q-btn(
unelevated
- :label='$t(`common.actions.create`)'
+ :label='t(`common.actions.create`)'
color='primary'
padding='xs md'
@click='create'
- :loading='isLoading'
+ :loading='state.isLoading'
)
-
diff --git a/ux/src/components/GroupDeleteDialog.vue b/ux/src/components/GroupDeleteDialog.vue
index e4b9f773..4123ca3b 100644
--- a/ux/src/components/GroupDeleteDialog.vue
+++ b/ux/src/components/GroupDeleteDialog.vue
@@ -1,93 +1,96 @@
-q-dialog(ref='dialog', @hide='onDialogHide')
+q-dialog(ref='dialogRef', @hide='onDialogHide')
q-card(style='min-width: 350px; max-width: 450px;')
q-card-section.card-header
q-icon(name='img:/_assets/icons/fluent-delete-bin.svg', left, size='sm')
- span {{$t(`admin.groups.delete`)}}
+ span {{t(`admin.groups.delete`)}}
q-card-section
.text-body2
i18n-t(keypath='admin.groups.deleteConfirm')
template(#groupName)
- strong {{group.name}}
+ strong {{props.group.name}}
.text-body2.q-mt-md
- strong.text-negative {{$t(`admin.groups.deleteConfirmWarn`)}}
+ strong.text-negative {{t(`admin.groups.deleteConfirmWarn`)}}
q-card-actions.card-actions
q-space
q-btn.acrylic-btn(
flat
- :label='$t(`common.actions.cancel`)'
+ :label='t(`common.actions.cancel`)'
color='grey'
padding='xs md'
- @click='hide'
+ @click='onDialogCancel'
)
q-btn(
unelevated
- :label='$t(`common.actions.delete`)'
+ :label='t(`common.actions.delete`)'
color='negative'
padding='xs md'
@click='confirm'
)
-
diff --git a/ux/src/components/GroupEditOverlay.vue b/ux/src/components/GroupEditOverlay.vue
index b3228b3f..6ac2a3c5 100644
--- a/ux/src/components/GroupEditOverlay.vue
+++ b/ux/src/components/GroupEditOverlay.vue
@@ -3,24 +3,24 @@ q-layout(view='hHh lpR fFf', container)
q-header.card-header.q-px-md.q-py-sm
q-icon(name='img:/_assets/icons/fluent-people.svg', left, size='md')
div
- span {{$t(`admin.groups.edit`)}}
- .text-caption {{group.name}}
+ span {{t(`admin.groups.edit`)}}
+ .text-caption {{state.group.name}}
q-space
q-btn-group(push)
q-btn(
push
color='grey-6'
text-color='white'
- :aria-label='$t(`common.actions.refresh`)'
+ :aria-label='t(`common.actions.refresh`)'
icon='las la-redo-alt'
@click='refresh'
)
- q-tooltip(anchor='center left', self='center right') {{$t(`common.actions.refresh`)}}
+ q-tooltip(anchor='center left', self='center right') {{t(`common.actions.refresh`)}}
q-btn(
push
color='white'
text-color='grey-7'
- :label='$t(`common.actions.close`)'
+ :label='t(`common.actions.close`)'
icon='las la-times'
@click='close'
)
@@ -28,11 +28,11 @@ q-layout(view='hHh lpR fFf', container)
push
color='positive'
text-color='white'
- :label='$t(`common.actions.save`)'
+ :label='t(`common.actions.save`)'
icon='las la-check'
)
q-drawer.bg-dark-6(:model-value='true', :width='250', dark)
- q-list(padding, v-show='!isLoading')
+ q-list(padding, v-show='!state.isLoading')
q-item(
v-for='sc of sections'
:key='`section-` + sc.key'
@@ -45,109 +45,107 @@ q-layout(view='hHh lpR fFf', container)
q-icon(:name='sc.icon', color='white')
q-item-section {{sc.text}}
q-item-section(side, v-if='sc.usersTotal')
- q-badge(color='dark-3', :label='usersTotal')
- q-item-section(side, v-if='sc.rulesTotal && group.rules')
- q-badge(color='dark-3', :label='group.rules.length')
+ q-badge(color='dark-3', :label='state.usersTotal')
+ q-item-section(side, v-if='sc.rulesTotal && state.group.rules')
+ q-badge(color='dark-3', :label='state.group.rules.length')
q-page-container
- q-page(v-if='isLoading')
+ q-page(v-if='state.isLoading')
//- -----------------------------------------------------------------------
//- OVERVIEW
//- -----------------------------------------------------------------------
- q-page(v-else-if='$route.params.section === `overview`')
+ q-page(v-else-if='route.params.section === `overview`')
.q-pa-md
.row.q-col-gutter-md
.col-12.col-lg-8
q-card.shadow-1.q-pb-sm
q-card-section
- .text-subtitle1 {{$t('admin.groups.general')}}
+ .text-subtitle1 {{t('admin.groups.general')}}
q-item
blueprint-icon(icon='team')
q-item-section
- q-item-label {{$t(`admin.groups.name`)}}
- q-item-label(caption) {{$t(`admin.groups.nameHint`)}}
+ q-item-label {{t(`admin.groups.name`)}}
+ q-item-label(caption) {{t(`admin.groups.nameHint`)}}
q-item-section
q-input(
outlined
- v-model='group.name'
+ v-model='state.group.name'
dense
- :rules=`[
- val => /^[^<>"]+$/.test(val) || $t('admin.groups.nameInvalidChars')
- ]`
+ :rules='groupNameValidation'
hide-bottom-space
- :aria-label='$t(`admin.groups.name`)'
+ :aria-label='t(`admin.groups.name`)'
)
q-card.shadow-1.q-pb-sm.q-mt-md
q-card-section
- .text-subtitle1 {{$t('admin.groups.authBehaviors')}}
+ .text-subtitle1 {{t('admin.groups.authBehaviors')}}
q-item
blueprint-icon(icon='double-right')
q-item-section
- q-item-label {{$t(`admin.groups.redirectOnLogin`)}}
- q-item-label(caption) {{$t(`admin.groups.redirectOnLoginHint`)}}
+ q-item-label {{t(`admin.groups.redirectOnLogin`)}}
+ q-item-label(caption) {{t(`admin.groups.redirectOnLoginHint`)}}
q-item-section
q-input(
outlined
- v-model='group.redirectOnLogin'
+ v-model='state.group.redirectOnLogin'
dense
- :aria-label='$t(`admin.groups.redirectOnLogin`)'
+ :aria-label='t(`admin.groups.redirectOnLogin`)'
)
q-separator.q-my-sm(inset)
q-item
blueprint-icon(icon='chevron-right')
q-item-section
- q-item-label {{$t(`admin.groups.redirectOnFirstLogin`)}}
- q-item-label(caption) {{$t(`admin.groups.redirectOnFirstLoginHint`)}}
+ q-item-label {{t(`admin.groups.redirectOnFirstLogin`)}}
+ q-item-label(caption) {{t(`admin.groups.redirectOnFirstLoginHint`)}}
q-item-section
q-input(
outlined
- v-model='group.redirectOnFirstLogin'
+ v-model='state.group.redirectOnFirstLogin'
dense
- :aria-label='$t(`admin.groups.redirectOnLogin`)'
+ :aria-label='t(`admin.groups.redirectOnLogin`)'
)
q-separator.q-my-sm(inset)
q-item
blueprint-icon(icon='exit')
q-item-section
- q-item-label {{$t(`admin.groups.redirectOnLogout`)}}
- q-item-label(caption) {{$t(`admin.groups.redirectOnLogoutHint`)}}
+ q-item-label {{t(`admin.groups.redirectOnLogout`)}}
+ q-item-label(caption) {{t(`admin.groups.redirectOnLogoutHint`)}}
q-item-section
q-input(
outlined
- v-model='group.redirectOnLogout'
+ v-model='state.group.redirectOnLogout'
dense
- :aria-label='$t(`admin.groups.redirectOnLogout`)'
+ :aria-label='t(`admin.groups.redirectOnLogout`)'
)
.col-12.col-lg-4
q-card.shadow-1.q-pb-sm
q-card-section
- .text-subtitle1 {{$t('admin.groups.info')}}
+ .text-subtitle1 {{t('admin.groups.info')}}
q-item
blueprint-icon(icon='team', :hue-rotate='-45')
q-item-section
- q-item-label {{$t(`common.field.id`)}}
- q-item-label: strong {{groupId}}
+ q-item-label {{t(`common.field.id`)}}
+ q-item-label: strong {{state.group.id}}
q-separator.q-my-sm(inset)
q-item
blueprint-icon(icon='calendar-plus', :hue-rotate='-45')
q-item-section
- q-item-label {{$t(`common.field.createdOn`)}}
- q-item-label: strong {{humanizeDate(group.createdAt)}}
+ q-item-label {{t(`common.field.createdOn`)}}
+ q-item-label: strong {{humanizeDate(state.group.createdAt)}}
q-separator.q-my-sm(inset)
q-item
blueprint-icon(icon='summertime', :hue-rotate='-45')
q-item-section
- q-item-label {{$t(`common.field.lastUpdated`)}}
- q-item-label: strong {{humanizeDate(group.updatedAt)}}
+ q-item-label {{t(`common.field.lastUpdated`)}}
+ q-item-label: strong {{humanizeDate(state.group.updatedAt)}}
//- -----------------------------------------------------------------------
//- RULES
//- -----------------------------------------------------------------------
- q-page(v-else-if='$route.params.section === `rules`')
+ q-page(v-else-if='route.params.section === `rules`')
q-toolbar.q-pl-md(
:class='$q.dark.isActive ? `bg-dark-3` : `bg-white`'
)
- .text-subtitle1 {{$t('admin.groups.rules')}}
+ .text-subtitle1 {{t('admin.groups.rules')}}
q-space
q-btn.acrylic-btn.q-mr-sm(
icon='las la-question-circle'
@@ -163,14 +161,14 @@ q-layout(view='hHh lpR fFf', container)
icon='las la-file-export'
@click='exportRules'
)
- q-tooltip {{$t('admin.groups.exportRules')}}
+ q-tooltip {{t('admin.groups.exportRules')}}
q-btn.acrylic-btn.q-mr-sm(
flat
color='indigo'
icon='las la-file-import'
@click='importRules'
)
- q-tooltip {{$t('admin.groups.importRules')}}
+ q-tooltip {{t('admin.groups.importRules')}}
q-btn(
unelevated
color='primary'
@@ -181,14 +179,14 @@ q-layout(view='hHh lpR fFf', container)
q-separator
.q-pa-md
q-banner(
- v-if='!group.rules || group.rules.length < 1'
+ v-if='!state.group.rules || state.group.rules.length < 1'
rounded
:class='$q.dark.isActive ? `bg-negative text-white` : `bg-grey-4 text-grey-9`'
- ) {{$t('admin.groups.rulesNone')}}
+ ) {{t('admin.groups.rulesNone')}}
q-card.shadow-1.q-pb-sm(v-else)
q-card-section
.admin-groups-rule(
- v-for='(rule, idx) of group.rules'
+ v-for='(rule, idx) of state.group.rules'
:key='rule.id'
)
.admin-groups-rule-icon(:class='getRuleModeColor(rule.mode)')
@@ -213,7 +211,7 @@ q-layout(view='hHh lpR fFf', container)
emit-value
map-options
dense
- :aria-label='$t(`admin.groups.ruleSites`)'
+ :aria-label='t(`admin.groups.ruleSites`)'
:options='rules'
placeholder='Select permissions...'
option-value='permission'
@@ -261,13 +259,13 @@ q-layout(view='hHh lpR fFf', container)
emit-value
map-options
dense
- :aria-label='$t(`admin.groups.ruleSites`)'
- :options='sites'
+ :aria-label='t(`admin.groups.ruleSites`)'
+ :options='adminStore.sites'
option-value='id'
option-label='title'
multiple
behavior='dialog'
- :display-value='$tc(`admin.groups.selectedSites`, rule.sites.length, { count: rule.sites.length })'
+ :display-value='t(`admin.groups.selectedSites`, rule.sites.length, { count: rule.sites.length })'
)
template(v-slot:option='{ itemProps, itemEvents, opt, selected, toggleOption }')
q-item(v-bind='itemProps', v-on='itemEvents')
@@ -288,13 +286,13 @@ q-layout(view='hHh lpR fFf', container)
emit-value
map-options
dense
- :aria-label='$t(`admin.groups.ruleLocales`)'
- :options='locales'
+ :aria-label='t(`admin.groups.ruleLocales`)'
+ :options='adminStore.locales'
option-value='code'
option-label='name'
multiple
behavior='dialog'
- :display-value='$tc(`admin.groups.selectedLocales`, rule.locales.length, { count: rule.locales.length, locale: rule.locales.length === 1 ? rule.locales[0].toUpperCase() : `` })'
+ :display-value='t(`admin.groups.selectedLocales`, rule.locales.length, { count: rule.locales.length, locale: rule.locales.length === 1 ? rule.locales[0].toUpperCase() : `` })'
)
template(v-slot:option='{ itemProps, opt, selected, toggleOption }')
q-item(v-bind='itemProps')
@@ -317,14 +315,14 @@ q-layout(view='hHh lpR fFf', container)
emit-value
map-options
dense
- :aria-label='$t(`admin.groups.ruleMatch`)'
+ :aria-label='t(`admin.groups.ruleMatch`)'
:options=`[
- { label: $t('admin.groups.ruleMatchStart'), value: 'START' },
- { label: $t('admin.groups.ruleMatchEnd'), value: 'END' },
- { label: $t('admin.groups.ruleMatchRegex'), value: 'REGEX' },
- { label: $t('admin.groups.ruleMatchTag'), value: 'TAG' },
- { label: $t('admin.groups.ruleMatchTagAll'), value: 'TAGALL' },
- { label: $t('admin.groups.ruleMatchExact'), value: 'EXACT' }
+ { label: t('admin.groups.ruleMatchStart'), value: 'START' },
+ { label: t('admin.groups.ruleMatchEnd'), value: 'END' },
+ { label: t('admin.groups.ruleMatchRegex'), value: 'REGEX' },
+ { label: t('admin.groups.ruleMatchTag'), value: 'TAG' },
+ { label: t('admin.groups.ruleMatchTagAll'), value: 'TAGALL' },
+ { label: t('admin.groups.ruleMatchExact'), value: 'EXACT' }
]`
)
q-input.q-mt-sm(
@@ -333,19 +331,19 @@ q-layout(view='hHh lpR fFf', container)
dense
:prefix='[`START`, `REGEX`, `EXACT`].includes(rule.match) ? `/` : null'
:suffix='rule.match === `REGEX` ? `/` : null'
- :aria-label='$t(`admin.groups.rulePath`)'
+ :aria-label='t(`admin.groups.rulePath`)'
)
//- -----------------------------------------------------------------------
//- PERMISSIONS
//- -----------------------------------------------------------------------
- q-page(v-else-if='$route.params.section === `permissions`')
+ q-page(v-else-if='route.params.section === `permissions`')
.q-pa-md
.row.q-col-gutter-md
.col-12.col-lg-6
q-card.shadow-1.q-pb-sm
.flex.justify-between
q-card-section
- .text-subtitle1 {{$t(`admin.groups.permissions`)}}
+ .text-subtitle1 {{t(`admin.groups.permissions`)}}
q-card-section
q-btn.acrylic-btn(
icon='las la-question-circle'
@@ -368,22 +366,22 @@ q-layout(view='hHh lpR fFf', container)
q-item-label(caption) {{perm.hint}}
q-item-section(avatar)
q-toggle(
- v-model='group.permissions'
+ v-model='state.group.permissions'
:val='perm.permission'
color='primary'
checked-icon='las la-check'
unchecked-icon='las la-times'
- :aria-label='$t(`admin.general.allowComments`)'
+ :aria-label='t(`admin.general.allowComments`)'
)
q-separator.q-my-sm(inset, v-if='idx < permissions.length - 1')
//- -----------------------------------------------------------------------
//- USERS
//- -----------------------------------------------------------------------
- q-page(v-else-if='$route.params.section === `users`')
+ q-page(v-else-if='route.params.section === `users`')
q-toolbar(
:class='$q.dark.isActive ? `bg-dark-3` : `bg-white`'
)
- .text-subtitle1 {{$t('admin.groups.users')}}
+ .text-subtitle1 {{t('admin.groups.users')}}
q-space
q-btn.acrylic-btn.q-mr-sm(
icon='las la-question-circle'
@@ -395,8 +393,8 @@ q-layout(view='hHh lpR fFf', container)
)
q-input.denser.fill-outline.q-mr-sm(
outlined
- v-model='usersFilter'
- :placeholder='$t(`admin.groups.filterUsers`)'
+ v-model='state.usersFilter'
+ :placeholder='t(`admin.groups.filterUsers`)'
dense
)
template(#prepend)
@@ -410,22 +408,27 @@ q-layout(view='hHh lpR fFf', container)
q-btn.q-mr-xs(
unelevated
icon='las la-user-plus'
- :label='$t(`admin.groups.assignUser`)'
+ :label='t(`admin.groups.assignUser`)'
color='primary'
@click='assignUser'
)
q-separator
.q-pa-md
+ q-banner(
+ v-if='!state.users || state.users.length < 1'
+ rounded
+ :class='$q.dark.isActive ? `bg-negative text-white` : `bg-grey-4 text-grey-9`'
+ ) {{t('admin.groups.usersNone')}}
q-card.shadow-1
q-table(
- :rows='users'
+ :rows='state.users'
:columns='usersHeaders'
row-key='id'
flat
hide-header
hide-bottom
:rows-per-page-options='[0]'
- :loading='isLoadingUsers'
+ :loading='state.isLoadingUsers'
)
template(v-slot:body-cell-id='props')
q-td(:props='props')
@@ -467,7 +470,7 @@ q-layout(view='hHh lpR fFf', container)
:to='`/_admin/users/` + props.row.id'
icon='las la-pen'
color='indigo'
- :label='$t(`common.actions.edit`)'
+ :label='t(`common.actions.edit`)'
no-caps
)
q-btn.acrylic-btn(
@@ -480,7 +483,7 @@ q-layout(view='hHh lpR fFf', container)
.flex.flex-center.q-mt-md(v-if='usersTotalPages > 1')
q-pagination(
- v-model='usersPage'
+ v-model='state.usersPage'
:max='usersTotalPages'
:max-pages='9'
boundary-numbers
@@ -488,517 +491,565 @@ q-layout(view='hHh lpR fFf', container)
)
-