You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

UserManagementNavigation.vue 7.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. <template>
  2. <NcAppNavigation :aria-label="t('settings', 'Account management')">
  3. <NcAppNavigationNew button-id="new-user-button"
  4. :text="t('settings','New account')"
  5. @click="showNewUserMenu"
  6. @keyup.enter="showNewUserMenu"
  7. @keyup.space="showNewUserMenu">
  8. <template #icon>
  9. <NcIconSvgWrapper :path="mdiPlus" />
  10. </template>
  11. </NcAppNavigationNew>
  12. <NcAppNavigationList class="account-management__system-list"
  13. data-cy-users-settings-navigation-groups="system">
  14. <NcAppNavigationItem id="everyone"
  15. :exact="true"
  16. :name="t('settings', 'Active accounts')"
  17. :to="{ name: 'users' }">
  18. <template #icon>
  19. <NcIconSvgWrapper :path="mdiAccount" />
  20. </template>
  21. <template #counter>
  22. <NcCounterBubble v-if="userCount" :type="!selectedGroupDecoded ? 'highlighted' : undefined">
  23. {{ userCount }}
  24. </NcCounterBubble>
  25. </template>
  26. </NcAppNavigationItem>
  27. <NcAppNavigationItem v-if="isAdmin"
  28. id="admin"
  29. :exact="true"
  30. :name="t('settings', 'Admins')"
  31. :to="{ name: 'group', params: { selectedGroup: 'admin' } }">
  32. <template #icon>
  33. <NcIconSvgWrapper :path="mdiShieldAccount" />
  34. </template>
  35. <template #counter>
  36. <NcCounterBubble v-if="adminGroup && adminGroup.count > 0"
  37. :type="selectedGroupDecoded === 'admin' ? 'highlighted' : undefined">
  38. {{ adminGroup.count }}
  39. </NcCounterBubble>
  40. </template>
  41. </NcAppNavigationItem>
  42. <!-- Hide the disabled if none, if we don't have the data (-1) show it -->
  43. <NcAppNavigationItem v-if="disabledGroup && (disabledGroup.usercount > 0 || disabledGroup.usercount === -1)"
  44. id="disabled"
  45. :exact="true"
  46. :name="t('settings', 'Disabled accounts')"
  47. :to="{ name: 'group', params: { selectedGroup: 'disabled' } }">
  48. <template #icon>
  49. <NcIconSvgWrapper :path="mdiAccountOff" />
  50. </template>
  51. <template v-if="disabledGroup.usercount > 0" #counter>
  52. <NcCounterBubble :type="selectedGroupDecoded === 'disabled' ? 'highlighted' : undefined">
  53. {{ disabledGroup.usercount }}
  54. </NcCounterBubble>
  55. </template>
  56. </NcAppNavigationItem>
  57. </NcAppNavigationList>
  58. <NcAppNavigationCaption :name="t('settings', 'Groups')"
  59. :disabled="loadingAddGroup"
  60. :aria-label="loadingAddGroup ? t('settings', 'Creating group…') : t('settings', 'Create group')"
  61. force-menu
  62. is-heading
  63. :open.sync="isAddGroupOpen">
  64. <template #actionsTriggerIcon>
  65. <NcLoadingIcon v-if="loadingAddGroup" />
  66. <NcIconSvgWrapper v-else :path="mdiPlus" />
  67. </template>
  68. <template #actions>
  69. <NcActionText>
  70. <template #icon>
  71. <AccountGroup :size="20" />
  72. </template>
  73. {{ t('settings', 'Create group') }}
  74. </NcActionText>
  75. <NcActionInput :label="t('settings', 'Group name')"
  76. data-cy-users-settings-new-group-name
  77. :label-outside="false"
  78. :disabled="loadingAddGroup"
  79. :value.sync="newGroupName"
  80. :error="hasAddGroupError"
  81. :helper-text="hasAddGroupError ? t('settings', 'Please enter a valid group name') : ''"
  82. @submit="createGroup" />
  83. </template>
  84. </NcAppNavigationCaption>
  85. <NcAppNavigationList class="account-management__group-list" data-cy-users-settings-navigation-groups="custom">
  86. <GroupListItem v-for="group in userGroups"
  87. :id="group.id"
  88. :key="group.id"
  89. :active="selectedGroupDecoded === group.id"
  90. :name="group.title"
  91. :count="group.count" />
  92. </NcAppNavigationList>
  93. <template #footer>
  94. <NcButton class="account-management__settings-toggle"
  95. type="tertiary"
  96. @click="isDialogOpen = true">
  97. <template #icon>
  98. <NcIconSvgWrapper :path="mdiCog" />
  99. </template>
  100. {{ t('settings', 'Account management settings') }}
  101. </NcButton>
  102. <UserSettingsDialog :open.sync="isDialogOpen" />
  103. </template>
  104. </NcAppNavigation>
  105. </template>
  106. <script setup lang="ts">
  107. import { mdiAccount, mdiAccountOff, mdiCog, mdiPlus, mdiShieldAccount } from '@mdi/js'
  108. import { showError } from '@nextcloud/dialogs'
  109. import { translate as t } from '@nextcloud/l10n'
  110. import { computed, ref } from 'vue'
  111. import NcActionInput from '@nextcloud/vue/dist/Components/NcActionInput.js'
  112. import NcActionText from '@nextcloud/vue/dist/Components/NcActionText.js'
  113. import NcAppNavigation from '@nextcloud/vue/dist/Components/NcAppNavigation.js'
  114. import NcAppNavigationCaption from '@nextcloud/vue/dist/Components/NcAppNavigationCaption.js'
  115. import NcAppNavigationItem from '@nextcloud/vue/dist/Components/NcAppNavigationItem.js'
  116. import NcAppNavigationList from '@nextcloud/vue/dist/Components/NcAppNavigationList.js'
  117. import NcAppNavigationNew from '@nextcloud/vue/dist/Components/NcAppNavigationNew.js'
  118. import NcButton from '@nextcloud/vue/dist/Components/NcButton.js'
  119. import NcCounterBubble from '@nextcloud/vue/dist/Components/NcCounterBubble.js'
  120. import NcIconSvgWrapper from '@nextcloud/vue/dist/Components/NcIconSvgWrapper.js'
  121. import NcLoadingIcon from '@nextcloud/vue/dist/Components/NcLoadingIcon.js'
  122. import GroupListItem from '../components/GroupListItem.vue'
  123. import UserSettingsDialog from '../components/Users/UserSettingsDialog.vue'
  124. import { useStore } from '../store'
  125. import { useRoute, useRouter } from 'vue-router/composables'
  126. import { useFormatGroups } from '../composables/useGroupsNavigation'
  127. const route = useRoute()
  128. const router = useRouter()
  129. const store = useStore()
  130. /** State of the 'new-account' dialog */
  131. const isDialogOpen = ref(false)
  132. /** Current active group in the view - this is URL encoded */
  133. const selectedGroup = computed(() => route.params?.selectedGroup)
  134. /** Current active group - URL decoded */
  135. const selectedGroupDecoded = computed(() => selectedGroup.value ? decodeURIComponent(selectedGroup.value) : null)
  136. /** Overall user count */
  137. const userCount = computed(() => store.getters.getUserCount)
  138. /** All available groups */
  139. const groups = computed(() => store.getters.getSortedGroups)
  140. const { adminGroup, disabledGroup, userGroups } = useFormatGroups(groups)
  141. /** True if the current user is an administrator */
  142. const isAdmin = computed(() => store.getters.getServerData.isAdmin)
  143. /** True if the 'add-group' dialog is open - needed to be able to close it when the group is created */
  144. const isAddGroupOpen = ref(false)
  145. /** True if the group creation is in progress to show loading spinner and disable adding another one */
  146. const loadingAddGroup = ref(false)
  147. /** Error state for creating a new group */
  148. const hasAddGroupError = ref(false)
  149. /** Name of the group to create (used in the group creation dialog) */
  150. const newGroupName = ref('')
  151. /**
  152. * Create a new group
  153. */
  154. async function createGroup() {
  155. hasAddGroupError.value = false
  156. const groupId = newGroupName.value.trim()
  157. if (groupId === '') {
  158. hasAddGroupError.value = true
  159. return
  160. }
  161. isAddGroupOpen.value = false
  162. loadingAddGroup.value = true
  163. try {
  164. await store.dispatch('addGroup', groupId)
  165. await router.push({
  166. name: 'group',
  167. params: {
  168. selectedGroup: encodeURIComponent(groupId),
  169. },
  170. })
  171. newGroupName.value = ''
  172. } catch {
  173. showError(t('settings', 'Failed to create group'))
  174. }
  175. loadingAddGroup.value = false
  176. }
  177. /**
  178. * Open the new-user form dialog
  179. */
  180. function showNewUserMenu() {
  181. store.commit('setShowConfig', {
  182. key: 'showNewUserForm',
  183. value: true,
  184. })
  185. }
  186. </script>
  187. <style scoped lang="scss">
  188. .account-management{
  189. &__system-list {
  190. height: auto !important;
  191. overflow: visible !important;
  192. }
  193. &__group-list {
  194. height: 100% !important;
  195. }
  196. &__settings-toggle {
  197. margin-bottom: 12px;
  198. }
  199. }
  200. </style>