UNPKG

@craftercms/studio-ui

Version:

Services, components, models & utils to build CrafterCMS authoring extensions.

439 lines (437 loc) 17.5 kB
/* * Copyright (C) 2007-2022 Crafter Software Corporation. All Rights Reserved. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 as published by * the Free Software Foundation. * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ /* * Copyright (C) 2007-2022 Crafter Software Corporation. All Rights Reserved. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as published by * the Free Software Foundation. * * 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ import useStyles from './styles'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; import Avatar from '@mui/material/Avatar'; import Typography from '@mui/material/Typography'; import Tooltip from '@mui/material/Tooltip'; import IconButton from '@mui/material/IconButton'; import PasswordRoundedIcon from '@mui/icons-material/VpnKeyRounded'; import ConfirmDropdown from '../ConfirmDropdown'; import DeleteRoundedIcon from '@mui/icons-material/DeleteRounded'; import CloseRoundedIcon from '@mui/icons-material/CloseRounded'; import Divider from '@mui/material/Divider'; import DialogBody from '../DialogBody/DialogBody'; import Switch from '@mui/material/Switch'; import Chip from '@mui/material/Chip'; import SecondaryButton from '../SecondaryButton'; import PrimaryButton from '../PrimaryButton'; import Grid from '@mui/material/Grid'; import Skeleton from '@mui/material/Skeleton'; import { rand } from '../PathNavigator/utils'; import ResetPasswordDialog from '../ResetPasswordDialog'; import * as React from 'react'; import InputLabel from '@mui/material/InputLabel'; import { UserGroupMembershipEditor } from '../UserGroupMembershipEditor'; import TextField from '@mui/material/TextField'; import { isInvalidEmail, USER_EMAIL_MAX_LENGTH, USER_FIRST_NAME_MAX_LENGTH, USER_FIRST_NAME_MIN_LENGTH, USER_LAST_NAME_MAX_LENGTH, USER_LAST_NAME_MIN_LENGTH, validateFieldMinLength, validateRequiredField } from '../UserManagement/utils'; const translations = defineMessages({ externallyManaged: { id: 'userInfoDialog.externallyManaged', defaultMessage: 'Managed externally' }, siteName: { id: 'userInfoDialog.siteName', defaultMessage: 'Project name' }, roles: { id: 'words.roles', defaultMessage: 'Roles' }, confirmHelperText: { id: 'userInfoDialog.helperText', defaultMessage: 'Delete user "{username}"?' }, confirmOk: { id: 'words.yes', defaultMessage: 'Yes' }, confirmCancel: { id: 'words.no', defaultMessage: 'No' }, invalidMinLength: { id: 'userInfoDialog.invalidMinLength', defaultMessage: 'Min {length} characters' } }); export function EditUserDialogUI(props) { const { classes } = useStyles(); const { formatMessage } = useIntl(); const managedInStudio = !props.user.externallyManaged; const { user, inProgress, submitOk, dirty, openResetPassword, sites, rolesBySite, passwordRequirementsMinComplexity, onSave, onCloseButtonClick, onDelete, onCloseResetPasswordDialog, onInputChange, onEnableChange, onCancelForm, onResetPassword } = props; return React.createElement( React.Fragment, null, React.createElement( 'header', { className: classes.header }, React.createElement( Avatar, { className: classes.avatar }, user.firstName.charAt(0), user.lastName?.charAt(0) ?? '' ), React.createElement( 'section', { className: classes.userInfo }, React.createElement(Typography, { variant: 'h6', component: 'h2' }, user.firstName, ' ', user.lastName), React.createElement(Typography, { variant: 'subtitle1', noWrap: true, title: user.username }, user.username) ), React.createElement( 'section', { className: classes.actions }, managedInStudio ? React.createElement( React.Fragment, null, React.createElement( Tooltip, { title: React.createElement(FormattedMessage, { id: 'userInfoDialog.resetPassword', defaultMessage: 'Reset password' }) }, React.createElement( IconButton, { onClick: () => onResetPassword(true), size: 'large' }, React.createElement(PasswordRoundedIcon, null) ) ), React.createElement(ConfirmDropdown, { cancelText: formatMessage(translations.confirmCancel), confirmText: formatMessage(translations.confirmOk), confirmHelperText: formatMessage(translations.confirmHelperText, { username: user.username }), iconTooltip: React.createElement(FormattedMessage, { id: 'userInfoDialog.deleteUser', defaultMessage: 'Delete user' }), icon: DeleteRoundedIcon, onConfirm: () => { onDelete(user.username); } }) ) : React.createElement(Chip, { label: formatMessage(translations.externallyManaged), size: 'small', className: classes.chip }), React.createElement( Tooltip, { title: React.createElement(FormattedMessage, { id: 'userInfoDialog.close', defaultMessage: 'Close' }) }, React.createElement( IconButton, { edge: 'end', onClick: onCloseButtonClick, size: 'large' }, React.createElement(CloseRoundedIcon, null) ) ) ) ), React.createElement(Divider, null), React.createElement( DialogBody, { className: classes.body }, React.createElement( Grid, { container: true }, React.createElement( Grid, { item: true, sm: 6 }, React.createElement( 'section', { className: classes.section }, React.createElement( Typography, { variant: 'subtitle1', className: classes.sectionTitle }, React.createElement(FormattedMessage, { id: 'userInfoDialog.userDetails', defaultMessage: 'User Details' }) ), React.createElement( 'form', null, React.createElement( 'div', { className: classes.row }, React.createElement( Typography, { color: 'textSecondary', className: classes.label }, React.createElement(FormattedMessage, { id: 'words.enabled', defaultMessage: 'Enabled' }) ), React.createElement( 'div', { className: classes.switchWrapper }, React.createElement(Switch, { disabled: !managedInStudio, checked: user.enabled, onChange: (e) => onEnableChange({ enabled: e.target.checked }), color: 'primary', name: 'enabled', inputProps: { 'aria-label': 'enabled checkbox' } }) ) ), React.createElement(Divider, null), React.createElement( 'div', { className: classes.row }, React.createElement( Typography, { color: 'textSecondary', className: classes.label }, React.createElement(FormattedMessage, { id: 'words.username', defaultMessage: 'Username' }) ), React.createElement( 'section', { className: classes.userNameWrapper }, React.createElement(Typography, { noWrap: true, title: user.username }, user.username) ) ), React.createElement( 'div', { className: classes.row }, React.createElement( InputLabel, { htmlFor: 'firstName', className: classes.label }, React.createElement( Typography, { color: 'textSecondary' }, React.createElement(FormattedMessage, { id: 'words.firstName', defaultMessage: 'First name' }) ) ), managedInStudio ? React.createElement(TextField, { id: 'firstName', onChange: (e) => onInputChange({ firstName: e.currentTarget.value }), inputProps: { maxLength: USER_FIRST_NAME_MAX_LENGTH }, value: user.firstName, fullWidth: true, error: validateRequiredField(user.firstName) || validateFieldMinLength('firstName', user.firstName), helperText: validateRequiredField(user.firstName) ? React.createElement(FormattedMessage, { id: 'editUserDialog.firstNameRequired', defaultMessage: 'First Name is required' }) : validateFieldMinLength('firstName', user.firstName) ? formatMessage(translations.invalidMinLength, { length: USER_FIRST_NAME_MIN_LENGTH }) : null }) : React.createElement(Typography, { className: classes.userNameWrapper, children: user.firstName }) ), React.createElement( 'div', { className: classes.row }, React.createElement( InputLabel, { htmlFor: 'lastName', className: classes.label }, React.createElement( Typography, { color: 'textSecondary' }, React.createElement(FormattedMessage, { id: 'words.lastName', defaultMessage: 'Last name' }) ) ), managedInStudio ? React.createElement(TextField, { id: 'lastName', onChange: (e) => onInputChange({ lastName: e.currentTarget.value }), inputProps: { maxLength: USER_LAST_NAME_MAX_LENGTH }, value: user.lastName, fullWidth: true, error: validateRequiredField(user.lastName) || validateFieldMinLength('lastName', user.lastName), helperText: validateRequiredField(user.lastName) ? React.createElement(FormattedMessage, { id: 'editUserDialog.lastNameRequired', defaultMessage: 'Last Name is required' }) : validateFieldMinLength('lastName', user.lastName) ? formatMessage(translations.invalidMinLength, { length: USER_LAST_NAME_MIN_LENGTH }) : null }) : React.createElement(Typography, { className: classes.userNameWrapper, children: user.lastName }) ), React.createElement( 'div', { className: classes.row }, React.createElement( InputLabel, { htmlFor: 'email', className: classes.label }, React.createElement( Typography, { color: 'textSecondary' }, React.createElement(FormattedMessage, { id: 'words.email', defaultMessage: 'E-mail' }) ) ), managedInStudio ? React.createElement(TextField, { id: 'email', onChange: (e) => onInputChange({ email: e.currentTarget.value }), value: user.email, error: validateRequiredField(user.email) || isInvalidEmail(user.email), fullWidth: true, helperText: validateRequiredField(user.email) ? React.createElement(FormattedMessage, { id: 'editUserDialog.emailRequired', defaultMessage: 'Email is required' }) : isInvalidEmail(user.email) ? React.createElement(FormattedMessage, { id: 'editUserDialog.invalidEmail', defaultMessage: 'Email is invalid' }) : null, inputProps: { maxLength: USER_EMAIL_MAX_LENGTH } }) : React.createElement(Typography, { className: classes.userNameWrapper, children: user.email }) ), managedInStudio && React.createElement( 'div', { className: classes.formActions }, React.createElement( SecondaryButton, { disabled: !dirty || inProgress, onClick: onCancelForm }, React.createElement(FormattedMessage, { id: 'words.cancel', defaultMessage: 'Cancel' }) ), React.createElement( PrimaryButton, { disabled: !dirty || !submitOk || inProgress, onClick: onSave, loading: inProgress }, React.createElement(FormattedMessage, { id: 'words.save', defaultMessage: 'Save' }) ) ) ) ) ), React.createElement( Grid, { item: true, sm: 6 }, React.createElement( 'section', { className: classes.section }, React.createElement(UserGroupMembershipEditor, { username: user.username }) ) ) ), React.createElement(Divider, null), React.createElement( 'section', { className: classes.section }, React.createElement( Typography, { variant: 'subtitle1', className: classes.sectionTitle }, React.createElement(FormattedMessage, { id: 'userInfoDialog.siteRoles', defaultMessage: 'Roles per project' }) ), React.createElement( Grid, { container: true, spacing: 2 }, React.createElement( Grid, { item: true, xs: 4 }, sites.map((site) => React.createElement( Typography, { key: site.id, variant: 'body2', className: classes.siteItem }, site.name ) ) ), React.createElement( Grid, { item: true, xs: 8 }, sites.map((site, i) => rolesBySite[site.id] ? rolesBySite[site.id].length ? React.createElement( Typography, { key: site.id, variant: 'body2', className: classes.siteItem }, rolesBySite[site.id].join(', ') ) : React.createElement( Typography, { key: site.id, variant: 'body2', color: 'textSecondary', className: classes.siteItem }, '(', React.createElement(FormattedMessage, { id: 'userInfoDialog.noRoles', defaultMessage: 'No roles' }), ')' ) : React.createElement(Skeleton, { key: i, variant: 'text', className: classes.siteItem, style: { width: `${rand(50, 90)}%` } }) ) ) ) ) ), managedInStudio && React.createElement(ResetPasswordDialog, { open: openResetPassword, passwordRequirementsMinComplexity: passwordRequirementsMinComplexity, user: user, onClose: onCloseResetPasswordDialog }) ); } export default EditUserDialogUI;