From c8bb645ae804e995f75d7509670f2d6a1d0b6d55 Mon Sep 17 00:00:00 2001 From: poulch Date: Wed, 1 Feb 2023 16:50:43 +0100 Subject: [PATCH] Fix edit user settings (#3079) * Prepare mutation for change password nad user first nad last name * User mutations * User and staff mutation hooks * Split mution for user and staff specific * Ad props types and refetch after user mutations * Add save button status for user mutation * Get rid of maybe * Improve hooks naming * useProfileOperations --- src/auth/hooks/useAuthProvider.ts | 8 +- src/graphql/hooks.generated.ts | 143 +++++++++++++----- src/graphql/types.generated.ts | 27 +++- .../StaffDetailsPage/StaffDetailsPage.tsx | 18 +-- src/staff/hooks/index.ts | 2 + src/staff/hooks/useProfileOperations.ts | 95 ++++++++++++ src/staff/hooks/useStaffUserOperations.ts | 47 ++++++ src/staff/mutations.ts | 32 +++- src/staff/views/StaffDetails.tsx | 129 +++++----------- 9 files changed, 348 insertions(+), 153 deletions(-) create mode 100644 src/staff/hooks/index.ts create mode 100644 src/staff/hooks/useProfileOperations.ts create mode 100644 src/staff/hooks/useStaffUserOperations.ts diff --git a/src/auth/hooks/useAuthProvider.ts b/src/auth/hooks/useAuthProvider.ts index b5c80db78..ac96f4dae 100644 --- a/src/auth/hooks/useAuthProvider.ts +++ b/src/auth/hooks/useAuthProvider.ts @@ -42,12 +42,8 @@ export function useAuthProvider({ notify, apolloClient, }: UseAuthProviderOpts): UserContext { - const { - login, - getExternalAuthUrl, - getExternalAccessToken, - logout, - } = useAuth(); + const { login, getExternalAuthUrl, getExternalAccessToken, logout } = + useAuth(); const navigate = useNavigator(); const { authenticated, authenticating, user } = useAuthState(); const [requestedExternalPluginId] = useLocalStorage( diff --git a/src/graphql/hooks.generated.ts b/src/graphql/hooks.generated.ts index 1178b883f..7c2bf8b75 100644 --- a/src/graphql/hooks.generated.ts +++ b/src/graphql/hooks.generated.ts @@ -14865,6 +14865,77 @@ export function useStaffMemberUpdateMutation(baseOptions?: ApolloReactHooks.Muta export type StaffMemberUpdateMutationHookResult = ReturnType; export type StaffMemberUpdateMutationResult = Apollo.MutationResult; export type StaffMemberUpdateMutationOptions = Apollo.BaseMutationOptions; +export const UserPassowrdChangeDocument = gql` + mutation UserPassowrdChange($newPassword: String!, $oldPassword: String!) { + passwordChange(newPassword: $newPassword, oldPassword: $oldPassword) { + errors { + ...AccountError + } + } +} + ${AccountErrorFragmentDoc}`; +export type UserPassowrdChangeMutationFn = Apollo.MutationFunction; + +/** + * __useUserPassowrdChangeMutation__ + * + * To run a mutation, you first call `useUserPassowrdChangeMutation` within a React component and pass it any options that fit your needs. + * When your component renders, `useUserPassowrdChangeMutation` returns a tuple that includes: + * - A mutate function that you can call at any time to execute the mutation + * - An object with fields that represent the current status of the mutation's execution + * + * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; + * + * @example + * const [userPassowrdChangeMutation, { data, loading, error }] = useUserPassowrdChangeMutation({ + * variables: { + * newPassword: // value for 'newPassword' + * oldPassword: // value for 'oldPassword' + * }, + * }); + */ +export function useUserPassowrdChangeMutation(baseOptions?: ApolloReactHooks.MutationHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return ApolloReactHooks.useMutation(UserPassowrdChangeDocument, options); + } +export type UserPassowrdChangeMutationHookResult = ReturnType; +export type UserPassowrdChangeMutationResult = Apollo.MutationResult; +export type UserPassowrdChangeMutationOptions = Apollo.BaseMutationOptions; +export const UserAccountUpdateDocument = gql` + mutation UserAccountUpdate($input: AccountInput!) { + accountUpdate(input: $input) { + errors { + ...AccountError + } + } +} + ${AccountErrorFragmentDoc}`; +export type UserAccountUpdateMutationFn = Apollo.MutationFunction; + +/** + * __useUserAccountUpdateMutation__ + * + * To run a mutation, you first call `useUserAccountUpdateMutation` within a React component and pass it any options that fit your needs. + * When your component renders, `useUserAccountUpdateMutation` returns a tuple that includes: + * - A mutate function that you can call at any time to execute the mutation + * - An object with fields that represent the current status of the mutation's execution + * + * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; + * + * @example + * const [userAccountUpdateMutation, { data, loading, error }] = useUserAccountUpdateMutation({ + * variables: { + * input: // value for 'input' + * }, + * }); + */ +export function useUserAccountUpdateMutation(baseOptions?: ApolloReactHooks.MutationHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return ApolloReactHooks.useMutation(UserAccountUpdateDocument, options); + } +export type UserAccountUpdateMutationHookResult = ReturnType; +export type UserAccountUpdateMutationResult = Apollo.MutationResult; +export type UserAccountUpdateMutationOptions = Apollo.BaseMutationOptions; export const StaffMemberDeleteDocument = gql` mutation StaffMemberDelete($id: ID!) { staffDelete(id: $id) { @@ -14900,8 +14971,8 @@ export function useStaffMemberDeleteMutation(baseOptions?: ApolloReactHooks.Muta export type StaffMemberDeleteMutationHookResult = ReturnType; export type StaffMemberDeleteMutationResult = Apollo.MutationResult; export type StaffMemberDeleteMutationOptions = Apollo.BaseMutationOptions; -export const StaffAvatarUpdateDocument = gql` - mutation StaffAvatarUpdate($image: Upload!) { +export const UserAvatarUpdateDocument = gql` + mutation UserAvatarUpdate($image: Upload!) { userAvatarUpdate(image: $image) { errors { ...AccountError @@ -14915,34 +14986,34 @@ export const StaffAvatarUpdateDocument = gql` } } ${AccountErrorFragmentDoc}`; -export type StaffAvatarUpdateMutationFn = Apollo.MutationFunction; +export type UserAvatarUpdateMutationFn = Apollo.MutationFunction; /** - * __useStaffAvatarUpdateMutation__ + * __useUserAvatarUpdateMutation__ * - * To run a mutation, you first call `useStaffAvatarUpdateMutation` within a React component and pass it any options that fit your needs. - * When your component renders, `useStaffAvatarUpdateMutation` returns a tuple that includes: + * To run a mutation, you first call `useUserAvatarUpdateMutation` within a React component and pass it any options that fit your needs. + * When your component renders, `useUserAvatarUpdateMutation` returns a tuple that includes: * - A mutate function that you can call at any time to execute the mutation * - An object with fields that represent the current status of the mutation's execution * * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; * * @example - * const [staffAvatarUpdateMutation, { data, loading, error }] = useStaffAvatarUpdateMutation({ + * const [userAvatarUpdateMutation, { data, loading, error }] = useUserAvatarUpdateMutation({ * variables: { * image: // value for 'image' * }, * }); */ -export function useStaffAvatarUpdateMutation(baseOptions?: ApolloReactHooks.MutationHookOptions) { +export function useUserAvatarUpdateMutation(baseOptions?: ApolloReactHooks.MutationHookOptions) { const options = {...defaultOptions, ...baseOptions} - return ApolloReactHooks.useMutation(StaffAvatarUpdateDocument, options); + return ApolloReactHooks.useMutation(UserAvatarUpdateDocument, options); } -export type StaffAvatarUpdateMutationHookResult = ReturnType; -export type StaffAvatarUpdateMutationResult = Apollo.MutationResult; -export type StaffAvatarUpdateMutationOptions = Apollo.BaseMutationOptions; -export const StaffAvatarDeleteDocument = gql` - mutation StaffAvatarDelete { +export type UserAvatarUpdateMutationHookResult = ReturnType; +export type UserAvatarUpdateMutationResult = Apollo.MutationResult; +export type UserAvatarUpdateMutationOptions = Apollo.BaseMutationOptions; +export const UserAvatarDeleteDocument = gql` + mutation UserAvatarDelete { userAvatarDelete { errors { ...AccountError @@ -14956,33 +15027,33 @@ export const StaffAvatarDeleteDocument = gql` } } ${AccountErrorFragmentDoc}`; -export type StaffAvatarDeleteMutationFn = Apollo.MutationFunction; +export type UserAvatarDeleteMutationFn = Apollo.MutationFunction; /** - * __useStaffAvatarDeleteMutation__ + * __useUserAvatarDeleteMutation__ * - * To run a mutation, you first call `useStaffAvatarDeleteMutation` within a React component and pass it any options that fit your needs. - * When your component renders, `useStaffAvatarDeleteMutation` returns a tuple that includes: + * To run a mutation, you first call `useUserAvatarDeleteMutation` within a React component and pass it any options that fit your needs. + * When your component renders, `useUserAvatarDeleteMutation` returns a tuple that includes: * - A mutate function that you can call at any time to execute the mutation * - An object with fields that represent the current status of the mutation's execution * * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; * * @example - * const [staffAvatarDeleteMutation, { data, loading, error }] = useStaffAvatarDeleteMutation({ + * const [userAvatarDeleteMutation, { data, loading, error }] = useUserAvatarDeleteMutation({ * variables: { * }, * }); */ -export function useStaffAvatarDeleteMutation(baseOptions?: ApolloReactHooks.MutationHookOptions) { +export function useUserAvatarDeleteMutation(baseOptions?: ApolloReactHooks.MutationHookOptions) { const options = {...defaultOptions, ...baseOptions} - return ApolloReactHooks.useMutation(StaffAvatarDeleteDocument, options); + return ApolloReactHooks.useMutation(UserAvatarDeleteDocument, options); } -export type StaffAvatarDeleteMutationHookResult = ReturnType; -export type StaffAvatarDeleteMutationResult = Apollo.MutationResult; -export type StaffAvatarDeleteMutationOptions = Apollo.BaseMutationOptions; -export const ChangeStaffPasswordDocument = gql` - mutation ChangeStaffPassword($newPassword: String!, $oldPassword: String!) { +export type UserAvatarDeleteMutationHookResult = ReturnType; +export type UserAvatarDeleteMutationResult = Apollo.MutationResult; +export type UserAvatarDeleteMutationOptions = Apollo.BaseMutationOptions; +export const ChangeUserPasswordDocument = gql` + mutation ChangeUserPassword($newPassword: String!, $oldPassword: String!) { passwordChange(newPassword: $newPassword, oldPassword: $oldPassword) { errors { ...AccountError @@ -14990,33 +15061,33 @@ export const ChangeStaffPasswordDocument = gql` } } ${AccountErrorFragmentDoc}`; -export type ChangeStaffPasswordMutationFn = Apollo.MutationFunction; +export type ChangeUserPasswordMutationFn = Apollo.MutationFunction; /** - * __useChangeStaffPasswordMutation__ + * __useChangeUserPasswordMutation__ * - * To run a mutation, you first call `useChangeStaffPasswordMutation` within a React component and pass it any options that fit your needs. - * When your component renders, `useChangeStaffPasswordMutation` returns a tuple that includes: + * To run a mutation, you first call `useChangeUserPasswordMutation` within a React component and pass it any options that fit your needs. + * When your component renders, `useChangeUserPasswordMutation` returns a tuple that includes: * - A mutate function that you can call at any time to execute the mutation * - An object with fields that represent the current status of the mutation's execution * * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; * * @example - * const [changeStaffPasswordMutation, { data, loading, error }] = useChangeStaffPasswordMutation({ + * const [changeUserPasswordMutation, { data, loading, error }] = useChangeUserPasswordMutation({ * variables: { * newPassword: // value for 'newPassword' * oldPassword: // value for 'oldPassword' * }, * }); */ -export function useChangeStaffPasswordMutation(baseOptions?: ApolloReactHooks.MutationHookOptions) { +export function useChangeUserPasswordMutation(baseOptions?: ApolloReactHooks.MutationHookOptions) { const options = {...defaultOptions, ...baseOptions} - return ApolloReactHooks.useMutation(ChangeStaffPasswordDocument, options); + return ApolloReactHooks.useMutation(ChangeUserPasswordDocument, options); } -export type ChangeStaffPasswordMutationHookResult = ReturnType; -export type ChangeStaffPasswordMutationResult = Apollo.MutationResult; -export type ChangeStaffPasswordMutationOptions = Apollo.BaseMutationOptions; +export type ChangeUserPasswordMutationHookResult = ReturnType; +export type ChangeUserPasswordMutationResult = Apollo.MutationResult; +export type ChangeUserPasswordMutationOptions = Apollo.BaseMutationOptions; export const StaffListDocument = gql` query StaffList($first: Int, $after: String, $last: Int, $before: String, $filter: StaffUserInput, $sort: UserSortingInput) { staffUsers( diff --git a/src/graphql/types.generated.ts b/src/graphql/types.generated.ts index d22874fc6..7fd101484 100644 --- a/src/graphql/types.generated.ts +++ b/src/graphql/types.generated.ts @@ -9144,6 +9144,21 @@ export type StaffMemberUpdateMutationVariables = Exact<{ export type StaffMemberUpdateMutation = { __typename: 'Mutation', staffUpdate: { __typename: 'StaffUpdate', errors: Array<{ __typename: 'StaffError', code: AccountErrorCode, field: string | null, message: string | null }>, user: { __typename: 'User', id: string, email: string, firstName: string, isActive: boolean, lastName: string, permissionGroups: Array<{ __typename: 'Group', id: string, name: string, userCanManage: boolean }> | null, userPermissions: Array<{ __typename: 'UserPermission', code: PermissionEnum, name: string }> | null, avatar: { __typename: 'Image', url: string } | null } | null } | null }; +export type UserPassowrdChangeMutationVariables = Exact<{ + newPassword: Scalars['String']; + oldPassword: Scalars['String']; +}>; + + +export type UserPassowrdChangeMutation = { __typename: 'Mutation', passwordChange: { __typename: 'PasswordChange', errors: Array<{ __typename: 'AccountError', code: AccountErrorCode, field: string | null, addressType: AddressTypeEnum | null, message: string | null }> } | null }; + +export type UserAccountUpdateMutationVariables = Exact<{ + input: AccountInput; +}>; + + +export type UserAccountUpdateMutation = { __typename: 'Mutation', accountUpdate: { __typename: 'AccountUpdate', errors: Array<{ __typename: 'AccountError', code: AccountErrorCode, field: string | null, addressType: AddressTypeEnum | null, message: string | null }> } | null }; + export type StaffMemberDeleteMutationVariables = Exact<{ id: Scalars['ID']; }>; @@ -9151,25 +9166,25 @@ export type StaffMemberDeleteMutationVariables = Exact<{ export type StaffMemberDeleteMutation = { __typename: 'Mutation', staffDelete: { __typename: 'StaffDelete', errors: Array<{ __typename: 'StaffError', code: AccountErrorCode, field: string | null, message: string | null }> } | null }; -export type StaffAvatarUpdateMutationVariables = Exact<{ +export type UserAvatarUpdateMutationVariables = Exact<{ image: Scalars['Upload']; }>; -export type StaffAvatarUpdateMutation = { __typename: 'Mutation', userAvatarUpdate: { __typename: 'UserAvatarUpdate', errors: Array<{ __typename: 'AccountError', code: AccountErrorCode, field: string | null, addressType: AddressTypeEnum | null, message: string | null }>, user: { __typename: 'User', id: string, avatar: { __typename: 'Image', url: string } | null } | null } | null }; +export type UserAvatarUpdateMutation = { __typename: 'Mutation', userAvatarUpdate: { __typename: 'UserAvatarUpdate', errors: Array<{ __typename: 'AccountError', code: AccountErrorCode, field: string | null, addressType: AddressTypeEnum | null, message: string | null }>, user: { __typename: 'User', id: string, avatar: { __typename: 'Image', url: string } | null } | null } | null }; -export type StaffAvatarDeleteMutationVariables = Exact<{ [key: string]: never; }>; +export type UserAvatarDeleteMutationVariables = Exact<{ [key: string]: never; }>; -export type StaffAvatarDeleteMutation = { __typename: 'Mutation', userAvatarDelete: { __typename: 'UserAvatarDelete', errors: Array<{ __typename: 'AccountError', code: AccountErrorCode, field: string | null, addressType: AddressTypeEnum | null, message: string | null }>, user: { __typename: 'User', id: string, avatar: { __typename: 'Image', url: string } | null } | null } | null }; +export type UserAvatarDeleteMutation = { __typename: 'Mutation', userAvatarDelete: { __typename: 'UserAvatarDelete', errors: Array<{ __typename: 'AccountError', code: AccountErrorCode, field: string | null, addressType: AddressTypeEnum | null, message: string | null }>, user: { __typename: 'User', id: string, avatar: { __typename: 'Image', url: string } | null } | null } | null }; -export type ChangeStaffPasswordMutationVariables = Exact<{ +export type ChangeUserPasswordMutationVariables = Exact<{ newPassword: Scalars['String']; oldPassword: Scalars['String']; }>; -export type ChangeStaffPasswordMutation = { __typename: 'Mutation', passwordChange: { __typename: 'PasswordChange', errors: Array<{ __typename: 'AccountError', code: AccountErrorCode, field: string | null, addressType: AddressTypeEnum | null, message: string | null }> } | null }; +export type ChangeUserPasswordMutation = { __typename: 'Mutation', passwordChange: { __typename: 'PasswordChange', errors: Array<{ __typename: 'AccountError', code: AccountErrorCode, field: string | null, addressType: AddressTypeEnum | null, message: string | null }> } | null }; export type StaffListQueryVariables = Exact<{ first?: InputMaybe; diff --git a/src/staff/components/StaffDetailsPage/StaffDetailsPage.tsx b/src/staff/components/StaffDetailsPage/StaffDetailsPage.tsx index 08ce3bcce..1840e8a2f 100644 --- a/src/staff/components/StaffDetailsPage/StaffDetailsPage.tsx +++ b/src/staff/components/StaffDetailsPage/StaffDetailsPage.tsx @@ -93,16 +93,14 @@ const StaffDetailsPage: React.FC = ({ const isActive = isMemberActive(staffMember); const permissionGroups = getMemberPermissionGroups(staffMember); - const [ - permissionGroupsDisplayValues, - setPermissionGroupsDisplayValues, - ] = useStateFromProps( - permissionGroups.map(group => ({ - disabled: !group.userCanManage, - label: group.name, - value: group.id, - })) || [], - ); + const [permissionGroupsDisplayValues, setPermissionGroupsDisplayValues] = + useStateFromProps( + permissionGroups.map(group => ({ + disabled: !group.userCanManage, + label: group.name, + value: group.id, + })) || [], + ); const initialForm: StaffDetailsFormData = { email: staffMember?.email || "", diff --git a/src/staff/hooks/index.ts b/src/staff/hooks/index.ts new file mode 100644 index 000000000..e9700b647 --- /dev/null +++ b/src/staff/hooks/index.ts @@ -0,0 +1,2 @@ +export * from "./useStaffUserOperations"; +export * from "./useProfileOperations"; diff --git a/src/staff/hooks/useProfileOperations.ts b/src/staff/hooks/useProfileOperations.ts new file mode 100644 index 000000000..678df6e53 --- /dev/null +++ b/src/staff/hooks/useProfileOperations.ts @@ -0,0 +1,95 @@ +import { + useChangeUserPasswordMutation, + useUserAccountUpdateMutation, + useUserAvatarDeleteMutation, + useUserAvatarUpdateMutation, +} from "@dashboard/graphql"; +import useNavigator from "@dashboard/hooks/useNavigator"; +import useNotifier from "@dashboard/hooks/useNotifier"; +import { commonMessages, errorMessages } from "@dashboard/intl"; +import { useIntl } from "react-intl"; + +import { staffMemberDetailsUrl } from "../urls"; + +interface UseUserMutationProps { + refetch: () => void; + id: string; + closeModal: () => void; +} + +export const useProfileOperations = ({ + refetch, + id, + closeModal, +}: UseUserMutationProps) => { + const notify = useNotifier(); + const intl = useIntl(); + const navigate = useNavigator(); + + const [updateUserAccount, updateUserAccountOpts] = + useUserAccountUpdateMutation({ + onCompleted: data => { + if (!data.accountUpdate?.errors.length) { + refetch(); + notify({ + status: "success", + text: intl.formatMessage(commonMessages.savedChanges), + }); + } + }, + }); + + const [updateUserAvatar] = useUserAvatarUpdateMutation({ + onCompleted: data => { + if (!data.userAvatarUpdate?.errors.length) { + notify({ + status: "success", + text: intl.formatMessage(commonMessages.savedChanges), + }); + refetch(); + navigate(staffMemberDetailsUrl(id)); + } else { + notify({ + status: "error", + title: intl.formatMessage(errorMessages.imgageUploadErrorTitle), + text: intl.formatMessage(errorMessages.imageUploadErrorText), + }); + } + }, + }); + + const [deleteUserAvatar, deleteAvatarResult] = useUserAvatarDeleteMutation({ + onCompleted: data => { + if (!data.userAvatarDelete?.errors.length) { + notify({ + status: "success", + text: intl.formatMessage(commonMessages.savedChanges), + }); + refetch(); + navigate(staffMemberDetailsUrl(id)); + } + }, + }); + + const [changePassword, changePasswordOpts] = useChangeUserPasswordMutation({ + onCompleted: data => { + if (!data.passwordChange?.errors.length) { + notify({ + status: "success", + text: intl.formatMessage(commonMessages.savedChanges), + }); + closeModal(); + } + }, + }); + + return { + updateUserAccount, + deleteAvatarResult, + deleteUserAvatar, + updateUserAvatar, + changePassword, + changePasswordOpts, + updateUserAccountOpts, + }; +}; diff --git a/src/staff/hooks/useStaffUserOperations.ts b/src/staff/hooks/useStaffUserOperations.ts new file mode 100644 index 000000000..a7b95eb1c --- /dev/null +++ b/src/staff/hooks/useStaffUserOperations.ts @@ -0,0 +1,47 @@ +import { + useStaffMemberDeleteMutation, + useStaffMemberUpdateMutation, +} from "@dashboard/graphql"; +import useNavigator from "@dashboard/hooks/useNavigator"; +import useNotifier from "@dashboard/hooks/useNotifier"; +import { commonMessages } from "@dashboard/intl"; +import { useIntl } from "react-intl"; + +import { staffListUrl } from "../urls"; + +export const useStaffUserOperations = () => { + const notify = useNotifier(); + const intl = useIntl(); + const navigate = useNavigator(); + + const [updateStaffMember, updateStaffMemberOpts] = + useStaffMemberUpdateMutation({ + onCompleted: data => { + if (!data.staffUpdate?.errors.length) { + notify({ + status: "success", + text: intl.formatMessage(commonMessages.savedChanges), + }); + } + }, + }); + + const [deleteStaffMember, deleteResult] = useStaffMemberDeleteMutation({ + onCompleted: data => { + if (!data.staffDelete?.errors.length) { + notify({ + status: "success", + text: intl.formatMessage(commonMessages.savedChanges), + }); + navigate(staffListUrl()); + } + }, + }); + + return { + updateStaffMember, + updateStaffMemberOpts, + deleteStaffMember, + deleteResult, + }; +}; diff --git a/src/staff/mutations.ts b/src/staff/mutations.ts index 95c4a1454..45a9db645 100644 --- a/src/staff/mutations.ts +++ b/src/staff/mutations.ts @@ -26,6 +26,26 @@ export const staffMemberUpdateMutation = gql` } `; +export const userPassowrdChangeMutation = gql` + mutation UserPassowrdChange($newPassword: String!, $oldPassword: String!) { + passwordChange(newPassword: $newPassword, oldPassword: $oldPassword) { + errors { + ...AccountError + } + } + } +`; + +export const userAccountUpdateMutation = gql` + mutation UserAccountUpdate($input: AccountInput!) { + accountUpdate(input: $input) { + errors { + ...AccountError + } + } + } +`; + export const staffMemberDeleteMutation = gql` mutation StaffMemberDelete($id: ID!) { staffDelete(id: $id) { @@ -36,8 +56,8 @@ export const staffMemberDeleteMutation = gql` } `; -export const staffAvatarUpdateMutation = gql` - mutation StaffAvatarUpdate($image: Upload!) { +export const userAvatarUpdateMutation = gql` + mutation UserAvatarUpdate($image: Upload!) { userAvatarUpdate(image: $image) { errors { ...AccountError @@ -52,8 +72,8 @@ export const staffAvatarUpdateMutation = gql` } `; -export const staffAvatarDeleteMutation = gql` - mutation StaffAvatarDelete { +export const userAvatarDeleteMutation = gql` + mutation UserAvatarDelete { userAvatarDelete { errors { ...AccountError @@ -68,8 +88,8 @@ export const staffAvatarDeleteMutation = gql` } `; -export const changeStaffPassword = gql` - mutation ChangeStaffPassword($newPassword: String!, $oldPassword: String!) { +export const changeUserPassword = gql` + mutation ChangeUserPassword($newPassword: String!, $oldPassword: String!) { passwordChange(newPassword: $newPassword, oldPassword: $oldPassword) { errors { ...AccountError diff --git a/src/staff/views/StaffDetails.tsx b/src/staff/views/StaffDetails.tsx index 56bc0ee2f..266507e9b 100644 --- a/src/staff/views/StaffDetails.tsx +++ b/src/staff/views/StaffDetails.tsx @@ -4,23 +4,9 @@ import NotFoundPage from "@dashboard/components/NotFoundPage"; import { hasPermissions } from "@dashboard/components/RequirePermissions"; import { WindowTitle } from "@dashboard/components/WindowTitle"; import { DEFAULT_INITIAL_SEARCH_DATA } from "@dashboard/config"; -import { - PermissionEnum, - useChangeStaffPasswordMutation, - useStaffAvatarDeleteMutation, - useStaffAvatarUpdateMutation, - useStaffMemberDeleteMutation, - useStaffMemberDetailsQuery, - useStaffMemberUpdateMutation, -} from "@dashboard/graphql"; +import { PermissionEnum, useStaffMemberDetailsQuery } from "@dashboard/graphql"; import useNavigator from "@dashboard/hooks/useNavigator"; -import useNotifier from "@dashboard/hooks/useNotifier"; -import { commonMessages, errorMessages } from "@dashboard/intl"; -import { - extractMutationErrors, - getStringOrPlaceholder, - maybe, -} from "@dashboard/misc"; +import { extractMutationErrors, getStringOrPlaceholder } from "@dashboard/misc"; import usePermissionGroupSearch from "@dashboard/searches/usePermissionGroupSearch"; import { mapEdgesToItems } from "@dashboard/utils/maps"; import { DialogContentText } from "@material-ui/core"; @@ -31,6 +17,7 @@ import StaffDetailsPage, { StaffDetailsFormData, } from "../components/StaffDetailsPage/StaffDetailsPage"; import StaffPasswordResetDialog from "../components/StaffPasswordResetDialog"; +import { useProfileOperations, useStaffUserOperations } from "../hooks"; import { staffListUrl, staffMemberDetailsUrl, @@ -45,7 +32,6 @@ interface OrderListProps { export const StaffDetails: React.FC = ({ id, params }) => { const navigate = useNavigator(); - const notify = useNotifier(); const user = useUser(); const intl = useIntl(); @@ -64,24 +50,28 @@ export const StaffDetails: React.FC = ({ id, params }) => { variables: { id }, skip: isUserSameAsViewer, }); + const { + deleteResult, + deleteStaffMember, + updateStaffMember, + updateStaffMemberOpts, + } = useStaffUserOperations(); + + const { + updateUserAccount, + updateUserAccountOpts, + changePassword, + changePasswordOpts, + deleteAvatarResult, + deleteUserAvatar, + updateUserAvatar, + } = useProfileOperations({ closeModal, id, refetch }); const staffMember = isUserSameAsViewer ? user.user : data?.user; const hasManageStaffPermission = hasPermissions(user.user.userPermissions, [ PermissionEnum.MANAGE_STAFF, ]); - const [changePassword, changePasswordOpts] = useChangeStaffPasswordMutation({ - onCompleted: data => { - if (data.passwordChange.errors.length === 0) { - notify({ - status: "success", - text: intl.formatMessage(commonMessages.savedChanges), - }); - closeModal(); - } - }, - }); - const { loadMore: loadMorePermissionGroups, search: searchPermissionGroups, @@ -91,66 +81,11 @@ export const StaffDetails: React.FC = ({ id, params }) => { skip: !hasManageStaffPermission, }); - const [updateStaffMember, updateStaffMemberOpts] = - useStaffMemberUpdateMutation({ - onCompleted: data => { - if (!maybe(() => data.staffUpdate.errors.length !== 0)) { - notify({ - status: "success", - text: intl.formatMessage(commonMessages.savedChanges), - }); - } - }, - }); - - const [deleteStaffMember, deleteResult] = useStaffMemberDeleteMutation({ - onCompleted: data => { - if (!maybe(() => data.staffDelete.errors.length !== 0)) { - notify({ - status: "success", - text: intl.formatMessage(commonMessages.savedChanges), - }); - navigate(staffListUrl()); - } - }, - }); - - const [updateStaffAvatar] = useStaffAvatarUpdateMutation({ - onCompleted: data => { - if (!maybe(() => data.userAvatarUpdate.errors.length !== 0)) { - notify({ - status: "success", - text: intl.formatMessage(commonMessages.savedChanges), - }); - refetch(); - } else { - notify({ - status: "error", - title: intl.formatMessage(errorMessages.imgageUploadErrorTitle), - text: intl.formatMessage(errorMessages.imageUploadErrorText), - }); - } - }, - }); - - const [deleteStaffAvatar, deleteAvatarResult] = useStaffAvatarDeleteMutation({ - onCompleted: data => { - if (!maybe(() => data.userAvatarDelete.errors.length !== 0)) { - notify({ - status: "success", - text: intl.formatMessage(commonMessages.savedChanges), - }); - navigate(staffMemberDetailsUrl(id)); - refetch(); - } - }, - }); - if (staffMember === null) { return ; } - const handleUpdate = (formData: StaffDetailsFormData) => + const handleStaffUpdate = (formData: StaffDetailsFormData) => extractMutationErrors( updateStaffMember({ variables: { @@ -168,6 +103,18 @@ export const StaffDetails: React.FC = ({ id, params }) => { }), ); + const handleUserUpdate = (formData: StaffDetailsFormData) => + extractMutationErrors( + updateUserAccount({ + variables: { + input: { + firstName: formData.firstName, + lastName: formData.lastName, + }, + }, + }), + ); + return ( <> @@ -193,9 +140,9 @@ export const StaffDetails: React.FC = ({ id, params }) => { }), ) } - onSubmit={handleUpdate} + onSubmit={isUserSameAsViewer ? handleUserUpdate : handleStaffUpdate} onImageUpload={file => - updateStaffAvatar({ + updateUserAvatar({ variables: { image: file, }, @@ -212,7 +159,11 @@ export const StaffDetails: React.FC = ({ id, params }) => { searchPermissionGroupsOpts?.data?.search, )} staffMember={staffMember} - saveButtonBarState={updateStaffMemberOpts.status} + saveButtonBarState={ + isUserSameAsViewer + ? updateUserAccountOpts.status + : updateStaffMemberOpts.status + } fetchMorePermissionGroups={{ hasMore: searchPermissionGroupsOpts.data?.search?.pageInfo.hasNextPage, @@ -257,7 +208,7 @@ export const StaffDetails: React.FC = ({ id, params }) => { confirmButtonState={deleteAvatarResult.status} variant="delete" onClose={closeModal} - onConfirm={deleteStaffAvatar} + onConfirm={deleteUserAvatar} >