Merge pull request #45315 from nextcloud/fix/new-user-dialog

fix(settings): Move new user modal to dialog + minor refactoring
This commit is contained in:
Pytal 2024-05-30 17:40:19 -07:00 committed by GitHub
commit 06878535c4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 90 additions and 82 deletions

View file

@ -22,12 +22,12 @@
<template>
<Fragment>
<NewUserModal v-if="showConfig.showNewUserForm"
<NewUserDialog v-if="showConfig.showNewUserForm"
:loading="loading"
:new-user="newUser"
:quota-options="quotaOptions"
@reset="resetForm"
@close="closeModal" />
@closing="closeDialog" />
<NcEmptyContent v-if="filteredUsers.length === 0"
class="empty"
@ -88,7 +88,7 @@ import NcIconSvgWrapper from '@nextcloud/vue/dist/Components/NcIconSvgWrapper.js
import NcLoadingIcon from '@nextcloud/vue/dist/Components/NcLoadingIcon.js'
import VirtualList from './Users/VirtualList.vue'
import NewUserModal from './Users/NewUserModal.vue'
import NewUserDialog from './Users/NewUserDialog.vue'
import UserListFooter from './Users/UserListFooter.vue'
import UserListHeader from './Users/UserListHeader.vue'
import UserRow from './Users/UserRow.vue'
@ -119,7 +119,7 @@ export default {
NcEmptyContent,
NcIconSvgWrapper,
NcLoadingIcon,
NewUserModal,
NewUserDialog,
UserListFooter,
UserListHeader,
VirtualList,
@ -332,7 +332,7 @@ export default {
this.isInitialLoad = false
},
closeModal() {
closeDialog() {
this.$store.commit('setShowConfig', {
key: 'showNewUserForm',
value: false,

View file

@ -21,16 +21,18 @@
-->
<template>
<NcModal class="modal"
<NcDialog class="dialog"
size="small"
:name="t('settings', 'New account')"
out-transition
v-on="$listeners">
<form class="modal__form"
<form id="new-user-form"
class="dialog__form"
data-test="form"
:disabled="loading.all"
@submit.prevent="createUser">
<h2>{{ t('settings', 'New user') }}</h2>
<NcTextField ref="username"
class="modal__item"
class="dialog__item"
data-test="username"
:value.sync="newUser.id"
:disabled="settings.newUserGenerateUserID"
@ -40,7 +42,7 @@
spellcheck="false"
pattern="[a-zA-Z0-9 _\.@\-']+"
required />
<NcTextField class="modal__item"
<NcTextField class="dialog__item"
data-test="displayName"
:value.sync="newUser.displayName"
:label="t('settings', 'Display name')"
@ -49,11 +51,11 @@
spellcheck="false" />
<span v-if="!settings.newUserRequireEmail"
id="password-email-hint"
class="modal__hint">
class="dialog__hint">
{{ t('settings', 'Either password or email is required') }}
</span>
<NcPasswordField ref="password"
class="modal__item"
class="dialog__item"
data-test="password"
:value.sync="newUser.password"
:minlength="minPasswordLength"
@ -64,7 +66,7 @@
autocomplete="new-password"
spellcheck="false"
:required="newUser.mailAddress === ''" />
<NcTextField class="modal__item"
<NcTextField class="dialog__item"
data-test="email"
type="email"
:value.sync="newUser.mailAddress"
@ -74,14 +76,10 @@
autocomplete="off"
spellcheck="false"
:required="newUser.password === '' || settings.newUserRequireEmail" />
<div class="modal__item">
<label class="modal__label"
for="new-user-groups">
{{ !settings.isAdmin ? t('settings', 'Groups (required)') : t('settings', 'Groups') }}
</label>
<NcSelect class="modal__select"
input-id="new-user-groups"
:placeholder="t('settings', 'Set user groups')"
<div class="dialog__item">
<NcSelect class="dialog__select"
:input-label="!settings.isAdmin ? t('settings', 'Groups (required)') : t('settings', 'Groups')"
:placeholder="t('settings', 'Set account groups')"
:disabled="loading.groups || loading.all"
:options="canAddGroups"
:value="newUser.groups"
@ -97,43 +95,31 @@
Therefore, empty select is forbidden -->
</div>
<div v-if="subAdminsGroups.length > 0"
class="modal__item">
<label class="modal__label"
for="new-user-sub-admin">
{{ t('settings', 'Administered groups') }}
</label>
class="dialog__item">
<NcSelect v-model="newUser.subAdminsGroups"
class="modal__select"
input-id="new-user-sub-admin"
:placeholder="t('settings', 'Set user as admin for …')"
class="dialog__select"
:input-label="t('settings', 'Administered groups')"
:placeholder="t('settings', 'Set account as admin for …')"
:options="subAdminsGroups"
:close-on-select="false"
:multiple="true"
label="name" />
</div>
<div class="modal__item">
<label class="modal__label"
for="new-user-quota">
{{ t('settings', 'Quota') }}
</label>
<div class="dialog__item">
<NcSelect v-model="newUser.quota"
class="modal__select"
input-id="new-user-quota"
:placeholder="t('settings', 'Set user quota')"
class="dialog__select"
:input-label="t('settings', 'Quota')"
:placeholder="t('settings', 'Set account quota')"
:options="quotaOptions"
:clearable="false"
:taggable="true"
:create-option="validateQuota" />
</div>
<div v-if="showConfig.showLanguages"
class="modal__item">
<label class="modal__label"
for="new-user-language">
{{ t('settings', 'Language') }}
</label>
class="dialog__item">
<NcSelect v-model="newUser.language"
class="modal__select"
input-id="new-user-language"
class="dialog__select"
:input-label="t('settings', 'Language')"
:placeholder="t('settings', 'Set default language')"
:clearable="false"
:selectable="option => !option.languages"
@ -141,44 +127,43 @@
:options="languages"
label="name" />
</div>
<div :class="['modal__item managers', { 'icon-loading-small': loading.manager }]">
<label class="modal__label"
for="new-user-manager">
<!-- TRANSLATORS This string describes a manager in the context of an organization -->
{{ t('settings', 'Manager') }}
</label>
<div :class="['dialog__item dialog__managers', { 'icon-loading-small': loading.manager }]">
<NcSelect v-model="newUser.manager"
class="modal__select"
input-id="new-user-manager"
class="dialog__select"
:input-label="managerInputLabel"
:placeholder="managerLabel"
:options="possibleManagers"
:user-select="true"
label="displayname"
@search="searchUserManager" />
</div>
<NcButton class="modal__submit"
</form>
<template #actions>
<NcButton class="dialog__submit"
data-test="submit"
form="new-user-form"
type="primary"
native-type="submit">
{{ t('settings', 'Add new user') }}
{{ t('settings', 'Add new account') }}
</NcButton>
</form>
</NcModal>
</template>
</NcDialog>
</template>
<script>
import NcButton from '@nextcloud/vue/dist/Components/NcButton.js'
import NcModal from '@nextcloud/vue/dist/Components/NcModal.js'
import NcDialog from '@nextcloud/vue/dist/Components/NcDialog.js'
import NcPasswordField from '@nextcloud/vue/dist/Components/NcPasswordField.js'
import NcSelect from '@nextcloud/vue/dist/Components/NcSelect.js'
import NcTextField from '@nextcloud/vue/dist/Components/NcTextField.js'
export default {
name: 'NewUserModal',
name: 'NewUserDialog',
components: {
NcButton,
NcModal,
NcDialog,
NcPasswordField,
NcSelect,
NcTextField,
@ -205,7 +190,9 @@ export default {
return {
possibleManagers: [],
// TRANSLATORS This string describes a manager in the context of an organization
managerLabel: t('settings', 'Set user manager'),
managerInputLabel: t('settings', 'Manager'),
// TRANSLATORS This string describes a manager in the context of an organization
managerLabel: t('settings', 'Set account manager'),
}
},
@ -272,6 +259,10 @@ export default {
await this.searchUserManager()
},
mounted() {
this.$refs.username?.focus?.()
},
methods: {
async createUser() {
this.loading.all = true
@ -289,18 +280,18 @@ export default {
})
this.$emit('reset')
this.$refs.username?.$refs?.inputField?.$refs?.input?.focus?.()
this.$emit('close')
this.$refs.username?.focus?.()
this.$emit('closing')
} catch (error) {
this.loading.all = false
if (error.response && error.response.data && error.response.data.ocs && error.response.data.ocs.meta) {
const statuscode = error.response.data.ocs.meta.statuscode
if (statuscode === 102) {
// wrong username
this.$refs.username?.$refs?.inputField?.$refs?.input?.focus?.()
this.$refs.username?.focus?.()
} else if (statuscode === 107) {
// wrong password
this.$refs.password?.$refs?.inputField?.$refs?.input?.focus?.()
this.$refs.password?.focus?.()
}
}
}
@ -383,12 +374,12 @@ export default {
</script>
<style lang="scss" scoped>
.modal {
.dialog {
&__form {
display: flex;
flex-direction: column;
align-items: center;
padding: 20px;
padding: 0 8px;
gap: 4px 0;
}
@ -415,8 +406,19 @@ export default {
width: 100%;
}
&__managers {
margin-bottom: 12px;
}
&__submit {
margin-top: 20px;
margin-top: 4px;
margin-bottom: 8px;
}
:deep {
.dialog__actions {
margin: auto;
}
}
}
</style>

View file

@ -28,7 +28,7 @@
</th>
<td class="footer__cell footer__cell--loading">
<NcLoadingIcon v-if="loading"
:title="t('settings', 'Loading users …')"
:title="t('settings', 'Loading accounts …')"
:size="32" />
</td>
<td class="footer__cell footer__cell--count footer__cell--multiline">
@ -73,8 +73,8 @@ export default Vue.extend({
if (this.loading) {
return this.n(
'settings',
'{userCount} user …',
'{userCount} users …',
'{userCount} account …',
'{userCount} accounts …',
this.filteredUsers.length,
{
userCount: this.filteredUsers.length,
@ -83,8 +83,8 @@ export default Vue.extend({
}
return this.n(
'settings',
'{userCount} user',
'{userCount} users',
'{userCount} account',
'{userCount} accounts',
this.filteredUsers.length,
{
userCount: this.filteredUsers.length,

View file

@ -104,7 +104,7 @@
data-cy-user-list-header-actions
scope="col">
<span class="hidden-visually">
{{ t('settings', 'User actions') }}
{{ t('settings', 'Account actions') }}
</span>
</th>
</tr>

View file

@ -120,7 +120,7 @@
<template v-if="editing">
<label class="hidden-visually"
:for="'groups' + uniqueId">
{{ t('settings', 'Add user to group') }}
{{ t('settings', 'Add account to group') }}
</label>
<NcSelect data-cy-user-list-input-groups
:data-loading="loading.groups || undefined"

View file

@ -39,6 +39,9 @@ describe('Settings: Create and delete accounts', function() {
cy.get('input[type="password"]').type(john.password)
// see that the password is 123456
cy.get('input[type="password"]').should('have.value', john.password)
})
cy.get('form[data-test="form"]').parents('[role="dialog"]').within(() => {
// submit the new user form
cy.get('button[type="submit"]').click({ force: true })
})
@ -73,6 +76,9 @@ describe('Settings: Create and delete accounts', function() {
cy.get('input[type="password"]').should('exist').and('have.value', '')
cy.get('input[type="password"]').type(john.password)
cy.get('input[type="password"]').should('have.value', john.password)
})
cy.get('form[data-test="form"]').parents('[role="dialog"]').within(() => {
// submit the new user form
cy.get('button[type="submit"]').click({ force: true })
})

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long