diff --git a/editor.planx.uk/src/pages/FlowEditor/components/Team/components/AddNewEditorModal.tsx b/editor.planx.uk/src/pages/FlowEditor/components/Team/components/AddNewEditorModal.tsx index 0f942e9de0..b87f3217c0 100644 --- a/editor.planx.uk/src/pages/FlowEditor/components/Team/components/AddNewEditorModal.tsx +++ b/editor.planx.uk/src/pages/FlowEditor/components/Team/components/AddNewEditorModal.tsx @@ -24,13 +24,15 @@ import { optimisticallyUpdateMembersTable } from "./lib/optimisticallyUpdateMemb export const AddNewEditorModal = ({ showModal, setShowModal, - setShowToast, + setShowSuccessToast, + setShowErrorToast, }: AddNewEditorModalProps) => { const [showUserAlreadyExistsError, setShowUserAlreadyExistsError] = useState(false); const clearErrors = () => { setShowUserAlreadyExistsError(false); + setShowErrorToast(false); }; const handleSubmit = async ( @@ -39,7 +41,7 @@ export const AddNewEditorModal = ({ ) => { const { teamId, teamSlug } = useStore.getState(); - const newUserId = await createAndAddUserToTeam( + const createUserResult = await createAndAddUserToTeam( values.email, values.firstName, values.lastName, @@ -49,16 +51,19 @@ export const AddNewEditorModal = ({ if (isUserAlreadyExistsError(err.message)) { setShowUserAlreadyExistsError(true); } + if (err.message === "Unable to create user") { + setShowErrorToast(true); + } console.error(err); }); - if (!newUserId) { + if (!createUserResult) { return; } clearErrors(); - optimisticallyUpdateMembersTable(values, newUserId); + optimisticallyUpdateMembersTable(values, createUserResult.id); setShowModal(false); - setShowToast(true); + setShowSuccessToast(true); resetForm({ values }); }; diff --git a/editor.planx.uk/src/pages/FlowEditor/components/Team/components/MembersTable.tsx b/editor.planx.uk/src/pages/FlowEditor/components/Team/components/MembersTable.tsx index 22420bd017..cc5118c439 100644 --- a/editor.planx.uk/src/pages/FlowEditor/components/Team/components/MembersTable.tsx +++ b/editor.planx.uk/src/pages/FlowEditor/components/Team/components/MembersTable.tsx @@ -19,9 +19,10 @@ export const MembersTable = ({ showAddMemberButton, }: MembersTableProps) => { const [showModal, setShowModal] = useState(false); - const [showToast, setShowToast] = useState(false); + const [showSuccessToast, setShowSuccessToast] = useState(false); + const [showErrorToast, setShowErrorToast] = useState(false); - const handleCloseToast = ( + const handleCloseSuccessToast = ( _event?: React.SyntheticEvent | Event, reason?: string, ) => { @@ -29,7 +30,18 @@ export const MembersTable = ({ return; } - setShowToast(false); + setShowSuccessToast(false); + }; + + const handleCloseErrorToast = ( + _event?: React.SyntheticEvent | Event, + reason?: string, + ) => { + if (reason === "clickaway") { + return; + } + + setShowErrorToast(false); }; const roleLabels: Record = { @@ -114,12 +126,12 @@ export const MembersTable = ({ {showAddMemberButton && ( @@ -127,10 +139,26 @@ export const MembersTable = ({ )} + {showAddMemberButton && ( + + + Failed to add new user, please try again + + + )} {showModal && ( diff --git a/editor.planx.uk/src/pages/FlowEditor/components/Team/queries/createAndAddUserToTeam.tsx b/editor.planx.uk/src/pages/FlowEditor/components/Team/queries/createAndAddUserToTeam.tsx index 46a4161e55..6bd17e6ab0 100644 --- a/editor.planx.uk/src/pages/FlowEditor/components/Team/queries/createAndAddUserToTeam.tsx +++ b/editor.planx.uk/src/pages/FlowEditor/components/Team/queries/createAndAddUserToTeam.tsx @@ -1,8 +1,12 @@ -import { gql } from "@apollo/client"; +import { FetchResult, gql } from "@apollo/client"; import { GET_USERS_FOR_TEAM_QUERY } from "routes/teamMembers"; import { client } from "../../../../../lib/graphql"; +type CreateAndAddUserResponse = FetchResult<{ + insert_users_one: { id: number; __typename: "users" }; +}>; + export const createAndAddUserToTeam = async ( email: string, firstName: string, @@ -11,7 +15,7 @@ export const createAndAddUserToTeam = async ( teamSlug: string, ) => { // NB: the user is hard-coded with the 'teamEditor' role for now - const response = (await client.mutate({ + const response: CreateAndAddUserResponse = await client.mutate({ mutation: gql` mutation CreateAndAddUserToTeam( $email: String! @@ -40,6 +44,9 @@ export const createAndAddUserToTeam = async ( refetchQueries: [ { query: GET_USERS_FOR_TEAM_QUERY, variables: { teamSlug } }, ], - })) as any; - return response.data.insert_users_one; + }); + if (response.data) { + return response.data.insert_users_one; + } + throw new Error("Unable to create user"); }; diff --git a/editor.planx.uk/src/pages/FlowEditor/components/Team/tests/TeamMembers.addNewEditor.errors.serverSide.test.tsx b/editor.planx.uk/src/pages/FlowEditor/components/Team/tests/TeamMembers.addNewEditor.errors.serverSide.test.tsx new file mode 100644 index 0000000000..22fd4e5db2 --- /dev/null +++ b/editor.planx.uk/src/pages/FlowEditor/components/Team/tests/TeamMembers.addNewEditor.errors.serverSide.test.tsx @@ -0,0 +1,36 @@ +import { screen } from "@testing-library/react"; +import { FullStore, useStore } from "pages/FlowEditor/lib/store"; +import { vi } from "vitest"; + +import { setupTeamMembersScreen } from "./helpers/setupTeamMembersScreen"; +import { userTriesToAddNewEditor } from "./helpers/userTriesToAddNewEditor"; +import { mockTeamMembersData } from "./mocks/mockTeamMembersData"; +import { alreadyExistingUser } from "./mocks/mockUsers"; + +let initialState: FullStore; +vi.mock( + "pages/FlowEditor/components/Team/queries/createAndAddUserToTeam.tsx", + () => ({ + createAndAddUserToTeam: vi.fn().mockRejectedValue({ + message: "Unable to create user", + }), + }), +); + +describe("when a user fills in the 'add a new editor' form correctly but there is a server-side error", () => { + afterAll(() => useStore.setState(initialState)); + beforeEach(async () => { + useStore.setState({ + teamMembers: [...mockTeamMembersData, alreadyExistingUser], + }); + + const { user } = await setupTeamMembersScreen(); + await userTriesToAddNewEditor(user); + }); + + it("shows an appropriate error message", async () => { + expect( + await screen.findByText(/Failed to add new user, please try again/), + ).toBeInTheDocument(); + }); +}); diff --git a/editor.planx.uk/src/pages/FlowEditor/components/Team/tests/TeamMembers.addNewEditor.errors.test.tsx b/editor.planx.uk/src/pages/FlowEditor/components/Team/tests/TeamMembers.addNewEditor.errors.userAlreadyExists.test.tsx similarity index 99% rename from editor.planx.uk/src/pages/FlowEditor/components/Team/tests/TeamMembers.addNewEditor.errors.test.tsx rename to editor.planx.uk/src/pages/FlowEditor/components/Team/tests/TeamMembers.addNewEditor.errors.userAlreadyExists.test.tsx index ce4d5feec9..11f53bc288 100644 --- a/editor.planx.uk/src/pages/FlowEditor/components/Team/tests/TeamMembers.addNewEditor.errors.test.tsx +++ b/editor.planx.uk/src/pages/FlowEditor/components/Team/tests/TeamMembers.addNewEditor.errors.userAlreadyExists.test.tsx @@ -16,7 +16,6 @@ vi.mock( }), }), ); - let initialState: FullStore; describe("when a user fills in the 'add a new editor' form correctly but the user already exists", () => { diff --git a/editor.planx.uk/src/pages/FlowEditor/components/Team/tests/TeamMembers.addNewEditor.test.tsx b/editor.planx.uk/src/pages/FlowEditor/components/Team/tests/TeamMembers.addNewEditor.test.tsx index 7d0be501c3..7b1753dbf5 100644 --- a/editor.planx.uk/src/pages/FlowEditor/components/Team/tests/TeamMembers.addNewEditor.test.tsx +++ b/editor.planx.uk/src/pages/FlowEditor/components/Team/tests/TeamMembers.addNewEditor.test.tsx @@ -80,7 +80,7 @@ describe("when the addNewEditor modal is rendered", () => { {}} - setShowToast={() => {}} + setShowSuccessToast={() => {}} /> , ); diff --git a/editor.planx.uk/src/pages/FlowEditor/components/Team/types.ts b/editor.planx.uk/src/pages/FlowEditor/components/Team/types.ts index ef03981734..2eb9dfd373 100644 --- a/editor.planx.uk/src/pages/FlowEditor/components/Team/types.ts +++ b/editor.planx.uk/src/pages/FlowEditor/components/Team/types.ts @@ -12,7 +12,8 @@ export interface MembersTableProps { export interface AddNewEditorModalProps { showModal: boolean; setShowModal: React.Dispatch>; - setShowToast: React.Dispatch>; + setShowSuccessToast: React.Dispatch>; + setShowErrorToast: React.Dispatch>; } export interface AddNewEditorFormValues {