diff --git a/core/i18n/resources/en/common.js b/core/i18n/resources/en/common.js index ede3fe7107..0427538e88 100644 --- a/core/i18n/resources/en/common.js +++ b/core/i18n/resources/en/common.js @@ -898,6 +898,7 @@ Copy the invitation link to the clipboard?`, }, surveysDraft: 'Surveys (draft)', surveysPublished: 'Surveys (published)', + editSurveyUserExtraPropsForUser: 'Edit survey user extra properties for user "{{userName}}"', }, usersAccessRequestView: { diff --git a/webapp/views/App/views/Users/UserEdit/UserAuthGroupExtraPropsEditor/UserAuthGroupExtraPropsEditor.js b/webapp/views/App/views/Users/UserEdit/UserAuthGroupExtraPropsEditor/UserAuthGroupExtraPropsEditor.js index a5e20af328..7cbe47d575 100644 --- a/webapp/views/App/views/Users/UserEdit/UserAuthGroupExtraPropsEditor/UserAuthGroupExtraPropsEditor.js +++ b/webapp/views/App/views/Users/UserEdit/UserAuthGroupExtraPropsEditor/UserAuthGroupExtraPropsEditor.js @@ -7,14 +7,14 @@ import * as Survey from '@core/survey/survey' import { ExtraPropDef } from '@core/survey/extraPropDef' import * as User from '@core/user/user' -import { ExpansionPanel } from '@webapp/components' import { FormItem, Input, NumberFormats } from '@webapp/components/form/Input' import { useSurveyInfo } from '@webapp/store/survey' import { useAuthCanEditSurvey } from '@webapp/store/user' +import { ButtonSave } from '@webapp/components' export const UserAuthGroupExtraPropsEditor = (props) => { - const { onChange, userToUpdate } = props + const { onChange, onSave, userToUpdate } = props const canEditSurvey = useAuthCanEditSurvey() const surveyInfo = useSurveyInfo() @@ -41,26 +41,24 @@ export const UserAuthGroupExtraPropsEditor = (props) => { const readOnly = !canEditSurvey return ( - +
{extraDefsArray.map(({ name, dataType }) => ( ))} - + {onSave && } +
) } UserAuthGroupExtraPropsEditor.propTypes = { onChange: PropTypes.func.isRequired, + onSave: PropTypes.func, userToUpdate: PropTypes.object.isRequired, } diff --git a/webapp/views/App/views/Users/UserEdit/UserEdit.js b/webapp/views/App/views/Users/UserEdit/UserEdit.js index 1bb4d65288..381554dfca 100644 --- a/webapp/views/App/views/Users/UserEdit/UserEdit.js +++ b/webapp/views/App/views/Users/UserEdit/UserEdit.js @@ -3,28 +3,30 @@ import './UserEdit.scss' import React from 'react' import { useNavigate, useParams } from 'react-router' +import { Objects } from '@openforis/arena-core' + +import * as AuthGroup from '@core/auth/authGroup' +import * as ProcessUtils from '@core/processUtils' import * as Survey from '@core/survey/survey' import * as User from '@core/user/user' import * as Validation from '@core/validation/validation' -import * as AuthGroup from '@core/auth/authGroup' -import * as ProcessUtils from '@core/processUtils' -import ProfilePicture from '@webapp/components/profilePicture' -import { FormItem, Input, NumberFormats } from '@webapp/components/form/Input' +import { Button, ButtonDelete, ButtonInvite, ButtonSave, ExpansionPanel } from '@webapp/components' import Checkbox from '@webapp/components/form/checkbox' import DropdownUserTitle from '@webapp/components/form/DropdownUserTitle' -import { ButtonSave, ButtonDelete, ButtonInvite, Button } from '@webapp/components' +import { FormItem, Input, NumberFormats } from '@webapp/components/form/Input' +import ProfilePicture from '@webapp/components/profilePicture' import { appModuleUri, userModules } from '@webapp/app/appModules' import { useSurveyInfo } from '@webapp/store/survey' import { useI18n } from '@webapp/store/system' import { useAuthCanUseMap } from '@webapp/store/user/hooks' -import { useEditUser } from './store' import DropdownUserGroup from '../DropdownUserGroup' import ProfilePictureEditor from './ProfilePictureEditor' -import { UserExtraPropsEditor } from './UserExtraPropsEditor' +import { useEditUser } from './store' import { UserAuthGroupExtraPropsEditor } from './UserAuthGroupExtraPropsEditor/UserAuthGroupExtraPropsEditor' +import { UserExtraPropsEditor } from './UserExtraPropsEditor' const UserEdit = () => { const { userUuid } = useParams() @@ -167,7 +169,13 @@ const UserEdit = () => { )} {ProcessUtils.ENV.experimentalFeatures && ( - + + + )} )} diff --git a/webapp/views/App/views/Users/UserEdit/UserEdit.scss b/webapp/views/App/views/Users/UserEdit/UserEdit.scss index cabecc84f6..e38c5644e8 100644 --- a/webapp/views/App/views/Users/UserEdit/UserEdit.scss +++ b/webapp/views/App/views/Users/UserEdit/UserEdit.scss @@ -32,9 +32,18 @@ } .extra-props { - .form-item { - display: flex; - white-space: nowrap; + .form { + height: auto; + grid-row-gap: 0; + + .form-item { + width: 20rem; + white-space: nowrap; + + .form-input-container { + width: 12rem; + } + } } .btn-add { justify-self: center; diff --git a/webapp/views/App/views/Users/UserEdit/UserExtraPropsEditor/UserExtraPropEditor.js b/webapp/views/App/views/Users/UserEdit/UserExtraPropsEditor/UserExtraPropEditor.js index 0999da5e3d..34d34479e4 100644 --- a/webapp/views/App/views/Users/UserEdit/UserExtraPropsEditor/UserExtraPropEditor.js +++ b/webapp/views/App/views/Users/UserEdit/UserExtraPropsEditor/UserExtraPropEditor.js @@ -101,7 +101,7 @@ export const UserExtraPropEditor = (props) => { }, [initialItem, newItem, onDelete, onEditChange]) return ( -
+
{ return ( - {items.map(({ name, newItem, uuid, value }, index) => ( - + {items.map(({ name, newItem, uuid, value }, index) => ( + + ))} + item.newItem)} + label="extraProp.addExtraProp" + onClick={onAdd} /> - ))} - item.newItem)} - label="extraProp.addExtraProp" - onClick={onAdd} - /> +
) } diff --git a/webapp/views/App/views/Users/UserEdit/store/actions/useOnSave.js b/webapp/views/App/views/Users/UserEdit/store/actions/useOnSave.js index 2de42234cc..55340b40ce 100644 --- a/webapp/views/App/views/Users/UserEdit/store/actions/useOnSave.js +++ b/webapp/views/App/views/Users/UserEdit/store/actions/useOnSave.js @@ -14,13 +14,13 @@ import { useSurveyId } from '@webapp/store/survey' import { validateUserEdit } from './validate' -export const useOnSave = ({ userToUpdate, userToUpdateOriginal, setUserToUpdateOriginal }) => { +export const useOnSave = ({ userToUpdate, userToUpdateOriginal = null, setUserToUpdateOriginal = null }) => { const dispatch = useDispatch() const { hideSurveyGroup } = useQuery() const user = useUser() const surveyId = useSurveyId() - const saveUser = async () => { + const saveUser = useCallback(async () => { const editingSelf = User.isEqual(user)(userToUpdate) const userToUpdateUuid = User.getUuid(userToUpdate) const profilePicture = User.getProfilePicture(userToUpdate) @@ -61,17 +61,17 @@ export const useOnSave = ({ userToUpdate, userToUpdateOriginal, setUserToUpdateO params: { name: User.getName(userToUpdate) }, }) ) - setUserToUpdateOriginal(userToUpdate) + setUserToUpdateOriginal?.(userToUpdate) } finally { dispatch(LoaderActions.hideLoader()) } - } + }, [dispatch, hideSurveyGroup, setUserToUpdateOriginal, surveyId, user, userToUpdate]) return useCallback(async () => { const userUpdatedValidated = await validateUserEdit(userToUpdate) if (Validation.isObjValid(userUpdatedValidated)) { - if (User.isSystemAdmin(userToUpdate) && !User.isSystemAdmin(userToUpdateOriginal)) { + if (userToUpdateOriginal && User.isSystemAdmin(userToUpdate) && !User.isSystemAdmin(userToUpdateOriginal)) { dispatch( DialogConfirmActions.showDialogConfirm({ key: 'usersView.confirmUserWillBeSystemAdmin', @@ -82,5 +82,5 @@ export const useOnSave = ({ userToUpdate, userToUpdateOriginal, setUserToUpdateO await saveUser() } } - }, [userToUpdate, userToUpdateOriginal]) + }, [dispatch, saveUser, userToUpdate, userToUpdateOriginal]) } diff --git a/webapp/views/App/views/Users/UserEdit/store/useEditUser.js b/webapp/views/App/views/Users/UserEdit/store/useEditUser.js index 5cc3405700..fd6222a5b4 100644 --- a/webapp/views/App/views/Users/UserEdit/store/useEditUser.js +++ b/webapp/views/App/views/Users/UserEdit/store/useEditUser.js @@ -87,14 +87,17 @@ export const useEditUser = ({ userUuid }) => { onUpdate(userUpdated) } - const onMapApiKeyTest = useCallback(async ({ provider, apiKey }) => { - const success = await API.testMapApiKey({ provider, apiKey }) - if (success) { - dispatch(NotificationActions.notifyInfo({ key: 'user.mapApiKeys.keyIsCorrect' })) - } else { - dispatch(NotificationActions.notifyError({ key: 'user.mapApiKeys.keyIsNotCorrect' })) - } - }, []) + const onMapApiKeyTest = useCallback( + async ({ provider, apiKey }) => { + const success = await API.testMapApiKey({ provider, apiKey }) + if (success) { + dispatch(NotificationActions.notifyInfo({ key: 'user.mapApiKeys.keyIsCorrect' })) + } else { + dispatch(NotificationActions.notifyError({ key: 'user.mapApiKeys.keyIsNotCorrect' })) + } + }, + [dispatch] + ) const onExtraChange = useCallback( (extra) => { @@ -103,10 +106,12 @@ export const useEditUser = ({ userUuid }) => { [onUpdate, userToUpdate] ) - const onSurveyExtraPropsChange = (extraPropsNew) => { - const userUpdated = User.assocAuthGroupExtraProps(extraPropsNew)(userToUpdate) - onUpdate(userUpdated) - } + const onSurveyExtraPropsChange = useCallback( + (extraPropsNew) => { + onUpdate(User.assocAuthGroupExtraProps(extraPropsNew)(userToUpdate)) + }, + [onUpdate, userToUpdate] + ) return { hideSurveyGroup, diff --git a/webapp/views/App/views/Users/UsersListSurvey/Row/Row.js b/webapp/views/App/views/Users/UsersListSurvey/Row/Row.js index 2287739978..63a80652dc 100644 --- a/webapp/views/App/views/Users/UsersListSurvey/Row/Row.js +++ b/webapp/views/App/views/Users/UsersListSurvey/Row/Row.js @@ -5,6 +5,7 @@ import * as AuthGroup from '@core/auth/authGroup' import * as Survey from '@core/survey/survey' import * as User from '@core/user/user' import * as DateUtils from '@core/dateUtils' +import * as ProcessUtils from '@core/processUtils' import { TestId } from '@webapp/utils/testId' import { useI18n } from '@webapp/store/system' @@ -17,13 +18,15 @@ import ProfilePicture from '@webapp/components/profilePicture' import { CopyInvitationLinkButton } from './CopyInvitationLinkButton' const Row = (props) => { - const { row: userListItem } = props + const { onEditSurveyUserExtraProps, row: userListItem } = props const surveyInfo = useSurveyInfo() const surveyUuid = Survey.getUuid(surveyInfo) const i18n = useI18n() const canEditUser = useAuthCanEditUser(userListItem) const emailVisible = useAuthCanViewOtherUsersEmail() + const handleResendInvitation = useOnInviteRepeat({ userToInvite: userListItem, hasToNavigate: false }) + const authGroup = User.getAuthGroupBySurveyUuid({ surveyUuid, defaultToMainGroup: true })(userListItem) const authGroupName = AuthGroup.getName(authGroup) const userUuid = User.getUuid(userListItem) @@ -34,8 +37,6 @@ const Row = (props) => { const lastLoginTime = User.getLastLoginTime(userListItem) const lastLoginTimeFormatted = DateUtils.convertDateTimeFromISOToDisplay(lastLoginTime) ?? '' - const handleResendInvitation = useOnInviteRepeat({ userToInvite: userListItem, hasToNavigate: false }) - return ( <>
@@ -86,11 +87,21 @@ const Row = (props) => {
+ {canEditUser && ProcessUtils.ENV.experimentalFeatures && ( +