feat(admin): migrate groups page to vue 3 composable

pull/5698/head
Nicolas Giard 2 years ago
parent 1eefdb06e6
commit 7e344fc6fa
No known key found for this signature in database
GPG Key ID: 85061B8F9D55B7C8

@ -341,7 +341,7 @@ exports.up = async knex => {
// -> GENERATE IDS
const groupAdminId = uuid()
const groupGuestId = '10000000-0000-4000-0000-000000000001'
const groupGuestId = '10000000-0000-4000-8000-000000000001'
const siteId = uuid()
const authModuleId = uuid()
const userAdminId = uuid()

@ -9,7 +9,7 @@ extend type Query {
): [Group]
groupById(
id: Int!
id: UUID!
): Group
}
@ -19,22 +19,25 @@ extend type Mutation {
): GroupResponse
updateGroup(
id: Int!
patch: GroupUpdateInput!
id: UUID!
name: String!
redirectOnLogin: String!
permissions: [String]!
rules: [GroupRuleInput]!
): DefaultResponse
deleteGroup(
id: Int!
id: UUID!
): DefaultResponse
assignUserToGroup(
groupId: Int!
userId: Int!
groupId: UUID!
userId: UUID!
): DefaultResponse
unassignUserFromGroup(
groupId: Int!
userId: Int!
groupId: UUID!
userId: UUID!
): DefaultResponse
}
@ -48,46 +51,60 @@ type GroupResponse {
}
type Group {
id: Int
id: UUID
name: String
isSystem: Boolean
redirectOnLogin: String
redirectOnFirstLogin: String
redirectOnLogout: String
permissions: [String]
pageRules: [PageRule]
users: [UserMinimal]
rules: [GroupRule]
users(
page: Int
pageSize: Int
orderBy: UserOrderBy
orderByDirection: OrderByDirection
# Filter by name / email
filter: String
): [UserMinimal]
userCount: Int
createdAt: Date
updatedAt: Date
}
type PageRule {
id: String
deny: Boolean
match: PageRuleMatch
type GroupRule {
id: UUID
name: String
mode: GroupRuleMode
match: GroupRuleMatch
roles: [String]
path: String
locales: [String]
sites: [UUID]
}
input GroupUpdateInput {
input GroupRuleInput {
id: UUID!
name: String!
redirectOnLogin: String!
permissions: [String]!
pageRules: [PageRuleInput]!
}
input PageRuleInput {
id: String!
deny: Boolean!
match: PageRuleMatch!
mode: GroupRuleMode!
match: GroupRuleMatch!
roles: [String]!
path: String!
locales: [String]!
sites: [UUID]
}
enum GroupRuleMode {
ALLOW
DENY
FORCEALLOW
}
enum PageRuleMatch {
enum GroupRuleMatch {
START
EXACT
END
REGEX
TAG
TAGALL
}

@ -4,12 +4,12 @@ q-page.admin-groups
.col-auto
img.admin-icon.animated.fadeInLeft(src='/_assets/icons/fluent-people.svg')
.col.q-pl-md
.text-h5.text-primary.animated.fadeInLeft {{ $t('admin.groups.title') }}
.text-subtitle1.text-grey.animated.fadeInLeft.wait-p2s {{ $t('admin.groups.subtitle') }}
.text-h5.text-primary.animated.fadeInLeft {{ t('admin.groups.title') }}
.text-subtitle1.text-grey.animated.fadeInLeft.wait-p2s {{ t('admin.groups.subtitle') }}
.col-auto.flex.items-center
q-input.denser.q-mr-sm(
outlined
v-model='search'
v-model='state.search'
dense
:class='$q.dark.isActive ? `bg-dark` : `bg-white`'
)
@ -28,12 +28,12 @@ q-page.admin-groups
flat
color='secondary'
@click='load'
:loading='loading > 0'
:loading='state.loading > 0'
)
q-btn(
unelevated
icon='las la-plus'
:label='$t(`admin.groups.create`)'
:label='t(`admin.groups.create`)'
color='primary'
@click='createGroup'
)
@ -42,15 +42,15 @@ q-page.admin-groups
.col-12
q-card.shadow-1
q-table(
:rows='groups'
:rows='state.groups'
:columns='headers'
row-key='id'
flat
hide-header
hide-bottom
:rows-per-page-options='[0]'
:loading='loading > 0'
:filter='search'
:loading='state.loading > 0'
:filter='state.search'
)
template(v-slot:body-cell-id='props')
q-td(:props='props')
@ -71,7 +71,7 @@ q-page.admin-groups
:color='$q.dark.isActive ? `dark-6` : `grey-2`'
:text-color='$q.dark.isActive ? `white` : `grey-8`'
dense
) {{$t('admin.groups.usersCount', { count: props.value })}}
) {{t('admin.groups.usersCount', { count: props.value })}}
template(v-slot:body-cell-edit='props')
q-td(:props='props')
q-btn.acrylic-btn.q-mr-sm(
@ -79,7 +79,7 @@ q-page.admin-groups
:to='`/_admin/groups/` + 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(
@ -91,136 +91,178 @@ q-page.admin-groups
)
</template>
<script>
<script setup>
import gql from 'graphql-tag'
import cloneDeep from 'lodash/cloneDeep'
import { createMetaMixin } from 'quasar'
import { sync } from 'vuex-pathify'
import { useI18n } from 'vue-i18n'
import { useMeta, useQuasar } from 'quasar'
import { computed, onBeforeUnmount, onMounted, reactive, watch } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { useAdminStore } from 'src/stores/admin'
import GroupCreateDialog from '../components/GroupCreateDialog.vue'
import GroupDeleteDialog from '../components/GroupDeleteDialog.vue'
export default {
mixins: [
createMetaMixin(function () {
return {
title: this.$t('admin.groups.title')
}
})
],
data () {
return {
groups: [],
loading: 0,
search: ''
}
},
computed: {
overlay: sync('admin/overlay', false),
headers () {
return [
{
align: 'center',
field: 'id',
name: 'id',
sortable: false,
style: 'width: 20px'
},
{
label: this.$t('common.field.name'),
align: 'left',
field: 'name',
name: 'name',
sortable: true
},
{
label: this.$t('admin.groups.userCount'),
align: 'center',
field: 'userCount',
name: 'usercount',
sortable: false,
style: 'width: 150px'
},
{
label: '',
align: 'right',
field: 'edit',
name: 'edit',
sortable: false,
style: 'width: 250px'
}
]
}
},
watch: {
overlay (newValue, oldValue) {
if (newValue === '' && oldValue === 'GroupEditOverlay') {
this.$router.push('/_admin/groups')
this.load()
}
},
$route: 'checkOverlay'
// QUASAR
const $q = useQuasar()
// STORES
const adminStore = useAdminStore()
// ROUTER
const router = useRouter()
const route = useRoute()
// I18N
const { t } = useI18n()
// META
useMeta({
title: t('admin.groups.title')
})
// DATA
const state = reactive({
groups: [],
loading: 0,
search: ''
})
const headers = [
{
align: 'center',
field: 'id',
name: 'id',
sortable: false,
style: 'width: 20px'
},
mounted () {
this.checkOverlay()
this.load()
{
label: t('common.field.name'),
align: 'left',
field: 'name',
name: 'name',
sortable: true
},
beforeUnmount () {
this.overlay = ''
{
label: t('admin.groups.userCount'),
align: 'center',
field: 'userCount',
name: 'usercount',
sortable: false,
style: 'width: 150px'
},
methods: {
async load () {
this.loading++
this.$q.loading.show()
const resp = await this.$apollo.query({
query: gql`
query getGroups {
groups {
id
name
isSystem
userCount
createdAt
updatedAt
}
{
label: '',
align: 'right',
field: 'edit',
name: 'edit',
sortable: false,
style: 'width: 250px'
}
]
watch(() => adminStore.overlay, (newValue, oldValue) => {
if (newValue === '' && oldValue === 'GroupEditOverlay') {
router.push('/_admin/groups')
load()
}
})
watch(() => route, () => {
checkOverlay()
})
// METHODS
async function load () {
state.loading++
$q.loading.show()
try {
const resp = await APOLLO_CLIENT.query({
query: gql`
query getGroups {
groups {
id
name
isSystem
userCount
createdAt
updatedAt
}
`,
fetchPolicy: 'network-only'
})
this.groups = cloneDeep(resp?.data?.groups)
this.$q.loading.hide()
this.loading--
},
checkOverlay () {
if (this.$route.params && this.$route.params.id) {
this.$store.set('admin/overlayOpts', { id: this.$route.params.id })
this.$store.set('admin/overlay', 'GroupEditOverlay')
} else {
this.$store.set('admin/overlay', '')
}
},
createGroup () {
this.$q.dialog({
component: GroupCreateDialog
}).onOk(() => {
this.load()
})
},
editGroup (gr) {
this.$router.push(`/_admin/groups/${gr.id}`)
},
deleteGroup (gr) {
this.$q.dialog({
component: GroupDeleteDialog,
componentProps: {
group: gr
}
}).onOk(() => {
this.load()
})
}
`,
fetchPolicy: 'network-only'
})
state.groups = cloneDeep(resp?.data?.groups)
} catch (err) {
$q.notify({
type: 'negative',
message: 'Failed to load groups.',
caption: err.message
})
}
$q.loading.hide()
state.loading--
}
function checkOverlay () {
if (route.params && route.params.id) {
adminStore.$patch({
overlayOpts: { id: route.params.id },
overlay: 'GroupEditOverlay'
})
} else {
adminStore.$patch({
overlay: ''
})
}
}
function createGroup () {
$q.dialog({
component: GroupCreateDialog
}).onOk(() => {
load()
})
}
function editGroup (gr) {
router.push(`/_admin/groups/${gr.id}`)
}
function deleteGroup (gr) {
$q.dialog({
component: GroupDeleteDialog,
componentProps: {
group: gr
}
}).onOk(() => {
load()
})
}
// MOUNTED
onMounted(() => {
checkOverlay()
load()
})
// BEFORE UNMOUNT
onBeforeUnmount(() => {
adminStore.$patch({
overlay: ''
})
})
</script>
<style lang='scss'>

@ -41,7 +41,7 @@ const routes = [
// { path: ':siteid/theme', component: () => import('../pages/AdminTheme.vue') },
// -> Users
// { path: 'auth', component: () => import('../pages/AdminAuth.vue') },
// { path: 'groups/:id?/:section?', component: () => import('../pages/AdminGroups.vue') },
{ path: 'groups/:id?/:section?', component: () => import('../pages/AdminGroups.vue') },
// { path: 'users/:id?/:section?', component: () => import('../pages/AdminUsers.vue') },
// -> System
// { path: 'api', component: () => import('../pages/AdminApi.vue') },

Loading…
Cancel
Save