From fe0fca54a9ba73cb8d950a3686fc9af4e9381ff7 Mon Sep 17 00:00:00 2001 From: Camille Roux Date: Tue, 21 Oct 2025 11:24:00 +0200 Subject: [PATCH 1/5] [frontend] Translate User's files from javascript to typescript --- .../rest/user/form/user/UserOutput.java | 4 + .../rest/user/helper/UserQueryHelper.java | 2 + .../admin/components/settings/users/User.d.ts | 10 + .../components/settings/users/UserPopover.js | 178 ------------------ .../components/settings/users/UserPopover.tsx | 169 +++++++++++++++++ .../settings/users/{Users.js => Users.tsx} | 66 ++++--- openaev-front/src/utils/api-types.d.ts | 2 + 7 files changed, 225 insertions(+), 206 deletions(-) create mode 100644 openaev-front/src/admin/components/settings/users/User.d.ts delete mode 100644 openaev-front/src/admin/components/settings/users/UserPopover.js create mode 100644 openaev-front/src/admin/components/settings/users/UserPopover.tsx rename openaev-front/src/admin/components/settings/users/{Users.js => Users.tsx} (73%) diff --git a/openaev-api/src/main/java/io/openaev/rest/user/form/user/UserOutput.java b/openaev-api/src/main/java/io/openaev/rest/user/form/user/UserOutput.java index 6cd70a648a..fd81a097b0 100644 --- a/openaev-api/src/main/java/io/openaev/rest/user/form/user/UserOutput.java +++ b/openaev-api/src/main/java/io/openaev/rest/user/form/user/UserOutput.java @@ -37,6 +37,10 @@ public class UserOutput { @Schema(description = "Organization of the user") private String organizationName; + @JsonProperty("user_organization_id") + @Schema(description = "Organization of the user") + private String organizationId; + @JsonProperty("user_tags") @Schema(description = "Tags of the user") private Set tags; diff --git a/openaev-api/src/main/java/io/openaev/rest/user/helper/UserQueryHelper.java b/openaev-api/src/main/java/io/openaev/rest/user/helper/UserQueryHelper.java index dce8aaaf36..bf02a197ac 100644 --- a/openaev-api/src/main/java/io/openaev/rest/user/helper/UserQueryHelper.java +++ b/openaev-api/src/main/java/io/openaev/rest/user/helper/UserQueryHelper.java @@ -32,6 +32,7 @@ public static void select(CriteriaBuilder cb, CriteriaQuery cq, Root execution(TypedQuery query) { .email(tuple.get("user_email", String.class)) .admin(tuple.get("user_admin", boolean.class)) .organizationName(tuple.get("user_organization_name", String.class)) + .organizationId(tuple.get("user_organization_id", String.class)) .tags(Set.of((tuple.get("user_tags", String[].class)))) .build()) .toList(); diff --git a/openaev-front/src/admin/components/settings/users/User.d.ts b/openaev-front/src/admin/components/settings/users/User.d.ts new file mode 100644 index 0000000000..894a6b7608 --- /dev/null +++ b/openaev-front/src/admin/components/settings/users/User.d.ts @@ -0,0 +1,10 @@ +import { type UpdateUserInput } from '../../../../utils/api-types'; +import type { Option } from '../../../../utils/Option'; + +export type UserInputForm = Omit< + UpdateUserInput, + 'user_organization' | 'user_tags' +> & { + user_organization: Option | undefined; + user_tags: Option[]; +}; diff --git a/openaev-front/src/admin/components/settings/users/UserPopover.js b/openaev-front/src/admin/components/settings/users/UserPopover.js deleted file mode 100644 index 4d458c8de1..0000000000 --- a/openaev-front/src/admin/components/settings/users/UserPopover.js +++ /dev/null @@ -1,178 +0,0 @@ -import { MoreVert } from '@mui/icons-material'; -import { Button, Dialog, DialogActions, DialogContent, DialogContentText, IconButton, Menu, MenuItem, Slide } from '@mui/material'; -import * as R from 'ramda'; -import { forwardRef, useState } from 'react'; -import { useDispatch } from 'react-redux'; - -import { deleteUser, updateUser, updateUserPassword } from '../../../../actions/User'; -import Drawer from '../../../../components/common/Drawer'; -import { useFormatter } from '../../../../components/i18n'; -import { tagOptions } from '../../../../utils/Option'; -import { Can } from '../../../../utils/permissions/PermissionsProvider.js'; -import { ACTIONS, SUBJECTS } from '../../../../utils/permissions/types.js'; -import UserForm from './UserForm'; -import UserPasswordForm from './UserPasswordForm'; - -const Transition = forwardRef((props, ref) => ( - -)); -Transition.displayName = 'TransitionSlide'; - -const UserPopover = ({ user, organizationsMap, tagsMap, onUpdate, onDelete }) => { - const [openDelete, setOpenDelete] = useState(false); - const [openEdit, setOpenEdit] = useState(false); - const [openEditPassword, setOpenEditPassword] = useState(false); - const [anchorEl, setAnchorEl] = useState(null); - const dispatch = useDispatch(); - const { t } = useFormatter(); - - const handlePopoverOpen = (event) => { - event.stopPropagation(); - setAnchorEl(event.currentTarget); - }; - - const handlePopoverClose = () => setAnchorEl(null); - - const handleOpenEdit = () => { - setOpenEdit(true); - handlePopoverClose(); - }; - - const handleCloseEdit = () => setOpenEdit(false); - - const onSubmitEdit = (data) => { - const inputValues = R.pipe( - R.assoc( - 'user_organization', - data.user_organization && data.user_organization.id - ? data.user_organization.id - : data.user_organization, - ), - R.assoc('user_tags', R.pluck('id', data.user_tags)), - )(data); - return dispatch(updateUser(user.user_id, inputValues)).then((result) => { - if (onUpdate) { - const userUpdated = result.entities.users[result.result]; - onUpdate(userUpdated); - } - handleCloseEdit(); - }); - }; - - const handleOpenEditPassword = () => { - setOpenEditPassword(true); - handlePopoverClose(); - }; - - const handleCloseEditPassword = () => setOpenEditPassword(false); - - const onSubmitEditPassword = (data) => { - dispatch(updateUserPassword(user.user_id, data)).then(() => handleCloseEditPassword()); - }; - - const handleOpenDelete = () => { - setOpenDelete(true); - handlePopoverClose(); - }; - - const handleCloseDelete = () => setOpenDelete(false); - - const submitDelete = () => { - dispatch(deleteUser(user.user_id)).then( - () => { - if (onDelete) { - onDelete(user.user_id); - } - }, - ); - handleCloseDelete(); - }; - - const org = organizationsMap[user.user_organization]; - const userOrganization = org - ? { - id: org.organization_id, - label: org.organization_name, - } - : null; - const userTags = tagOptions(user.user_tags, tagsMap); - const initialValues = R.pipe( - R.assoc('user_organization', userOrganization), - R.assoc('user_tags', userTags), - R.pick([ - 'user_firstname', - 'user_lastname', - 'user_email', - 'user_organization', - 'user_phone', - 'user_phone2', - 'user_pgp_key', - 'user_tags', - 'user_admin', - ]), - )(user); - return ( - <> - - - - - - - {t('Update')} - - {t('Update password')} - - {user.user_email !== 'admin@openaev.io' && ( - {t('Delete')} - )} - - - - - {t('Do you want to delete this user?')} - - - - - - - - - - - - - - - ); -}; - -export default UserPopover; diff --git a/openaev-front/src/admin/components/settings/users/UserPopover.tsx b/openaev-front/src/admin/components/settings/users/UserPopover.tsx new file mode 100644 index 0000000000..b178dd1729 --- /dev/null +++ b/openaev-front/src/admin/components/settings/users/UserPopover.tsx @@ -0,0 +1,169 @@ +import { useContext, useState } from 'react'; + +import { type OrganizationHelper, type TagHelper } from '../../../../actions/helper'; +import { deleteUser, updateUser, updateUserPassword } from '../../../../actions/User'; +import ButtonPopover from '../../../../components/common/ButtonPopover'; +import DialogDelete from '../../../../components/common/DialogDelete'; +import Drawer from '../../../../components/common/Drawer'; +import { useFormatter } from '../../../../components/i18n'; +import { useHelper } from '../../../../store'; +import { type ChangePasswordInput, type UpdateUserInput, type User, type UserOutput } from '../../../../utils/api-types'; +import { useAppDispatch } from '../../../../utils/hooks'; +import { type Option, organizationOption, tagOptions } from '../../../../utils/Option'; +import { AbilityContext } from '../../../../utils/permissions/PermissionsProvider.js'; +import { ACTIONS, SUBJECTS } from '../../../../utils/permissions/types.js'; +import { type UserInputForm } from './User'; +import UserForm from './UserForm'; +import UserPasswordForm from './UserPasswordForm'; + +interface UserPopoverProps { + user: UserOutput; + onUpdate: (result: User) => void; + onDelete: (result: string) => void; +} + +const UserPopover = ({ user, onUpdate, onDelete }: UserPopoverProps) => { + const [openDelete, setOpenDelete] = useState(false); + const [openEdit, setOpenEdit] = useState(false); + const [openEditPassword, setOpenEditPassword] = useState(false); + const dispatch = useAppDispatch(); + const ability = useContext(AbilityContext); + + const { t } = useFormatter(); + + const { organizationsMap, tagsMap } = useHelper( + ( + helper: OrganizationHelper & TagHelper, + ) => { + return { + organizationsMap: helper.getOrganizationsMap(), + tagsMap: helper.getTagsMap(), + }; + }, + ); + + const handleOpenEdit = () => { + setOpenEdit(true); + }; + + const handleCloseEdit = () => setOpenEdit(false); + + const onSubmitEdit = (data: UserInputForm) => { + const inputValues: UpdateUserInput = { + ...data, + user_organization: data.user_organization?.id, + user_tags: data.user_tags?.map((tag: Option) => tag.id), + }; + + return dispatch(updateUser(user.user_id, inputValues)).then((result: { + result: string; + entities: { users: Record }; + }) => { + if (onUpdate) { + const userUpdated = result.entities.users[result.result]; + + const orgId = userUpdated.user_organization; + const org = orgId ? organizationsMap[orgId] : undefined; + + const userToUpdate = { + ...userUpdated, + user_organization_name: org ? org.organization_name : '', + user_organization_id: org ? org.organization_id : '', + }; + + onUpdate(userToUpdate); + } + handleCloseEdit(); + }); + }; + + const handleOpenEditPassword = () => { + setOpenEditPassword(true); + }; + + const handleCloseEditPassword = () => setOpenEditPassword(false); + + const onSubmitEditPassword = (data: ChangePasswordInput) => { + dispatch(updateUserPassword(user.user_id, data)).then(() => handleCloseEditPassword()); + }; + + const handleOpenDelete = () => { + setOpenDelete(true); + }; + + const handleCloseDelete = () => setOpenDelete(false); + + const submitDelete = () => { + dispatch(deleteUser(user.user_id)).then( + () => { + if (onDelete) { + onDelete(user.user_id); + } + }, + ); + handleCloseDelete(); + }; + + const initialValues: UserInputForm = { + ...user, + user_organization: organizationOption( + user.user_organization_id, + organizationsMap, + ), + user_tags: tagOptions(user.user_tags, tagsMap), + }; + + // Button Popover + const entries = []; + entries.push({ + label: t('Update'), + action: () => handleOpenEdit(), + userRight: ability.can(ACTIONS.MANAGE, SUBJECTS.PLATFORM_SETTINGS), + }); + entries.push({ + label: t('Update password'), + action: () => handleOpenEditPassword(), + userRight: ability.can(ACTIONS.MANAGE, SUBJECTS.PLATFORM_SETTINGS), + }); + if (user.user_email !== 'admin@openaev.io') entries.push({ + label: t('Delete'), + action: () => handleOpenDelete(), + userRight: ability.can(ACTIONS.DELETE, SUBJECTS.PLATFORM_SETTINGS), + }); + + return ( + <> + + + + + + + + + + ); +}; + +export default UserPopover; diff --git a/openaev-front/src/admin/components/settings/users/Users.js b/openaev-front/src/admin/components/settings/users/Users.tsx similarity index 73% rename from openaev-front/src/admin/components/settings/users/Users.js rename to openaev-front/src/admin/components/settings/users/Users.tsx index 4b77ca47ad..633ed6ef03 100644 --- a/openaev-front/src/admin/components/settings/users/Users.js +++ b/openaev-front/src/admin/components/settings/users/Users.tsx @@ -1,18 +1,22 @@ import { CheckCircleOutlined, PersonOutlined } from '@mui/icons-material'; -import { List, ListItem, ListItemIcon, ListItemSecondaryAction, ListItemText } from '@mui/material'; +import { List, ListItem, ListItemIcon, ListItemText } from '@mui/material'; import { useState } from 'react'; -import { useDispatch } from 'react-redux'; +import { useSearchParams } from 'react-router'; import { makeStyles } from 'tss-react/mui'; import { fetchOrganizations } from '../../../../actions/Organization'; import { searchUsers } from '../../../../actions/User'; import Breadcrumbs from '../../../../components/Breadcrumbs'; -import PaginationComponent from '../../../../components/common/pagination/PaginationComponent'; -import SortHeadersComponent from '../../../../components/common/pagination/SortHeadersComponent'; +import ExportButton from '../../../../components/common/ExportButton'; import { initSorting } from '../../../../components/common/queryable/Page'; +import PaginationComponentV2 from '../../../../components/common/queryable/pagination/PaginationComponentV2'; +import { buildSearchPagination } from '../../../../components/common/queryable/QueryableUtils'; +import SortHeadersComponentV2 from '../../../../components/common/queryable/sort/SortHeadersComponentV2'; +import { useQueryableWithLocalStorage } from '../../../../components/common/queryable/useQueryableWithLocalStorage'; import { useFormatter } from '../../../../components/i18n'; import ItemTags from '../../../../components/ItemTags'; -import { useHelper } from '../../../../store'; +import { type User, type UserOutput } from '../../../../utils/api-types'; +import { useAppDispatch } from '../../../../utils/hooks'; import useDataLoader from '../../../../utils/hooks/useDataLoader'; import { Can } from '../../../../utils/permissions/PermissionsProvider.js'; import { ACTIONS, SUBJECTS } from '../../../../utils/permissions/types.js'; @@ -62,12 +66,9 @@ const inlineStyles = { const Users = () => { // Standard hooks const { classes } = useStyles(); - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); const { t } = useFormatter(); - const { tagsMap, organizationsMap } = useHelper(helper => ({ - organizationsMap: helper.getOrganizationsMap(), - tagsMap: helper.getTagsMap(), - })); + useDataLoader(() => { dispatch(fetchOrganizations()); }); @@ -106,8 +107,15 @@ const Users = () => { }, ]; - const [users, setUsers] = useState([]); - const [searchPaginationInput, setSearchPaginationInput] = useState({ sorts: initSorting('user_email') }); + // Query param + const [searchParams] = useSearchParams(); + const [search] = searchParams.getAll('search'); + + const [users, setUsers] = useState([]); + const { queryableHelpers, searchPaginationInput } = useQueryableWithLocalStorage('users', buildSearchPagination({ + sorts: initSorting('user_firstname'), + textSearch: search, + })); // Export const exportProps = { @@ -131,17 +139,22 @@ const Users = () => { current: true, }]} /> - + } />  } > { )} /> -   {users.map(user => ( setUsers(users.map(u => (u.user_id !== result.user_id ? u : result)))} + onDelete={(result: string) => setUsers(users.filter(u => (u.user_id !== result)))} + /> + )} > @@ -189,6 +207,7 @@ const Users = () => {
{user.user_organization_name} + {/* {user.user_organization && organizationsMap[user.user_organization].organization_name} */}
{user.user_admin ? () : ('-')} @@ -199,21 +218,12 @@ const Users = () => {
)} /> - - setUsers(users.map(u => (u.user_id !== result.user_id ? u : result)))} - onDelete={result => setUsers(users.filter(u => (u.user_id !== result)))} - /> -
))}
setUsers([result, ...users])} + onCreate={(result: User) => setUsers([result, ...users])} /> diff --git a/openaev-front/src/utils/api-types.d.ts b/openaev-front/src/utils/api-types.d.ts index 1c4b2c22e6..1445715f47 100644 --- a/openaev-front/src/utils/api-types.d.ts +++ b/openaev-front/src/utils/api-types.d.ts @@ -6202,6 +6202,8 @@ export interface UserOutput { /** Last name of the user */ user_lastname?: string; /** Organization of the user */ + user_organization_id?: string; + /** Organization of the user */ user_organization_name?: string; /** * Tags of the user From 854f79c28384402b9325ddd7139f99c47556afe0 Mon Sep 17 00:00:00 2001 From: Camille Roux Date: Tue, 21 Oct 2025 18:56:01 +0200 Subject: [PATCH 2/5] [frontend] Translate User's files from javascript to typescript --- openaev-front/src/actions/{ => users}/User.js | 4 +- .../users/users-helper.d.ts} | 7 +- .../src/admin/components/agents/Agents.tsx | 2 +- .../components/teams/TeamAddPlayers.tsx | 2 +- .../src/admin/components/profile/Index.js | 2 +- .../settings/groups/GroupManageUsers.tsx | 2 +- .../components/settings/groups/Groups.js | 2 +- .../components/settings/users/CreateUser.js | 101 ------------------ .../components/settings/users/CreateUser.tsx | 78 ++++++++++++++ .../users/{UserForm.js => UserForm.tsx} | 19 ++-- .../settings/users/UserPasswordForm.js | 84 --------------- .../settings/users/UserPasswordForm.tsx | 86 +++++++++++++++ .../components/settings/users/UserPopover.tsx | 4 +- .../admin/components/settings/users/Users.tsx | 4 +- .../simulations/simulation/mails/Inject.js | 2 +- .../mails/MailDistributionByPlayer.tsx | 2 +- .../expectations/ManualExpectations.tsx | 2 +- .../ManualExpectationsValidationForm.tsx | 2 +- .../components/teams/players/CreatePlayer.tsx | 2 +- .../teams/players/PlayerPopover.tsx | 2 +- 20 files changed, 197 insertions(+), 212 deletions(-) rename openaev-front/src/actions/{ => users}/User.js (95%) rename openaev-front/src/{admin/components/settings/users/User.d.ts => actions/users/users-helper.d.ts} (58%) delete mode 100644 openaev-front/src/admin/components/settings/users/CreateUser.js create mode 100644 openaev-front/src/admin/components/settings/users/CreateUser.tsx rename openaev-front/src/admin/components/settings/users/{UserForm.js => UserForm.tsx} (93%) delete mode 100644 openaev-front/src/admin/components/settings/users/UserPasswordForm.js create mode 100644 openaev-front/src/admin/components/settings/users/UserPasswordForm.tsx diff --git a/openaev-front/src/actions/User.js b/openaev-front/src/actions/users/User.js similarity index 95% rename from openaev-front/src/actions/User.js rename to openaev-front/src/actions/users/User.js index 21a33e2c21..b1ba2f1591 100644 --- a/openaev-front/src/actions/User.js +++ b/openaev-front/src/actions/users/User.js @@ -1,5 +1,5 @@ -import { delReferential, getReferential, postReferential, putReferential, simplePostCall } from '../utils/Action'; -import * as schema from './Schema'; +import { delReferential, getReferential, postReferential, putReferential, simplePostCall } from '../../utils/Action.js'; +import * as schema from '../Schema.js'; // region players export const fetchPlayers = () => dispatch => getReferential(schema.arrayOfUsers, '/api/players')(dispatch); diff --git a/openaev-front/src/admin/components/settings/users/User.d.ts b/openaev-front/src/actions/users/users-helper.d.ts similarity index 58% rename from openaev-front/src/admin/components/settings/users/User.d.ts rename to openaev-front/src/actions/users/users-helper.d.ts index 894a6b7608..42898af3eb 100644 --- a/openaev-front/src/admin/components/settings/users/User.d.ts +++ b/openaev-front/src/actions/users/users-helper.d.ts @@ -1,5 +1,5 @@ -import { type UpdateUserInput } from '../../../../utils/api-types'; import type { Option } from '../../../../utils/Option'; +import { type UpdateUserInput } from '../../utils/api-types'; export type UserInputForm = Omit< UpdateUserInput, @@ -8,3 +8,8 @@ export type UserInputForm = Omit< user_organization: Option | undefined; user_tags: Option[]; }; + +export interface UserResult { + entities: { users: Record }; + result: string; +} diff --git a/openaev-front/src/admin/components/agents/Agents.tsx b/openaev-front/src/admin/components/agents/Agents.tsx index 2b74dc0c7e..1f182f470a 100644 --- a/openaev-front/src/admin/components/agents/Agents.tsx +++ b/openaev-front/src/admin/components/agents/Agents.tsx @@ -5,7 +5,7 @@ import { useState } from 'react'; import { fetchExecutors } from '../../../actions/Executor'; import { type ExecutorHelper } from '../../../actions/executors/executor-helper'; import { type LoggedHelper, type MeTokensHelper } from '../../../actions/helper'; -import { meTokens } from '../../../actions/User'; +import { meTokens } from '../../../actions/users/User'; import Breadcrumbs from '../../../components/Breadcrumbs'; import Transition from '../../../components/common/Transition'; import { useFormatter } from '../../../components/i18n'; diff --git a/openaev-front/src/admin/components/components/teams/TeamAddPlayers.tsx b/openaev-front/src/admin/components/components/teams/TeamAddPlayers.tsx index e3e0542574..f63a409fa3 100644 --- a/openaev-front/src/admin/components/components/teams/TeamAddPlayers.tsx +++ b/openaev-front/src/admin/components/components/teams/TeamAddPlayers.tsx @@ -20,7 +20,7 @@ import { type FunctionComponent, useContext, useState } from 'react'; import { makeStyles } from 'tss-react/mui'; import { type OrganizationHelper, type UserHelper } from '../../../../actions/helper'; -import { fetchPlayers } from '../../../../actions/User'; +import { fetchPlayers } from '../../../../actions/users/User'; import Transition from '../../../../components/common/Transition'; import { useFormatter } from '../../../../components/i18n'; import ItemTags from '../../../../components/ItemTags'; diff --git a/openaev-front/src/admin/components/profile/Index.js b/openaev-front/src/admin/components/profile/Index.js index 8e3ca6f503..d189628baf 100644 --- a/openaev-front/src/admin/components/profile/Index.js +++ b/openaev-front/src/admin/components/profile/Index.js @@ -3,7 +3,7 @@ import { useTheme } from '@mui/material/styles'; import * as R from 'ramda'; import { useDispatch } from 'react-redux'; -import { meTokens, renewToken, updateMeInformation, updateMePassword, updateMeProfile } from '../../../actions/User'; +import { meTokens, renewToken, updateMeInformation, updateMePassword, updateMeProfile } from '../../../actions/users/User.js'; import Paper from '../../../components/common/Paper'; import { useFormatter } from '../../../components/i18n'; import { useHelper } from '../../../store'; diff --git a/openaev-front/src/admin/components/settings/groups/GroupManageUsers.tsx b/openaev-front/src/admin/components/settings/groups/GroupManageUsers.tsx index f65207a4f8..c1b79d5e8e 100644 --- a/openaev-front/src/admin/components/settings/groups/GroupManageUsers.tsx +++ b/openaev-front/src/admin/components/settings/groups/GroupManageUsers.tsx @@ -2,7 +2,7 @@ import { PersonOutlined } from '@mui/icons-material'; import { Box, Button } from '@mui/material'; import { type FunctionComponent, useEffect, useMemo, useState } from 'react'; -import { findUsers, searchUsers } from '../../../../actions/User'; +import { findUsers, searchUsers } from '../../../../actions/users/User'; import Drawer from '../../../../components/common/Drawer'; import PaginationComponentV2 from '../../../../components/common/queryable/pagination/PaginationComponentV2'; import { buildSearchPagination } from '../../../../components/common/queryable/QueryableUtils'; diff --git a/openaev-front/src/admin/components/settings/groups/Groups.js b/openaev-front/src/admin/components/settings/groups/Groups.js index 80e1326785..8ea4f6da14 100644 --- a/openaev-front/src/admin/components/settings/groups/Groups.js +++ b/openaev-front/src/admin/components/settings/groups/Groups.js @@ -9,7 +9,7 @@ import { searchGroups } from '../../../../actions/Group'; import { fetchOrganizations } from '../../../../actions/Organization'; import { fetchRoles } from '../../../../actions/roles/roles-actions.js'; import { fetchScenarios } from '../../../../actions/scenarios/scenario-actions'; -import { fetchUsers } from '../../../../actions/User'; +import { fetchUsers } from '../../../../actions/users/User.js'; import Breadcrumbs from '../../../../components/Breadcrumbs'; import PaginationComponent from '../../../../components/common/pagination/PaginationComponent'; import SortHeadersComponent from '../../../../components/common/pagination/SortHeadersComponent'; diff --git a/openaev-front/src/admin/components/settings/users/CreateUser.js b/openaev-front/src/admin/components/settings/users/CreateUser.js deleted file mode 100644 index ebcff22ff8..0000000000 --- a/openaev-front/src/admin/components/settings/users/CreateUser.js +++ /dev/null @@ -1,101 +0,0 @@ -import { Add } from '@mui/icons-material'; -import { Fab, Slide } from '@mui/material'; -import * as PropTypes from 'prop-types'; -import * as R from 'ramda'; -import { Component, forwardRef } from 'react'; -import { connect } from 'react-redux'; -import { withStyles } from 'tss-react/mui'; - -import { addUser } from '../../../../actions/User'; -import Drawer from '../../../../components/common/Drawer'; -import inject18n from '../../../../components/i18n'; -import UserForm from './UserForm'; - -const styles = () => ({ - createButton: { - position: 'fixed', - bottom: 30, - right: 230, - }, -}); - -const Transition = forwardRef((props, ref) => ( - -)); -Transition.displayName = 'TransitionSlide'; - -class CreateUser extends Component { - constructor(props) { - super(props); - this.state = { open: false }; - } - - handleOpen() { - this.setState({ open: true }); - } - - handleClose() { - this.setState({ open: false }); - } - - onSubmit(data) { - const inputValues = R.pipe( - R.assoc( - 'user_organization', - data.user_organization && data.user_organization.id - ? data.user_organization.id - : data.user_organization, - ), - R.assoc('user_tags', R.pluck('id', data.user_tags)), - )(data); - return this.props - .addUser(inputValues) - .then((result) => { - if (this.props.onCreate) { - const userCreated = result.entities.users[result.result]; - this.props.onCreate(userCreated); - } - return result.result ? this.handleClose() : result; - }); - } - - render() { - const { classes, t } = this.props; - return ( - <> - - - - - - - - ); - } -} - -CreateUser.propTypes = { - t: PropTypes.func, - organizations: PropTypes.array, - addUser: PropTypes.func, -}; - -export default R.compose( - connect(null, { addUser }), - inject18n, - Component => withStyles(Component, styles), -)(CreateUser); diff --git a/openaev-front/src/admin/components/settings/users/CreateUser.tsx b/openaev-front/src/admin/components/settings/users/CreateUser.tsx new file mode 100644 index 0000000000..b94950dd0d --- /dev/null +++ b/openaev-front/src/admin/components/settings/users/CreateUser.tsx @@ -0,0 +1,78 @@ +import { useState } from 'react'; + +import type { OrganizationHelper } from '../../../../actions/helper'; +import { addUser } from '../../../../actions/users/User'; +import { type UserInputForm, type UserResult } from '../../../../actions/users/users-helper'; +import ButtonCreate from '../../../../components/common/ButtonCreate'; +import Drawer from '../../../../components/common/Drawer'; +import { useFormatter } from '../../../../components/i18n'; +import { useHelper } from '../../../../store'; +import { type User } from '../../../../utils/api-types'; +import { useAppDispatch } from '../../../../utils/hooks'; +import { type Option } from '../../../../utils/Option'; +import UserForm from './UserForm'; + +interface CreateUserProps { onCreate?: (user: User) => void } + +const CreateUser = ({ onCreate }: CreateUserProps) => { + const { t } = useFormatter(); + const dispatch = useAppDispatch(); + + const [open, setOpen] = useState(false); + + const handleOpen = () => setOpen(true); + const handleClose = () => setOpen(false); + + const { organizationsMap } = useHelper( + ( + helper: OrganizationHelper, + ) => { + return { organizationsMap: helper.getOrganizationsMap() }; + }, + ); + const onSubmit = (data: UserInputForm) => { + const inputValues = { + ...data, + user_organization: data.user_organization?.id, + user_tags: data.user_tags?.map((tag: Option) => tag.id), + }; + + return dispatch(addUser(inputValues)).then((result: UserResult) => { + if (onCreate) { + const userCreated = result.entities.users[result.result]; + + const orgId = userCreated.user_organization; + const org = orgId ? organizationsMap[orgId] : undefined; + + const userToCreate = { + ...userCreated, + user_organization_name: org ? org.organization_name : '', + user_organization_id: org ? org.organization_id : '', + }; + + onCreate(userToCreate); + } + return result.result ? handleClose() : result; + }); + }; + + return ( + <> + + + + + + + ); +}; + +export default CreateUser; diff --git a/openaev-front/src/admin/components/settings/users/UserForm.js b/openaev-front/src/admin/components/settings/users/UserForm.tsx similarity index 93% rename from openaev-front/src/admin/components/settings/users/UserForm.js rename to openaev-front/src/admin/components/settings/users/UserForm.tsx index 34f14b01e2..730ad59020 100644 --- a/openaev-front/src/admin/components/settings/users/UserForm.js +++ b/openaev-front/src/admin/components/settings/users/UserForm.tsx @@ -1,8 +1,8 @@ import { Button } from '@mui/material'; -import * as PropTypes from 'prop-types'; import { Form } from 'react-final-form'; import { z } from 'zod'; +import { type UserInputForm } from '../../../../actions/users/users-helper'; import OldSwitchField from '../../../../components/fields/OldSwitchField'; import OldTextField from '../../../../components/fields/OldTextField'; import { useFormatter } from '../../../../components/i18n'; @@ -10,8 +10,14 @@ import OrganizationField from '../../../../components/OrganizationField'; import TagField from '../../../../components/TagField'; import { schemaValidator } from '../../../../utils/Zod.js'; -const UserForm = (props) => { - const { onSubmit, initialValues, editing, handleClose } = props; +interface UserFormProps { + onSubmit: (data: UserInputForm) => void; + initialValues?: Partial; + editing: boolean; + handleClose: () => void; +} + +const UserForm = ({ onSubmit, initialValues = {}, editing, handleClose }: UserFormProps) => { const { t } = useFormatter(); const requiredFields = editing @@ -29,6 +35,7 @@ const UserForm = (props) => { .nonempty(t('This field is required.')), }), }); + return (
{ ); }; -UserForm.propTypes = { - onSubmit: PropTypes.func.isRequired, - handleClose: PropTypes.func, - editing: PropTypes.bool, -}; - export default UserForm; diff --git a/openaev-front/src/admin/components/settings/users/UserPasswordForm.js b/openaev-front/src/admin/components/settings/users/UserPasswordForm.js deleted file mode 100644 index e76fc4be3d..0000000000 --- a/openaev-front/src/admin/components/settings/users/UserPasswordForm.js +++ /dev/null @@ -1,84 +0,0 @@ -import { Button } from '@mui/material'; -import * as PropTypes from 'prop-types'; -import { Component } from 'react'; -import { Form } from 'react-final-form'; - -import OldTextField from '../../../../components/fields/OldTextField'; -import inject18n from '../../../../components/i18n'; - -class UserPasswordForm extends Component { - validate(values) { - const { t } = this.props; - const errors = {}; - if (!values.password || values.password !== values.password_validation) { - errors.password = t('Passwords do no match'); - } - return errors; - } - - render() { - const { t, onSubmit, initialValues, handleClose } = this.props; - return ( - - {({ handleSubmit, submitting, pristine }) => ( - - - -
- - -
- - )} - - ); - } -} - -UserPasswordForm.propTypes = { - t: PropTypes.func, - error: PropTypes.string, - pristine: PropTypes.bool, - submitting: PropTypes.bool, - onSubmit: PropTypes.func.isRequired, - handleSubmit: PropTypes.func, - change: PropTypes.func, -}; - -export default inject18n(UserPasswordForm); diff --git a/openaev-front/src/admin/components/settings/users/UserPasswordForm.tsx b/openaev-front/src/admin/components/settings/users/UserPasswordForm.tsx new file mode 100644 index 0000000000..ec19cf2c93 --- /dev/null +++ b/openaev-front/src/admin/components/settings/users/UserPasswordForm.tsx @@ -0,0 +1,86 @@ +import { Button } from '@mui/material'; +import { Form } from 'react-final-form'; + +import OldTextField from '../../../../components/fields/OldTextField'; +import { useFormatter } from '../../../../components/i18n'; + +interface UserPasswordFormValues { + password: string; + password_validation: string; +} + +interface UserPasswordFormProps { + onSubmit: (values: UserPasswordFormValues) => void; + initialValues?: Partial; + handleClose: () => void; +} + +const UserPasswordForm = ({ + onSubmit, + initialValues, + handleClose, +}: UserPasswordFormProps) => { + const { t } = useFormatter(); + + const validate = (values: UserPasswordFormValues) => { + const errors: Partial> = {}; + if (!values.password || values.password !== values.password_validation) { + errors.password = t('Passwords do no match'); + } + return errors; + }; + + return ( +
+ {({ handleSubmit, submitting, pristine }) => ( + + + +
+ + +
+ + )} + + ); +}; + +export default UserPasswordForm; diff --git a/openaev-front/src/admin/components/settings/users/UserPopover.tsx b/openaev-front/src/admin/components/settings/users/UserPopover.tsx index b178dd1729..00facbb61c 100644 --- a/openaev-front/src/admin/components/settings/users/UserPopover.tsx +++ b/openaev-front/src/admin/components/settings/users/UserPopover.tsx @@ -1,7 +1,8 @@ import { useContext, useState } from 'react'; import { type OrganizationHelper, type TagHelper } from '../../../../actions/helper'; -import { deleteUser, updateUser, updateUserPassword } from '../../../../actions/User'; +import { deleteUser, updateUser, updateUserPassword } from '../../../../actions/users/User'; +import { type UserInputForm } from '../../../../actions/users/users-helper'; import ButtonPopover from '../../../../components/common/ButtonPopover'; import DialogDelete from '../../../../components/common/DialogDelete'; import Drawer from '../../../../components/common/Drawer'; @@ -12,7 +13,6 @@ import { useAppDispatch } from '../../../../utils/hooks'; import { type Option, organizationOption, tagOptions } from '../../../../utils/Option'; import { AbilityContext } from '../../../../utils/permissions/PermissionsProvider.js'; import { ACTIONS, SUBJECTS } from '../../../../utils/permissions/types.js'; -import { type UserInputForm } from './User'; import UserForm from './UserForm'; import UserPasswordForm from './UserPasswordForm'; diff --git a/openaev-front/src/admin/components/settings/users/Users.tsx b/openaev-front/src/admin/components/settings/users/Users.tsx index 633ed6ef03..158a376737 100644 --- a/openaev-front/src/admin/components/settings/users/Users.tsx +++ b/openaev-front/src/admin/components/settings/users/Users.tsx @@ -5,7 +5,7 @@ import { useSearchParams } from 'react-router'; import { makeStyles } from 'tss-react/mui'; import { fetchOrganizations } from '../../../../actions/Organization'; -import { searchUsers } from '../../../../actions/User'; +import { searchUsers } from '../../../../actions/users/User'; import Breadcrumbs from '../../../../components/Breadcrumbs'; import ExportButton from '../../../../components/common/ExportButton'; import { initSorting } from '../../../../components/common/queryable/Page'; @@ -140,6 +140,7 @@ const Users = () => { }]} /> {
{user.user_organization_name} - {/* {user.user_organization && organizationsMap[user.user_organization].organization_name} */}
{user.user_admin ? () : ('-')} diff --git a/openaev-front/src/admin/components/simulations/simulation/mails/Inject.js b/openaev-front/src/admin/components/simulations/simulation/mails/Inject.js index e1859753f9..41edd30bdd 100644 --- a/openaev-front/src/admin/components/simulations/simulation/mails/Inject.js +++ b/openaev-front/src/admin/components/simulations/simulation/mails/Inject.js @@ -8,7 +8,7 @@ import { makeStyles } from 'tss-react/mui'; import { fetchInjectCommunications } from '../../../../../actions/Communication'; import { executeInject, fetchExerciseInjects } from '../../../../../actions/Inject'; -import { fetchPlayers } from '../../../../../actions/User'; +import { fetchPlayers } from '../../../../../actions/users/User.js'; import Transition from '../../../../../components/common/Transition'; import { useFormatter } from '../../../../../components/i18n'; import ItemTags from '../../../../../components/ItemTags'; diff --git a/openaev-front/src/admin/components/simulations/simulation/mails/MailDistributionByPlayer.tsx b/openaev-front/src/admin/components/simulations/simulation/mails/MailDistributionByPlayer.tsx index ca90d84dad..dd2edbabbd 100644 --- a/openaev-front/src/admin/components/simulations/simulation/mails/MailDistributionByPlayer.tsx +++ b/openaev-front/src/admin/components/simulations/simulation/mails/MailDistributionByPlayer.tsx @@ -6,7 +6,7 @@ import Chart from 'react-apexcharts'; import { fetchExerciseCommunications } from '../../../../../actions/Communication'; import { type CommunicationHelper } from '../../../../../actions/communications/communication-helper'; import { type UserHelper } from '../../../../../actions/helper'; -import { fetchPlayers } from '../../../../../actions/User'; +import { fetchPlayers } from '../../../../../actions/users/User'; import Empty from '../../../../../components/Empty'; import { useFormatter } from '../../../../../components/i18n'; import { useHelper } from '../../../../../store'; diff --git a/openaev-front/src/admin/components/simulations/simulation/validation/expectations/ManualExpectations.tsx b/openaev-front/src/admin/components/simulations/simulation/validation/expectations/ManualExpectations.tsx index 755de67a9c..a81199548a 100644 --- a/openaev-front/src/admin/components/simulations/simulation/validation/expectations/ManualExpectations.tsx +++ b/openaev-front/src/admin/components/simulations/simulation/validation/expectations/ManualExpectations.tsx @@ -5,7 +5,7 @@ import { type FunctionComponent, type SyntheticEvent, useContext, useState } fro import { makeStyles } from 'tss-react/mui'; import { type UserHelper } from '../../../../../../actions/helper'; -import { fetchPlayers } from '../../../../../../actions/User'; +import { fetchPlayers } from '../../../../../../actions/users/User'; import colorStyles from '../../../../../../components/Color'; import Drawer from '../../../../../../components/common/Drawer'; import ExpandableText from '../../../../../../components/common/ExpendableText'; diff --git a/openaev-front/src/admin/components/simulations/simulation/validation/expectations/ManualExpectationsValidationForm.tsx b/openaev-front/src/admin/components/simulations/simulation/validation/expectations/ManualExpectationsValidationForm.tsx index 59b61255c5..e550c050e2 100644 --- a/openaev-front/src/admin/components/simulations/simulation/validation/expectations/ManualExpectationsValidationForm.tsx +++ b/openaev-front/src/admin/components/simulations/simulation/validation/expectations/ManualExpectationsValidationForm.tsx @@ -19,7 +19,7 @@ import { updateInjectExpectation } from '../../../../../../actions/Exercise'; import { type UserHelper } from '../../../../../../actions/helper'; import { fetchTeams } from '../../../../../../actions/teams/team-actions'; import { type TeamsHelper } from '../../../../../../actions/teams/team-helper'; -import { fetchPlayers } from '../../../../../../actions/User'; +import { fetchPlayers } from '../../../../../../actions/users/User'; import { useFormatter } from '../../../../../../components/i18n'; import { useHelper } from '../../../../../../store'; import { type Team, type User } from '../../../../../../utils/api-types'; diff --git a/openaev-front/src/admin/components/teams/players/CreatePlayer.tsx b/openaev-front/src/admin/components/teams/players/CreatePlayer.tsx index 756f65796c..4dc2887e24 100644 --- a/openaev-front/src/admin/components/teams/players/CreatePlayer.tsx +++ b/openaev-front/src/admin/components/teams/players/CreatePlayer.tsx @@ -3,7 +3,7 @@ import { ListItemButton, ListItemIcon, ListItemText } from '@mui/material'; import { type FunctionComponent, useState } from 'react'; import { makeStyles } from 'tss-react/mui'; -import { addPlayer } from '../../../../actions/User'; +import { addPlayer } from '../../../../actions/users/User'; import ButtonCreate from '../../../../components/common/ButtonCreate'; import Dialog from '../../../../components/common/dialog/Dialog'; import Drawer from '../../../../components/common/Drawer'; diff --git a/openaev-front/src/admin/components/teams/players/PlayerPopover.tsx b/openaev-front/src/admin/components/teams/players/PlayerPopover.tsx index de2f7e8343..e059a198a4 100644 --- a/openaev-front/src/admin/components/teams/players/PlayerPopover.tsx +++ b/openaev-front/src/admin/components/teams/players/PlayerPopover.tsx @@ -2,7 +2,7 @@ import { Button, Dialog as MuiDialog, DialogActions, DialogContent, DialogConten import { type FunctionComponent, useContext, useState } from 'react'; import { type OrganizationHelper, type TagHelper, type UserHelper } from '../../../../actions/helper'; -import { deletePlayer, updatePlayer } from '../../../../actions/User'; +import { deletePlayer, updatePlayer } from '../../../../actions/users/User'; import ButtonPopover from '../../../../components/common/ButtonPopover'; import DialogDelete from '../../../../components/common/DialogDelete'; import Drawer from '../../../../components/common/Drawer'; From 66af532de5e6f4ef795b378245cec31b41e98f91 Mon Sep 17 00:00:00 2001 From: Camille Roux Date: Wed, 22 Oct 2025 15:47:36 +0200 Subject: [PATCH 3/5] [frontend] Translate User's files from javascript to typescript --- openaev-front/src/admin/components/settings/users/CreateUser.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/openaev-front/src/admin/components/settings/users/CreateUser.tsx b/openaev-front/src/admin/components/settings/users/CreateUser.tsx index b94950dd0d..cd95515c7b 100644 --- a/openaev-front/src/admin/components/settings/users/CreateUser.tsx +++ b/openaev-front/src/admin/components/settings/users/CreateUser.tsx @@ -69,6 +69,7 @@ const CreateUser = ({ onCreate }: CreateUserProps) => { editing={false} onSubmit={onSubmit} handleClose={handleClose} + initialValues={{ user_tags: [] }} /> From 229029ccc495f5a947dcd13aff76d16f9de2c49d Mon Sep 17 00:00:00 2001 From: Camille Roux Date: Thu, 23 Oct 2025 14:34:02 +0200 Subject: [PATCH 4/5] [frontend] Translate User's files from javascript to typescript --- .../components/settings/users/CreateUser.tsx | 4 ++++ .../components/settings/users/UserForm.tsx | 19 +++++++++++++++++++ .../components/settings/users/UserPopover.tsx | 13 +++++++------ openaev-front/src/utils/lang/en.json | 1 + openaev-front/src/utils/lang/fr.json | 1 + openaev-front/src/utils/lang/zh.json | 1 + 6 files changed, 33 insertions(+), 6 deletions(-) diff --git a/openaev-front/src/admin/components/settings/users/CreateUser.tsx b/openaev-front/src/admin/components/settings/users/CreateUser.tsx index cd95515c7b..667688ae4d 100644 --- a/openaev-front/src/admin/components/settings/users/CreateUser.tsx +++ b/openaev-front/src/admin/components/settings/users/CreateUser.tsx @@ -38,6 +38,10 @@ const CreateUser = ({ onCreate }: CreateUserProps) => { }; return dispatch(addUser(inputValues)).then((result: UserResult) => { + if (!result?.entities?.users) { + return result; + } + if (onCreate) { const userCreated = result.entities.users[result.result]; diff --git a/openaev-front/src/admin/components/settings/users/UserForm.tsx b/openaev-front/src/admin/components/settings/users/UserForm.tsx index 730ad59020..e84f908716 100644 --- a/openaev-front/src/admin/components/settings/users/UserForm.tsx +++ b/openaev-front/src/admin/components/settings/users/UserForm.tsx @@ -24,6 +24,8 @@ const UserForm = ({ onSubmit, initialValues = {}, editing, handleClose }: UserFo ? ['user_email'] : ['user_email', 'user_plain_password']; + const phoneRegex = /^\+\d+$/; + const userFormSchemaValidation = z.object({ user_email: z .string() @@ -34,6 +36,23 @@ const UserForm = ({ onSubmit, initialValues = {}, editing, handleClose }: UserFo .string() .nonempty(t('This field is required.')), }), + user_phone: z + .string() + .nullable() + .optional() + .refine( + val => !val || phoneRegex.test(val), + t('Phone number must start with + and contain only digits'), + ), + + user_phone2: z + .string() + .nullable() + .optional() + .refine( + val => !val || phoneRegex.test(val), + t('Phone number must start with + and contain only digits'), + ), }); return ( diff --git a/openaev-front/src/admin/components/settings/users/UserPopover.tsx b/openaev-front/src/admin/components/settings/users/UserPopover.tsx index 00facbb61c..a55dc09716 100644 --- a/openaev-front/src/admin/components/settings/users/UserPopover.tsx +++ b/openaev-front/src/admin/components/settings/users/UserPopover.tsx @@ -2,7 +2,7 @@ import { useContext, useState } from 'react'; import { type OrganizationHelper, type TagHelper } from '../../../../actions/helper'; import { deleteUser, updateUser, updateUserPassword } from '../../../../actions/users/User'; -import { type UserInputForm } from '../../../../actions/users/users-helper'; +import { type UserInputForm, type UserResult } from '../../../../actions/users/users-helper'; import ButtonPopover from '../../../../components/common/ButtonPopover'; import DialogDelete from '../../../../components/common/DialogDelete'; import Drawer from '../../../../components/common/Drawer'; @@ -55,10 +55,11 @@ const UserPopover = ({ user, onUpdate, onDelete }: UserPopoverProps) => { user_tags: data.user_tags?.map((tag: Option) => tag.id), }; - return dispatch(updateUser(user.user_id, inputValues)).then((result: { - result: string; - entities: { users: Record }; - }) => { + return dispatch(updateUser(user.user_id, inputValues)).then((result: UserResult) => { + if (!result?.entities?.users) { + return result; + } + if (onUpdate) { const userUpdated = result.entities.users[result.result]; @@ -73,7 +74,7 @@ const UserPopover = ({ user, onUpdate, onDelete }: UserPopoverProps) => { onUpdate(userToUpdate); } - handleCloseEdit(); + return result.result ? handleCloseEdit() : result; }); }; diff --git a/openaev-front/src/utils/lang/en.json b/openaev-front/src/utils/lang/en.json index 784f9ad9f6..4fae5b9c0e 100644 --- a/openaev-front/src/utils/lang/en.json +++ b/openaev-front/src/utils/lang/en.json @@ -1298,6 +1298,7 @@ "Phase short name": "Phase short name", "Phone number (landline)": "Phone number (landline)", "Phone number (mobile)": "Phone number (mobile)", + "Phone number must start with + and contain only digits": "Phone number must start with + and contain only digits", "phone_number_tooltip": "Phone number should start with a plus sign ( + ). It may contain white spaces or hyphens ( – ) or parenthesis.", "Pipe": "Pipe", "Planning": "Planning", diff --git a/openaev-front/src/utils/lang/fr.json b/openaev-front/src/utils/lang/fr.json index 068e2c075d..c44c18a933 100644 --- a/openaev-front/src/utils/lang/fr.json +++ b/openaev-front/src/utils/lang/fr.json @@ -1298,6 +1298,7 @@ "Phase short name": "Nom court de la phase", "Phone number (landline)": "Numéro de téléphone (fixe)", "Phone number (mobile)": "Numéro de téléphone (mobile)", + "Phone number must start with + and contain only digits": "Le numéro de téléphone doit commencer par + et ne contenir que des chiffres", "phone_number_tooltip": "Le numéro de téléphone devrait commencer par un signe plus ( + ). Il peut contenir des espaces ou des tirets ( – ) ou des parenthèses.", "Pipe": "Tuyau", "Planning": "Planification", diff --git a/openaev-front/src/utils/lang/zh.json b/openaev-front/src/utils/lang/zh.json index e3e58730be..ba8748c4a8 100644 --- a/openaev-front/src/utils/lang/zh.json +++ b/openaev-front/src/utils/lang/zh.json @@ -1298,6 +1298,7 @@ "Phase short name": "阶段简称", "Phone number (landline)": "电话号码(有线)", "Phone number (mobile)": "电话号码(移动)", + "Phone number must start with + and contain only digits": "电话号码必须以 + 开头,且只包含数字", "phone_number_tooltip": "电话号码应以加号 ( + )开头. 它可以包含空格、连字符( - ) 或括号.", "Pipe": "管道", "Planning": "计划", From c9e2108118bdc7793372afbb4b63d006ff162736 Mon Sep 17 00:00:00 2001 From: Camille Roux Date: Thu, 23 Oct 2025 16:43:14 +0200 Subject: [PATCH 5/5] [frontend] Translate User's files from javascript to typescript --- .../components/settings/users/CreateUser.tsx | 6 +---- .../components/settings/users/UserPopover.tsx | 22 +++++-------------- 2 files changed, 6 insertions(+), 22 deletions(-) diff --git a/openaev-front/src/admin/components/settings/users/CreateUser.tsx b/openaev-front/src/admin/components/settings/users/CreateUser.tsx index 667688ae4d..5ee194e9b1 100644 --- a/openaev-front/src/admin/components/settings/users/CreateUser.tsx +++ b/openaev-front/src/admin/components/settings/users/CreateUser.tsx @@ -38,11 +38,7 @@ const CreateUser = ({ onCreate }: CreateUserProps) => { }; return dispatch(addUser(inputValues)).then((result: UserResult) => { - if (!result?.entities?.users) { - return result; - } - - if (onCreate) { + if (result?.entities?.users && onCreate) { const userCreated = result.entities.users[result.result]; const orgId = userCreated.user_organization; diff --git a/openaev-front/src/admin/components/settings/users/UserPopover.tsx b/openaev-front/src/admin/components/settings/users/UserPopover.tsx index a55dc09716..192041c060 100644 --- a/openaev-front/src/admin/components/settings/users/UserPopover.tsx +++ b/openaev-front/src/admin/components/settings/users/UserPopover.tsx @@ -42,9 +42,7 @@ const UserPopover = ({ user, onUpdate, onDelete }: UserPopoverProps) => { }, ); - const handleOpenEdit = () => { - setOpenEdit(true); - }; + const handleOpenEdit = () => setOpenEdit(true); const handleCloseEdit = () => setOpenEdit(false); @@ -56,11 +54,7 @@ const UserPopover = ({ user, onUpdate, onDelete }: UserPopoverProps) => { }; return dispatch(updateUser(user.user_id, inputValues)).then((result: UserResult) => { - if (!result?.entities?.users) { - return result; - } - - if (onUpdate) { + if (result?.entities?.users && onUpdate) { const userUpdated = result.entities.users[result.result]; const orgId = userUpdated.user_organization; @@ -78,19 +72,13 @@ const UserPopover = ({ user, onUpdate, onDelete }: UserPopoverProps) => { }); }; - const handleOpenEditPassword = () => { - setOpenEditPassword(true); - }; + const handleOpenEditPassword = () => setOpenEditPassword(true); const handleCloseEditPassword = () => setOpenEditPassword(false); - const onSubmitEditPassword = (data: ChangePasswordInput) => { - dispatch(updateUserPassword(user.user_id, data)).then(() => handleCloseEditPassword()); - }; + const onSubmitEditPassword = (data: ChangePasswordInput) => dispatch(updateUserPassword(user.user_id, data)).then(() => handleCloseEditPassword()); - const handleOpenDelete = () => { - setOpenDelete(true); - }; + const handleOpenDelete = () => setOpenDelete(true); const handleCloseDelete = () => setOpenDelete(false);