AdminGroups.vue 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. <template lang='pug'>
  2. q-page.admin-groups
  3. .row.q-pa-md.items-center
  4. .col-auto
  5. img.admin-icon.animated.fadeInLeft(src='/_assets/icons/fluent-people.svg')
  6. .col.q-pl-md
  7. .text-h5.text-primary.animated.fadeInLeft {{ t('admin.groups.title') }}
  8. .text-subtitle1.text-grey.animated.fadeInLeft.wait-p2s {{ t('admin.groups.subtitle') }}
  9. .col-auto.flex.items-center
  10. q-input.denser.q-mr-sm(
  11. outlined
  12. v-model='state.search'
  13. dense
  14. :class='$q.dark.isActive ? `bg-dark` : `bg-white`'
  15. )
  16. template(#prepend)
  17. q-icon(name='las la-search')
  18. q-btn.acrylic-btn.q-mr-sm(
  19. icon='las la-question-circle'
  20. flat
  21. color='grey'
  22. type='a'
  23. :href='siteStore.docsBase + `/admin/groups`'
  24. target='_blank'
  25. )
  26. q-btn.q-mr-sm.acrylic-btn(
  27. icon='las la-redo-alt'
  28. flat
  29. color='secondary'
  30. @click='load'
  31. :loading='state.loading > 0'
  32. )
  33. q-btn(
  34. unelevated
  35. icon='las la-plus'
  36. :label='t(`admin.groups.create`)'
  37. color='primary'
  38. @click='createGroup'
  39. )
  40. q-separator(inset)
  41. .row.q-pa-md.q-col-gutter-md
  42. .col-12
  43. q-card
  44. q-table(
  45. :rows='state.groups'
  46. :columns='headers'
  47. row-key='id'
  48. flat
  49. hide-header
  50. hide-bottom
  51. :rows-per-page-options='[0]'
  52. :loading='state.loading > 0'
  53. :filter='state.search'
  54. )
  55. template(v-slot:body-cell-id='props')
  56. q-td(:props='props')
  57. q-icon(name='las la-users', color='primary', size='sm')
  58. template(v-slot:body-cell-name='props')
  59. q-td(:props='props')
  60. .flex.items-center
  61. strong {{props.value}}
  62. q-icon.q-ml-sm(
  63. v-if='props.row.isSystem'
  64. name='las la-lock'
  65. color='pink'
  66. )
  67. template(v-slot:body-cell-usercount='props')
  68. q-td(:props='props')
  69. q-chip.text-caption(
  70. square
  71. :color='$q.dark.isActive ? `dark-6` : `grey-2`'
  72. :text-color='$q.dark.isActive ? `white` : `grey-8`'
  73. dense
  74. ) {{t('admin.groups.usersCount', { count: props.value })}}
  75. template(v-slot:body-cell-edit='props')
  76. q-td(:props='props')
  77. q-btn.acrylic-btn.q-mr-sm(
  78. flat
  79. :to='`/_admin/groups/` + props.row.id'
  80. icon='las la-pen'
  81. color='indigo'
  82. :label='t(`common.actions.edit`)'
  83. no-caps
  84. )
  85. q-btn.acrylic-btn(
  86. flat
  87. icon='las la-trash'
  88. :color='props.row.isSystem ? `grey` : `negative`'
  89. :disabled='props.row.isSystem'
  90. @click='deleteGroup(props.row)'
  91. )
  92. </template>
  93. <script setup>
  94. import gql from 'graphql-tag'
  95. import { cloneDeep } from 'lodash-es'
  96. import { useI18n } from 'vue-i18n'
  97. import { useMeta, useQuasar } from 'quasar'
  98. import { computed, onBeforeUnmount, onMounted, reactive, watch } from 'vue'
  99. import { useRouter, useRoute } from 'vue-router'
  100. import { useAdminStore } from 'src/stores/admin'
  101. import { useSiteStore } from 'src/stores/site'
  102. import GroupCreateDialog from '../components/GroupCreateDialog.vue'
  103. import GroupDeleteDialog from '../components/GroupDeleteDialog.vue'
  104. // QUASAR
  105. const $q = useQuasar()
  106. // STORES
  107. const adminStore = useAdminStore()
  108. const siteStore = useSiteStore()
  109. // ROUTER
  110. const router = useRouter()
  111. const route = useRoute()
  112. // I18N
  113. const { t } = useI18n()
  114. // META
  115. useMeta({
  116. title: t('admin.groups.title')
  117. })
  118. // DATA
  119. const state = reactive({
  120. groups: [],
  121. loading: 0,
  122. search: ''
  123. })
  124. const headers = [
  125. {
  126. align: 'center',
  127. field: 'id',
  128. name: 'id',
  129. sortable: false,
  130. style: 'width: 20px'
  131. },
  132. {
  133. label: t('common.field.name'),
  134. align: 'left',
  135. field: 'name',
  136. name: 'name',
  137. sortable: true
  138. },
  139. {
  140. label: t('admin.groups.userCount'),
  141. align: 'center',
  142. field: 'userCount',
  143. name: 'usercount',
  144. sortable: false,
  145. style: 'width: 150px'
  146. },
  147. {
  148. label: '',
  149. align: 'right',
  150. field: 'edit',
  151. name: 'edit',
  152. sortable: false,
  153. style: 'width: 250px'
  154. }
  155. ]
  156. // WATCHERS
  157. watch(() => adminStore.overlay, (newValue, oldValue) => {
  158. if (newValue === '' && oldValue === 'GroupEditOverlay') {
  159. router.push('/_admin/groups')
  160. load()
  161. }
  162. })
  163. watch(() => route.params.id, checkOverlay)
  164. // METHODS
  165. async function load () {
  166. state.loading++
  167. $q.loading.show()
  168. try {
  169. const resp = await APOLLO_CLIENT.query({
  170. query: gql`
  171. query getGroups {
  172. groups {
  173. id
  174. name
  175. isSystem
  176. userCount
  177. createdAt
  178. updatedAt
  179. }
  180. }
  181. `,
  182. fetchPolicy: 'network-only'
  183. })
  184. state.groups = cloneDeep(resp?.data?.groups)
  185. } catch (err) {
  186. $q.notify({
  187. type: 'negative',
  188. message: 'Failed to load groups.',
  189. caption: err.message
  190. })
  191. }
  192. $q.loading.hide()
  193. state.loading--
  194. }
  195. function checkOverlay () {
  196. if (route.params?.id) {
  197. adminStore.$patch({
  198. overlayOpts: { id: route.params.id },
  199. overlay: 'GroupEditOverlay'
  200. })
  201. } else {
  202. adminStore.$patch({
  203. overlay: ''
  204. })
  205. }
  206. }
  207. function createGroup () {
  208. $q.dialog({
  209. component: GroupCreateDialog
  210. }).onOk(() => {
  211. load()
  212. })
  213. }
  214. function editGroup (gr) {
  215. router.push(`/_admin/groups/${gr.id}`)
  216. }
  217. function deleteGroup (gr) {
  218. $q.dialog({
  219. component: GroupDeleteDialog,
  220. componentProps: {
  221. group: gr
  222. }
  223. }).onOk(() => {
  224. load()
  225. })
  226. }
  227. // MOUNTED
  228. onMounted(() => {
  229. checkOverlay()
  230. load()
  231. })
  232. // BEFORE UNMOUNT
  233. onBeforeUnmount(() => {
  234. adminStore.$patch({
  235. overlay: ''
  236. })
  237. })
  238. </script>
  239. <style lang='scss'>
  240. </style>