@craftercms/studio-ui
Version:
Services, components, models & utils to build CrafterCMS authoring extensions.
439 lines (437 loc) • 17.5 kB
JavaScript
/*
* 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;