123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824 |
- <!--
- - @copyright Copyright (c) 2018 John Molakvoæ <skjnldsv@protonmail.com>
- - @copyright Copyright (c) 2019 Gary Kim <gary@garykim.dev>
- -
- - @author John Molakvoæ <skjnldsv@protonmail.com>
- - @author Gary Kim <gary@garykim.dev>
- -
- - @license GNU AGPL version 3 or any later version
- -
- - This program is free software: you can redistribute it and/or modify
- - it under the terms of the GNU Affero General Public License as
- - published by the Free Software Foundation, either version 3 of the
- - License, or (at your option) any later version.
- -
- - This program is distributed in the hope that it will be useful,
- - but WITHOUT ANY WARRANTY; without even the implied warranty of
- - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- - GNU Affero General Public License for more details.
- -
- - You should have received a copy of the GNU Affero General Public License
- - along with this program. If not, see <http://www.gnu.org/licenses/>.
- -
- -->
- <template>
- <!-- Obfuscated user: Logged in user does not have permissions to see all of the data -->
- <div v-if="Object.keys(user).length ===1" :data-id="user.id" class="row">
- <div :class="{'icon-loading-small': loading.delete || loading.disable || loading.wipe}"
- class="avatar">
- <img v-if="!loading.delete && !loading.disable && !loading.wipe"
- :src="generateAvatar(user.id, isDarkTheme)"
- alt=""
- height="32"
- width="32">
- </div>
- <div class="name">
- {{ user.id }}
- </div>
- <div class="obfuscated">
- {{ t('settings','You do not have permissions to see the details of this user') }}
- </div>
- </div>
- <!-- User full data -->
- <UserRowSimple v-else-if="!editing"
- :editing.sync="editing"
- :feedback-message="feedbackMessage"
- :groups="groups"
- :languages="languages"
- :loading="loading"
- :opened-menu.sync="openedMenu"
- :settings="settings"
- :show-config="showConfig"
- :sub-admins-groups="subAdminsGroups"
- :user-actions="userActions"
- :user="user"
- :is-dark-theme="isDarkTheme"
- :class="{'row--menu-opened': openedMenu}" />
- <div v-else
- :class="{
- 'disabled': loading.delete || loading.disable,
- 'row--menu-opened': openedMenu
- }"
- :data-id="user.id"
- class="row row--editable">
- <div :class="{'icon-loading-small': loading.delete || loading.disable || loading.wipe}"
- class="avatar">
- <img v-if="!loading.delete && !loading.disable && !loading.wipe"
- :src="generateAvatar(user.id, isDarkTheme)"
- alt=""
- height="32"
- width="32">
- </div>
- <!-- dirty hack to ellipsis on two lines -->
- <div v-if="user.backendCapabilities.setDisplayName" class="displayName">
- <label class="hidden-visually" :for="'displayName'+user.id+rand">{{ t('settings', 'Edit display name') }}</label>
- <NcTextField :id="'displayName'+user.id+rand"
- :show-trailing-button="true"
- class="user-row-text-field"
- :class="{'icon-loading-small': loading.displayName}"
- :disabled="loading.displayName||loading.all"
- trailing-button-icon="arrowRight"
- :value.sync="editedDisplayName"
- autocapitalize="off"
- autocomplete="off"
- autocorrect="off"
- spellcheck="false"
- type="text"
- @trailing-button-click="updateDisplayName" />
- </div>
- <div v-else class="name">
- {{ user.id }}
- <div class="displayName subtitle">
- <div :title="user.displayname.length > 20 ? user.displayname : ''" class="cellText">
- {{ user.displayname }}
- </div>
- </div>
- </div>
- <div v-if="settings.canChangePassword && user.backendCapabilities.setPassword" class="password">
- <label class="hidden-visually" :for="'password'+user.id+rand">{{ t('settings', 'Add new password') }}</label>
- <NcTextField :id="'password'+user.id+rand"
- :show-trailing-button="true"
- class="user-row-text-field"
- :class="{'icon-loading-small': loading.password}"
- :disabled="loading.password || loading.all"
- :minlength="minPasswordLength"
- maxlength="469"
- :placeholder="t('settings', 'Add new password')"
- trailing-button-icon="arrowRight"
- :value.sync="editedPassword"
- autocapitalize="off"
- autocomplete="new-password"
- autocorrect="off"
- required
- spellcheck="false"
- type="password"
- @trailing-button-click="updatePassword" />
- </div>
- <div v-else />
- <div class="mailAddress">
- <label class="hidden-visually" :for="'mailAddress'+user.id+rand">{{ t('settings', 'Add new email address') }}</label>
- <NcTextField :id="'mailAddress'+user.id+rand"
- :show-trailing-button="true"
- class="user-row-text-field"
- :class="{'icon-loading-small': loading.mailAddress}"
- :disabled="loading.mailAddress||loading.all"
- :placeholder="t('settings', 'Add new email address')"
- trailing-button-icon="arrowRight"
- :value.sync="editedMail"
- autocapitalize="off"
- autocomplete="new-password"
- autocorrect="off"
- spellcheck="false"
- type="email"
- @trailing-button-click="updateEmail" />
- </div>
- <div :class="{'icon-loading-small': loading.groups}" class="groups">
- <label class="hidden-visually" :for="'groups'+user.id+rand">{{ t('settings', 'Add user to group') }}</label>
- <NcSelect :input-id="'groups'+user.id+rand"
- :close-on-select="false"
- :disabled="loading.groups||loading.all"
- :multiple="true"
- :options="availableGroups"
- :placeholder="t('settings', 'Add user to group')"
- :taggable="settings.isAdmin"
- :value="userGroups"
- class="select-vue"
- label="name"
- :no-wrap="true"
- :selectable="() => userGroups.length < 2"
- :create-option="(value) => ({ name: value, isCreating: true })"
- @option:created="createGroup"
- @option:selected="options => addUserGroup(options.at(-1))"
- @option:deselected="removeUserGroup" />
- </div>
- <div v-if="subAdminsGroups.length>0 && settings.isAdmin"
- :class="{'icon-loading-small': loading.subadmins}"
- class="subadmins">
- <label class="hidden-visually" :for="'subadmins'+user.id+rand">{{ t('settings', 'Set user as admin for') }}</label>
- <NcSelect :id="'subadmins'+user.id+rand"
- :close-on-select="false"
- :disabled="loading.subadmins||loading.all"
- label="name"
- :multiple="true"
- :no-wrap="true"
- :selectable="() => userSubAdminsGroups.length < 2"
- :options="subAdminsGroups"
- :placeholder="t('settings', 'Set user as admin for')"
- :value="userSubAdminsGroups"
- class="select-vue"
- @option:deselected="removeUserSubAdmin"
- @option:selected="options => addUserSubAdmin(options.at(-1))" />
- </div>
- <div :title="usedSpace"
- :class="{'icon-loading-small': loading.quota}"
- class="quota">
- <label class="hidden-visually" :for="'quota'+user.id+rand">{{ t('settings', 'Select user quota') }}</label>
- <NcSelect v-model="userQuota"
- :close-on-select="true"
- :create-option="validateQuota"
- :disabled="loading.quota||loading.all"
- :input-id="'quota'+user.id+rand"
- class="select-vue"
- :options="quotaOptions"
- :placeholder="t('settings', 'Select user quota')"
- :taggable="true"
- @option:selected="setUserQuota" />
- </div>
- <div v-if="showConfig.showLanguages"
- :class="{'icon-loading-small': loading.languages}"
- class="languages">
- <label class="hidden-visually" :for="'language'+user.id+rand">{{ t('settings', 'Set the language') }}</label>
- <NcSelect :id="'language'+user.id+rand"
- :allow-empty="false"
- :disabled="loading.languages||loading.all"
- :options="availableLanguages"
- :placeholder="t('settings', 'No language set')"
- :value="userLanguage"
- label="name"
- class="select-vue"
- @input="setUserLanguage" />
- </div>
- <div v-if="showConfig.showStoragePath || showConfig.showUserBackend"
- class="storageLocation" />
- <div v-if="showConfig.showLastLogin" />
- <div :class="{'icon-loading-small': loading.manager}" class="managers">
- <label class="hidden-visually" :for="'manager'+user.id+rand">{{ t('settings', 'Set the language') }}</label>
- <NcSelect v-model="currentManager"
- :input-id="'manager'+user.id+rand"
- :close-on-select="true"
- label="displayname"
- :options="possibleManagers"
- :placeholder="t('settings', 'Select manager')"
- class="select-vue"
- @search="searchUserManager"
- @option:selected="updateUserManager"
- @input="updateUserManager" />
- </div>
- <div class="userActions">
- <div v-if="!loading.all"
- class="toggleUserActions">
- <NcActions>
- <NcActionButton icon="icon-checkmark"
- :title="t('settings', 'Done')"
- :aria-label="t('settings', 'Done')"
- @click="handleDoneButton" />
- </NcActions>
- <div v-click-outside="hideMenu" class="userPopoverMenuWrapper">
- <button class="icon-more"
- :aria-expanded="openedMenu"
- :aria-label="t('settings', 'Toggle user actions menu')"
- @click.prevent="toggleMenu" />
- <div :class="{ 'open': openedMenu }" class="popovermenu">
- <NcPopoverMenu :menu="userActions" />
- </div>
- </div>
- </div>
- <div :style="{opacity: feedbackMessage !== '' ? 1 : 0}"
- class="feedback">
- <div class="icon-checkmark" />
- {{ feedbackMessage }}
- </div>
- </div>
- </div>
- </template>
- <script>
- import ClickOutside from 'vue-click-outside'
- import NcPopoverMenu from '@nextcloud/vue/dist/Components/NcPopoverMenu.js'
- import NcSelect from '@nextcloud/vue/dist/Components/NcSelect.js'
- import NcActions from '@nextcloud/vue/dist/Components/NcActions.js'
- import NcActionButton from '@nextcloud/vue/dist/Components/NcActionButton.js'
- import NcTextField from '@nextcloud/vue/dist/Components/NcTextField.js'
- import UserRowSimple from './UserRowSimple.vue'
- import UserRowMixin from '../../mixins/UserRowMixin.js'
- import { showSuccess, showError } from '@nextcloud/dialogs'
- export default {
- name: 'UserRow',
- components: {
- UserRowSimple,
- NcPopoverMenu,
- NcActions,
- NcActionButton,
- NcSelect,
- NcTextField,
- },
- directives: {
- ClickOutside,
- },
- mixins: [UserRowMixin],
- props: {
- users: {
- type: Array,
- required: true,
- },
- user: {
- type: Object,
- required: true,
- },
- settings: {
- type: Object,
- default: () => ({}),
- },
- groups: {
- type: Array,
- default: () => [],
- },
- subAdminsGroups: {
- type: Array,
- default: () => [],
- },
- quotaOptions: {
- type: Array,
- default: () => [],
- },
- showConfig: {
- type: Object,
- default: () => ({}),
- },
- languages: {
- type: Array,
- required: true,
- },
- externalActions: {
- type: Array,
- default: () => [],
- },
- isDarkTheme: {
- type: Boolean,
- required: true,
- },
- },
- data() {
- return {
- // default quota is set to unlimited
- unlimitedQuota: { id: 'none', label: t('settings', 'Unlimited') },
- // temporary value used for multiselect change
- selectedQuota: false,
- rand: parseInt(Math.random() * 1000),
- openedMenu: false,
- feedbackMessage: '',
- possibleManagers: [],
- currentManager: '',
- editing: false,
- loading: {
- all: false,
- displayName: false,
- password: false,
- mailAddress: false,
- groups: false,
- subadmins: false,
- quota: false,
- delete: false,
- disable: false,
- languages: false,
- wipe: false,
- manager: false,
- },
- editedDisplayName: this.user.displayname,
- editedPassword: '',
- editedMail: this.user.email ?? '',
- }
- },
- computed: {
- /* USER POPOVERMENU ACTIONS */
- userActions() {
- const actions = [
- {
- icon: 'icon-delete',
- text: t('settings', 'Delete user'),
- action: this.deleteUser,
- },
- {
- icon: 'icon-delete',
- text: t('settings', 'Wipe all devices'),
- action: this.wipeUserDevices,
- },
- {
- icon: this.user.enabled ? 'icon-close' : 'icon-add',
- text: this.user.enabled ? t('settings', 'Disable user') : t('settings', 'Enable user'),
- action: this.enableDisableUser,
- },
- ]
- if (this.user.email !== null && this.user.email !== '') {
- actions.push({
- icon: 'icon-mail',
- text: t('settings', 'Resend welcome email'),
- action: this.sendWelcomeMail,
- })
- }
- return actions.concat(this.externalActions)
- },
- // mapping saved values to objects
- userQuota: {
- get() {
- if (this.selectedQuota !== false) {
- return this.selectedQuota
- }
- if (this.settings.defaultQuota !== this.unlimitedQuota.id && OC.Util.computerFileSize(this.settings.defaultQuota) >= 0) {
- // if value is valid, let's map the quotaOptions or return custom quota
- return { id: this.settings.defaultQuota, label: this.settings.defaultQuota }
- }
- return this.unlimitedQuota // unlimited
- },
- set(quota) {
- this.selectedQuota = quota
- },
- },
- availableLanguages() {
- return this.languages[0].languages.concat(this.languages[1].languages)
- },
- },
- async beforeMount() {
- await this.searchUserManager()
- if (this.user.manager) {
- await this.initManager(this.user.manager)
- }
- },
- methods: {
- /* MENU HANDLING */
- toggleMenu() {
- this.openedMenu = !this.openedMenu
- },
- hideMenu() {
- this.openedMenu = false
- },
- wipeUserDevices() {
- const userid = this.user.id
- OC.dialogs.confirmDestructive(
- t('settings', 'In case of lost device or exiting the organization, this can remotely wipe the Nextcloud data from all devices associated with {userid}. Only works if the devices are connected to the internet.', { userid }),
- t('settings', 'Remote wipe of devices'),
- {
- type: OC.dialogs.YES_NO_BUTTONS,
- confirm: t('settings', 'Wipe {userid}\'s devices', { userid }),
- confirmClasses: 'error',
- cancel: t('settings', 'Cancel'),
- },
- (result) => {
- if (result) {
- this.loading.wipe = true
- this.loading.all = true
- this.$store.dispatch('wipeUserDevices', userid)
- .then(() => {
- this.loading.wipe = false
- this.loading.all = false
- })
- }
- },
- true
- )
- },
- filterManagers(managers) {
- return managers.filter((manager) => manager.id !== this.user.id)
- },
- async initManager(userId) {
- await this.$store.dispatch('getUser', userId).then(response => {
- this.currentManager = response?.data.ocs.data
- })
- },
- async searchUserManager(query) {
- await this.$store.dispatch('searchUsers', { offset: 0, limit: 10, search: query }).then(response => {
- const users = response?.data ? this.filterManagers(Object.values(response?.data.ocs.data.users)) : []
- if (users.length > 0) {
- this.possibleManagers = users
- }
- })
- },
- updateUserManager(manager) {
- if (manager === null) {
- this.currentManager = ''
- }
- this.loading.manager = true
- try {
- this.$store.dispatch('setUserData', {
- userid: this.user.id,
- key: 'manager',
- value: this.currentManager ? this.currentManager.id : '',
- })
- } catch (error) {
- showError(t('setting', 'Update of user manager was failed'))
- console.error(error)
- } finally {
- this.loading.manager = false
- }
- },
- deleteUser() {
- const userid = this.user.id
- OC.dialogs.confirmDestructive(
- t('settings', 'Fully delete {userid}\'s account including all their personal files, app data, etc.', { userid }),
- t('settings', 'Account deletion'),
- {
- type: OC.dialogs.YES_NO_BUTTONS,
- confirm: t('settings', 'Delete {userid}\'s account', { userid }),
- confirmClasses: 'error',
- cancel: t('settings', 'Cancel'),
- },
- (result) => {
- if (result) {
- this.loading.delete = true
- this.loading.all = true
- return this.$store.dispatch('deleteUser', userid)
- .then(() => {
- this.loading.delete = false
- this.loading.all = false
- })
- }
- },
- true
- )
- },
- enableDisableUser() {
- this.loading.delete = true
- this.loading.all = true
- const userid = this.user.id
- const enabled = !this.user.enabled
- return this.$store.dispatch('enableDisableUser', {
- userid,
- enabled,
- })
- .then(() => {
- this.loading.delete = false
- this.loading.all = false
- })
- },
- /**
- * Set user displayName
- *
- * @param {string} displayName The display name
- */
- updateDisplayName() {
- this.loading.displayName = true
- this.$store.dispatch('setUserData', {
- userid: this.user.id,
- key: 'displayname',
- value: this.editedDisplayName,
- }).then(() => {
- this.loading.displayName = false
- if (this.editedDisplayName === this.user.displayname) {
- showSuccess(t('setting', 'Display name was successfully changed'))
- }
- })
- },
- /**
- * Set user password
- *
- * @param {string} password The email address
- */
- updatePassword() {
- this.loading.password = true
- if (this.editedPassword.length === 0) {
- showError(t('setting', "Password can't be empty"))
- this.loading.password = false
- } else {
- this.$store.dispatch('setUserData', {
- userid: this.user.id,
- key: 'password',
- value: this.editedPassword,
- }).then(() => {
- this.loading.password = false
- this.editedPassword = ''
- showSuccess(t('setting', 'Password was successfully changed'))
- })
- }
- },
- /**
- * Set user mailAddress
- *
- * @param {string} mailAddress The email address
- */
- updateEmail() {
- this.loading.mailAddress = true
- if (this.editedMail === '') {
- showError(t('setting', "Email can't be empty"))
- this.loading.mailAddress = false
- this.editedMail = this.user.email
- } else {
- this.$store.dispatch('setUserData', {
- userid: this.user.id,
- key: 'email',
- value: this.editedMail,
- }).then(() => {
- this.loading.mailAddress = false
- if (this.editedMail === this.user.email) {
- showSuccess(t('setting', 'Email was successfully changed'))
- }
- })
- }
- },
- /**
- * Create a new group and add user to it
- *
- * @param {string} gid Group id
- */
- async createGroup({ name: gid }) {
- this.loading = { groups: true, subadmins: true }
- try {
- await this.$store.dispatch('addGroup', gid)
- const userid = this.user.id
- await this.$store.dispatch('addUserGroup', { userid, gid })
- } catch (error) {
- console.error(error)
- } finally {
- this.loading = { groups: false, subadmins: false }
- }
- return this.$store.getters.getGroups[this.groups.length]
- },
- /**
- * Add user to group
- *
- * @param {object} group Group object
- */
- async addUserGroup(group) {
- if (group.isCreating) {
- // This is NcSelect's internal value for a new inputted group name
- // Ignore
- return
- }
- this.loading.groups = true
- const userid = this.user.id
- const gid = group.id
- if (group.canAdd === false) {
- return false
- }
- try {
- await this.$store.dispatch('addUserGroup', { userid, gid })
- } catch (error) {
- console.error(error)
- } finally {
- this.loading.groups = false
- }
- },
- /**
- * Remove user from group
- *
- * @param {object} group Group object
- */
- async removeUserGroup(group) {
- if (group.canRemove === false) {
- return false
- }
- this.loading.groups = true
- const userid = this.user.id
- const gid = group.id
- try {
- await this.$store.dispatch('removeUserGroup', {
- userid,
- gid,
- })
- this.loading.groups = false
- // remove user from current list if current list is the removed group
- if (this.$route.params.selectedGroup === gid) {
- this.$store.commit('deleteUser', userid)
- }
- } catch {
- this.loading.groups = false
- }
- },
- /**
- * Add user to group
- *
- * @param {object} group Group object
- */
- async addUserSubAdmin(group) {
- this.loading.subadmins = true
- const userid = this.user.id
- const gid = group.id
- try {
- await this.$store.dispatch('addUserSubAdmin', {
- userid,
- gid,
- })
- this.loading.subadmins = false
- } catch (error) {
- console.error(error)
- }
- },
- /**
- * Remove user from group
- *
- * @param {object} group Group object
- */
- async removeUserSubAdmin(group) {
- this.loading.subadmins = true
- const userid = this.user.id
- const gid = group.id
- try {
- await this.$store.dispatch('removeUserSubAdmin', {
- userid,
- gid,
- })
- } catch (error) {
- console.error(error)
- } finally {
- this.loading.subadmins = false
- }
- },
- /**
- * Dispatch quota set request
- *
- * @param {string | object} quota Quota in readable format '5 GB' or Object {id: '5 GB', label: '5GB'}
- * @return {string}
- */
- async setUserQuota(quota = 'none') {
- // Make sure correct label is set for unlimited quota
- if (quota === 'none') {
- quota = this.unlimitedQuota
- }
- this.loading.quota = true
- // ensure we only send the preset id
- quota = quota.id ? quota.id : quota
- try {
- await this.$store.dispatch('setUserData', {
- userid: this.user.id,
- key: 'quota',
- value: quota,
- })
- } catch (error) {
- console.error(error)
- } finally {
- this.loading.quota = false
- }
- return quota
- },
- /**
- * Validate quota string to make sure it's a valid human file size
- *
- * @param {string | object} quota Quota in readable format '5 GB' or Object {id: '5 GB', label: '5GB'}
- * @return {object} The validated quota object or unlimited quota if input is invalid
- */
- validateQuota(quota) {
- if (typeof quota === 'object') {
- quota = quota?.id || quota.label
- }
- // only used for new presets sent through @Tag
- const validQuota = OC.Util.computerFileSize(quota)
- if (validQuota === null) {
- return this.unlimitedQuota
- } else {
- // unify format output
- quota = OC.Util.humanFileSize(OC.Util.computerFileSize(quota))
- return { id: quota, label: quota }
- }
- },
- /**
- * Dispatch language set request
- *
- * @param {object} lang language object {code:'en', name:'English'}
- * @return {object}
- */
- async setUserLanguage(lang) {
- this.loading.languages = true
- // ensure we only send the preset id
- try {
- await this.$store.dispatch('setUserData', {
- userid: this.user.id,
- key: 'language',
- value: lang.code,
- })
- this.loading.languages = false
- } catch (error) {
- console.error(error)
- }
- return lang
- },
- /**
- * Dispatch new welcome mail request
- */
- sendWelcomeMail() {
- this.loading.all = true
- this.$store.dispatch('sendWelcomeMail', this.user.id)
- .then(success => {
- if (success) {
- // Show feedback to indicate the success
- this.feedbackMessage = t('setting', 'Welcome mail sent!')
- setTimeout(() => {
- this.feedbackMessage = ''
- }, 2000)
- }
- this.loading.all = false
- })
- },
- handleDoneButton() {
- this.editing = false
- if (this.editedDisplayName !== this.user.displayname) {
- this.editedDisplayName = this.user.displayname
- } else if (this.editedMail !== this.user.email) {
- this.editedMail = this.user.email
- }
- },
- },
- }
- </script>
- <style scoped lang="scss">
- // Force menu to be above other rows
- .row--menu-opened {
- z-index: 1 !important;
- }
- .row :deep() {
- .mailAddress,
- .password,
- .displayName {
- .input-field,
- .input-field__input {
- height: 48px!important;
- }
- .button-vue--icon-only {
- height: 44px!important;
- }
- }
- }
- </style>
|