diff --git a/editor.planx.uk/src/pages/FlowEditor/components/Team/TeamMembers.tsx b/editor.planx.uk/src/pages/FlowEditor/components/Team/TeamMembers.tsx index e6b0535e30..ab914062d6 100644 --- a/editor.planx.uk/src/pages/FlowEditor/components/Team/TeamMembers.tsx +++ b/editor.planx.uk/src/pages/FlowEditor/components/Team/TeamMembers.tsx @@ -41,7 +41,7 @@ export const TeamMembers = () => { return ( - + Team editors @@ -52,6 +52,7 @@ export const TeamMembers = () => { members={activeMembers} showAddMemberButton={isNotTemplatesTeam} showEditMemberButton={isNotTemplatesTeam} + showRemoveMemberButton={isNotTemplatesTeam} /> @@ -67,7 +68,7 @@ export const TeamMembers = () => { /> {archivedMembers.length > 0 && ( - + Archived team editors diff --git a/editor.planx.uk/src/pages/FlowEditor/components/Team/components/EditorUpsertModal.tsx b/editor.planx.uk/src/pages/FlowEditor/components/Team/components/EditorUpsertModal.tsx index 08d9e1e050..b0818de316 100644 --- a/editor.planx.uk/src/pages/FlowEditor/components/Team/components/EditorUpsertModal.tsx +++ b/editor.planx.uk/src/pages/FlowEditor/components/Team/components/EditorUpsertModal.tsx @@ -1,6 +1,5 @@ import Box from "@mui/material/Box"; import Button from "@mui/material/Button"; -import Dialog from "@mui/material/Dialog"; import DialogActions from "@mui/material/DialogActions"; import DialogContent from "@mui/material/DialogContent"; import Typography from "@mui/material/Typography"; @@ -19,16 +18,17 @@ import { } from "../errors/addNewEditorErrors"; import { upsertEditorSchema } from "../formSchema"; import { createAndAddUserToTeam } from "../queries/createAndAddUserToTeam"; +import { updateTeamMember } from "../queries/updateUser"; +import { SettingsDialog } from "../styles"; import { AddNewEditorFormValues, EditorModalProps } from "../types"; import { optimisticallyAddNewMember, optimisticallyUpdateExistingMember, } from "./lib/optimisticallyUpdateMembersTable"; -import { updateTeamMember } from "../queries/updateUser"; export const EditorUpsertModal = ({ - showModal, setShowModal, + showModal, initialValues, actionType, }: EditorModalProps) => { @@ -43,7 +43,7 @@ export const EditorUpsertModal = ({ const handleSubmit = async ( values: AddNewEditorFormValues, - { resetForm }: FormikHelpers + { resetForm }: FormikHelpers, ) => { switch (actionType) { case "add": @@ -63,7 +63,7 @@ export const EditorUpsertModal = ({ formik.values.firstName, formik.values.lastName, teamId, - teamSlug + teamSlug, ).catch((err) => { if (isUserAlreadyExistsError(err.message)) { setShowUserAlreadyExistsError(true); @@ -88,7 +88,7 @@ export const EditorUpsertModal = ({ } const response = await updateTeamMember( initialValues.id, - formik.values + formik.values, ).catch((err) => { if (isUserAlreadyExistsError(err.message)) { setShowUserAlreadyExistsError(true); @@ -120,22 +120,10 @@ export const EditorUpsertModal = ({ }); return ( - ({ - width: "100%", - maxWidth: theme.breakpoints.values.md, - borderRadius: 0, - borderTop: `20px solid ${theme.palette.primary.main}`, - background: theme.palette.background.paper, - margin: theme.spacing(2), - }), - }} - open={showModal} + data-testid={`dialog-${actionType}-user`} + open={showModal || false} onClose={() => setShowModal(false)} >
@@ -235,6 +223,6 @@ export const EditorUpsertModal = ({
-
+ ); }; 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 45d32eed65..9c65ad4334 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 @@ -1,6 +1,7 @@ import Box from "@mui/material/Box"; import Button from "@mui/material/Button"; import Chip from "@mui/material/Chip"; +import Dialog from "@mui/material/Dialog"; import { styled } from "@mui/material/styles"; import Table from "@mui/material/Table"; import TableBody from "@mui/material/TableBody"; @@ -13,17 +14,29 @@ import React, { useState } from "react"; import Permission from "ui/editor/Permission"; import { StyledAvatar, StyledTableRow } from "./../styles"; -import { MembersTableProps, TeamMember } from "./../types"; +import { ActionType, MembersTableProps, TeamMember } from "./../types"; import { EditorUpsertModal } from "./EditorUpsertModal"; +import { RemoveUserModal } from "./RemoveUserModal"; -const EditUserButton = styled(Button)(({ theme }) => ({ - color: theme.palette.primary.main, +const TableRowButton = styled(Button)(({ theme }) => ({ textDecoration: "underline", boxShadow: "none", "&:hover": { boxShadow: "none", - color: theme.palette.primary.main, textDecoration: "underline", + backgroundColor: theme.palette.action.hover, + }, +})); +const EditUserButton = styled(TableRowButton)(({ theme }) => ({ + color: theme.palette.primary.light, + "&:hover": { + color: theme.palette.primary.dark, + }, +})); +const RemoveUserButton = styled(TableRowButton)(({ theme }) => ({ + color: theme.palette.text.secondary, + "&:hover": { + color: theme.palette.secondary.contrastText, }, })); @@ -31,9 +44,10 @@ export const MembersTable = ({ members, showAddMemberButton, showEditMemberButton, + showRemoveMemberButton, }: MembersTableProps) => { - const [showAddModal, setShowAddModal] = useState(false); - const [showUpdateModal, setShowUpdateModal] = useState(false); + const [showModal, setShowModal] = useState(false); + const [actionType, setActionType] = useState("add"); const [initialValues, setInitialValues] = useState(); const roleLabels: Record = { @@ -42,6 +56,22 @@ export const MembersTable = ({ teamViewer: "Viewer", }; + const editUser = (member: TeamMember) => { + setActionType("edit"); + setShowModal(true); + setInitialValues(member); + }; + const removeUser = (member: TeamMember) => { + setActionType("remove"); + setShowModal(true); + setInitialValues(member); + }; + const addUser = () => { + setActionType("add"); + setShowModal(true); + setInitialValues(undefined); + }; + const getRoleLabel = (role: string) => { return roleLabels[role] || role; }; @@ -62,8 +92,7 @@ export const MembersTable = ({ { - setInitialValues(undefined); - setShowAddModal(true); + addUser(); }} > Add a new editor @@ -72,12 +101,12 @@ export const MembersTable = ({ )} - {showAddModal && ( + {showModal && ( )} @@ -99,6 +128,10 @@ export const MembersTable = ({ Email {" "} + { + // empty table cells for styling across buttons + } + @@ -130,59 +163,68 @@ export const MembersTable = ({ /> {member.email} - {showEditMemberButton && ( + - + {showEditMemberButton && ( { - setShowUpdateModal(true); - setInitialValues(member); + editUser(member); }} - data-testId={`edit-button-${i}`} + data-testid={`edit-button-${member.id}`} > Edit - + )} + + + + + {showRemoveMemberButton && ( + { + removeUser(member); + }} + data-testid={`remove-button-${member.id}`} + > + Remove + + )} - )} + ))} {showAddMemberButton && ( - - - - { - setInitialValues(undefined); - setShowAddModal(true); - }} - > - Add a new editor - - - - + + + { + addUser(); + }} + > + Add a new editor + + + )} - {showAddModal && ( - - )} - {showUpdateModal && ( - - )} + {showModal && + (actionType === "remove" ? ( + + ) : ( + + ))} ); }; diff --git a/editor.planx.uk/src/pages/FlowEditor/components/Team/components/RemoveUserModal.tsx b/editor.planx.uk/src/pages/FlowEditor/components/Team/components/RemoveUserModal.tsx new file mode 100644 index 0000000000..f4deca85ed --- /dev/null +++ b/editor.planx.uk/src/pages/FlowEditor/components/Team/components/RemoveUserModal.tsx @@ -0,0 +1,105 @@ +import Box from "@mui/material/Box"; +import Button from "@mui/material/Button"; +import DialogActions from "@mui/material/DialogActions"; +import DialogContent from "@mui/material/DialogContent"; +import Typography from "@mui/material/Typography"; +import { useToast } from "hooks/useToast"; +import { useStore } from "pages/FlowEditor/lib/store"; +import React from "react"; + +import { SettingsDialog } from "../styles"; +import { EditorModalProps } from "../types"; +import { optimisticallyUpdateExistingMember } from "./lib/optimisticallyUpdateMembersTable"; + +export const RemoveUserModal = ({ + setShowModal, + showModal, + actionType, + initialValues, +}: EditorModalProps) => { + const toast = useToast(); + + const removeUser = useStore.getState().deleteUser; + const handleClick = async () => { + if (!initialValues?.id) { + return; + } + const response = await removeUser(initialValues.id).catch((err) => { + if (err.message === "Unable to remove user") { + toast.error( + `Failed to remove ${initialValues.firstName} ${initialValues.lastName}, try again`, + ); + } + console.error(err); + }); + + if (!response) { + return; + } + optimisticallyUpdateExistingMember( + { ...initialValues, email: null }, + initialValues.id, + ); + setShowModal(false); + toast.success( + `Successfully removed ${initialValues.firstName} ${initialValues.lastName}`, + ); + }; + + return ( + setShowModal(false)} + fullWidth + > + + + + Remove a user + + + + + {`Do you want to remove ${initialValues?.firstName} ${initialValues?.lastName}?`} + +
+ + {`They will be moved to Archived Users and no longer have access to PlanX`} + +
+
+ + + + + + +
+ ); +}; diff --git a/editor.planx.uk/src/pages/FlowEditor/components/Team/components/lib/optimisticallyUpdateMembersTable.tsx b/editor.planx.uk/src/pages/FlowEditor/components/Team/components/lib/optimisticallyUpdateMembersTable.tsx index 45b45299f2..a499633f7f 100644 --- a/editor.planx.uk/src/pages/FlowEditor/components/Team/components/lib/optimisticallyUpdateMembersTable.tsx +++ b/editor.planx.uk/src/pages/FlowEditor/components/Team/components/lib/optimisticallyUpdateMembersTable.tsx @@ -1,6 +1,10 @@ import { useStore } from "pages/FlowEditor/lib/store"; -import { AddNewEditorFormValues, TeamMember } from "../../types"; +import { + AddNewEditorFormValues, + TeamMember, + UpdateEditorFormValues, +} from "../../types"; export const optimisticallyAddNewMember = async ( values: AddNewEditorFormValues, @@ -17,7 +21,7 @@ export const optimisticallyAddNewMember = async ( }; export const optimisticallyUpdateExistingMember = async ( - values: AddNewEditorFormValues, + values: UpdateEditorFormValues, userId: number, ) => { const existingMembers = useStore.getState().teamMembers; diff --git a/editor.planx.uk/src/pages/FlowEditor/components/Team/styles.ts b/editor.planx.uk/src/pages/FlowEditor/components/Team/styles.ts index f847317646..704601a98c 100644 --- a/editor.planx.uk/src/pages/FlowEditor/components/Team/styles.ts +++ b/editor.planx.uk/src/pages/FlowEditor/components/Team/styles.ts @@ -1,4 +1,5 @@ import Avatar from "@mui/material/Avatar"; +import Dialog from "@mui/material/Dialog"; import { styled } from "@mui/material/styles"; import TableRow from "@mui/material/TableRow"; import { FONT_WEIGHT_SEMI_BOLD } from "theme"; @@ -16,3 +17,14 @@ export const StyledTableRow = styled(TableRow)(({ theme }) => ({ background: theme.palette.background.paper, }, })); + +export const SettingsDialog = styled(Dialog)(({ theme }) => ({ + "& .MuiDialog-paper": { + width: "100%", + maxWidth: theme.breakpoints.values.md, + borderRadius: 0, + borderTop: `20px solid ${theme.palette.primary.main}`, + background: theme.palette.background.paper, + margin: theme.spacing(2), + }, +})); diff --git a/editor.planx.uk/src/pages/FlowEditor/components/Team/tests/TeamMembers.addNewEditor.errors.userAlreadyExists.test.tsx b/editor.planx.uk/src/pages/FlowEditor/components/Team/tests/TeamMembers.addNewEditor.errors.userAlreadyExists.test.tsx index d2ac094305..15b30d22de 100644 --- a/editor.planx.uk/src/pages/FlowEditor/components/Team/tests/TeamMembers.addNewEditor.errors.userAlreadyExists.test.tsx +++ b/editor.planx.uk/src/pages/FlowEditor/components/Team/tests/TeamMembers.addNewEditor.errors.userAlreadyExists.test.tsx @@ -31,7 +31,7 @@ describe("when a user fills in the 'add a new editor' form correctly but the use }); it("shows an appropriate error message", async () => { - const addNewEditorModal = await screen.findByTestId("dialog-create-user"); + const addNewEditorModal = await screen.findByTestId("dialog-add-user"); expect( await within(addNewEditorModal).findByText(/User already exists/), diff --git a/editor.planx.uk/src/pages/FlowEditor/components/Team/tests/TeamMembers.removeUser.test.tsx b/editor.planx.uk/src/pages/FlowEditor/components/Team/tests/TeamMembers.removeUser.test.tsx new file mode 100644 index 0000000000..e07a0c74c9 --- /dev/null +++ b/editor.planx.uk/src/pages/FlowEditor/components/Team/tests/TeamMembers.removeUser.test.tsx @@ -0,0 +1,151 @@ +import { screen, waitFor, within } from "@testing-library/react"; +import { useStore } from "pages/FlowEditor/lib/store"; +import { vi } from "vitest"; +import { axe } from "vitest-axe"; + +import { setupTeamMembersScreen } from "./helpers/setupTeamMembersScreen"; +import { mockTeamMembersData } from "./mocks/mockTeamMembersData"; +import { mockPlainUser, mockPlatformAdminUser } from "./mocks/mockUsers"; + +const mockRemoveUser = vi.fn().mockResolvedValue(true); +vi.mock("@opensystemslab/planx-core", () => { + return { + CoreDomainClient: vi.fn().mockImplementation(() => ({ + user: { + delete: () => mockRemoveUser(), + }, + })), + }; +}); + +describe("when a user presses 'remove' button", () => { + let axeContainer: HTMLElement; + beforeEach(async () => { + useStore.setState({ + teamMembers: mockTeamMembersData, + user: mockPlatformAdminUser, + teamSlug: "planx", + }); + const { user, container } = await setupTeamMembersScreen(); + + const teamEditorsTable = screen.getByTestId("team-editors"); + const removeRowButton = await within(teamEditorsTable).findByTestId( + "remove-button-3", + ); + axeContainer = container; + await user.click(removeRowButton); + // Start each test with an open modal + }); + + it("the remove a user modal should appear", async () => { + const removeModal = screen.queryByTestId("modal-remove-user"); + expect(removeModal).toBeInTheDocument(); + }); + + it("should show an remove message and a button to remove the user", async () => { + const removeModal = screen.getByTestId("modal-remove-user"); + + const removeTitle = within(removeModal).queryByText("Remove a user"); + + expect(removeTitle).toBeInTheDocument(); + + const removeMessage = within(removeModal).queryByText( + /Do you want to remove/, + ); + expect(removeMessage).toBeInTheDocument(); + + const removeButton = screen.queryByRole("button", { + name: /Remove user/, + }); + + expect(removeButton).toBeInTheDocument(); + }); + + it("should not have any accessibility issues", async () => { + await screen.findByTestId("modal-remove-user"); + const results = await axe(axeContainer); + expect(results).toHaveNoViolations(); + }); +}); + +describe("when a user clicks 'Remove user' button", () => { + beforeEach(async () => { + useStore.setState({ + teamMembers: mockTeamMembersData, + user: mockPlatformAdminUser, + teamSlug: "planx", + }); + const { user } = await setupTeamMembersScreen(); + + const teamEditorsTable = screen.getByTestId("team-editors"); + + const removeRowButton = + within(teamEditorsTable).getByTestId("remove-button-3"); + + await user.click(removeRowButton); + + const removeButton = screen.getByTestId("modal-remove-user-button"); + await user.click(removeButton); + }); + + it("should close the modal", async () => { + expect(mockRemoveUser).toHaveBeenCalled(); + const removeModal = screen.queryByTestId("modal-remove-user"); + + await waitFor(() => { + expect(removeModal).not.toBeInTheDocument(); + }); + }); + + it("should move the user to Archived Members table", async () => { + const archiveTable = screen.getByTestId("archived-members"); + + const archivedBill = within(archiveTable).queryByText(/Bilbo/); + + expect(archivedBill).toBeInTheDocument(); + }); + + it("should display a success toast message", async () => { + const successToast = screen.queryByText( + /Successfully removed Bilbo Baggins/i, + ); + expect(successToast).toBeInTheDocument(); + }); +}); + +describe("'remove' button is hidden from Templates team", () => { + beforeEach(async () => { + useStore.setState({ + teamMembers: mockTeamMembersData, + user: mockPlatformAdminUser, + teamSlug: "templates", + }); + }); + + it("hides the button on the Templates team", async () => { + const { user: _user } = await setupTeamMembersScreen(); + const teamEditorsTable = screen.getByTestId("team-editors"); + const editButton = + within(teamEditorsTable).queryByTestId("remove-button-3"); + expect(editButton).not.toBeInTheDocument(); + }); +}); + +describe("when a user is not a platform admin", () => { + beforeEach(async () => { + useStore.setState({ + teamMembers: mockTeamMembersData, + user: mockPlainUser, + team: "planx", + }); + + await setupTeamMembersScreen(); + }); + it("does not show a remove button", async () => { + const teamEditorsTable = screen.getByTestId("team-editors"); + const addEditorButton = + within(teamEditorsTable).queryByTestId("remove-button-0"); + + expect(addEditorButton).not.toBeInTheDocument(); + }); +}); diff --git a/editor.planx.uk/src/pages/FlowEditor/components/Team/tests/TeamMembers.updateEditor.test.tsx b/editor.planx.uk/src/pages/FlowEditor/components/Team/tests/TeamMembers.updateEditor.test.tsx index 77c05d3c5e..b43b6478bc 100644 --- a/editor.planx.uk/src/pages/FlowEditor/components/Team/tests/TeamMembers.updateEditor.test.tsx +++ b/editor.planx.uk/src/pages/FlowEditor/components/Team/tests/TeamMembers.updateEditor.test.tsx @@ -24,7 +24,7 @@ describe("when a user presses 'edit button'", () => { const teamEditorsTable = screen.getByTestId("team-editors"); const addEditorButton = await within(teamEditorsTable).findByTestId( - "edit-button-0", + "edit-button-3", ); user.click(addEditorButton); @@ -37,9 +37,9 @@ describe("when a user presses 'edit button'", () => { const lastNameInput = await screen.findByLabelText("Last name"); const emailInput = await screen.findByLabelText("Email address"); // Sorted based on first letter of first name Bill > Donella in Mocks - expect(firstNameInput).toHaveDisplayValue("Bill"); - expect(lastNameInput).toHaveDisplayValue("Sharpe"); - expect(emailInput).toHaveDisplayValue("bill@example.com"); + expect(firstNameInput).toHaveDisplayValue("Bilbo"); + expect(lastNameInput).toHaveDisplayValue("Baggins"); + expect(emailInput).toHaveDisplayValue("bil.bags@email.com"); }); it("disables the update user button", async () => { // find whole modal @@ -64,13 +64,13 @@ describe("when a user deletes an input value", () => { const teamEditorsTable = screen.getByTestId("team-editors"); const addEditorButton = await within(teamEditorsTable).findByTestId( - "edit-button-0", + "edit-button-3", ); await user.click(addEditorButton); const modal = await screen.findByRole("dialog"); const firstNameInput = await screen.findByLabelText("First name"); - expect(firstNameInput).toHaveDisplayValue(mockTeamMembersData[1].firstName); + expect(firstNameInput).toHaveDisplayValue(mockTeamMembersData[2].firstName); await user.clear(firstNameInput); @@ -98,7 +98,7 @@ describe("when a user updates a field correctly", () => { const teamEditorsTable = screen.getByTestId("team-editors"); const addEditorButton = await within(teamEditorsTable).findByTestId( - "edit-button-0", + "edit-button-3", ); await user.click(addEditorButton); @@ -112,7 +112,7 @@ describe("when a user updates a field correctly", () => { it("updates the field", async () => { const firstNameInput = await screen.findByLabelText("First name"); expect(firstNameInput).toHaveDisplayValue( - mockTeamMembersData[1].firstName + "bo", + mockTeamMembersData[2].firstName + "bo", ); }); it("enables the update user button", async () => { @@ -130,7 +130,7 @@ describe("when a user correctly updates an Editor", () => { const teamEditorsTable = screen.getByTestId("team-editors"); const addEditorButton = await within(teamEditorsTable).findByTestId( - "edit-button-0", + "edit-button-3", ); await user.click(addEditorButton); @@ -147,7 +147,7 @@ describe("when a user correctly updates an Editor", () => { it("updates the member table with new details", async () => { const membersTable = await screen.findByTestId("members-table-add-editor"); await waitFor(() => { - expect(within(membersTable).getByText(/Billbo/)).toBeInTheDocument(); + expect(within(membersTable).getByText(/Bilbobo/)).toBeInTheDocument(); }); expect( await screen.findByText(/Successfully updated a user/), @@ -196,7 +196,7 @@ describe("when a user is not a platform admin", () => { it("does not show an edit button", async () => { const teamEditorsTable = screen.getByTestId("team-editors"); const addEditorButton = - within(teamEditorsTable).queryByTestId("edit-button-0"); + within(teamEditorsTable).queryByTestId("edit-button-3"); expect(addEditorButton).not.toBeInTheDocument(); }); diff --git a/editor.planx.uk/src/pages/FlowEditor/components/Team/tests/mocks/mockTeamMembersData.tsx b/editor.planx.uk/src/pages/FlowEditor/components/Team/tests/mocks/mockTeamMembersData.tsx index 092873fa1d..7ec4d81108 100644 --- a/editor.planx.uk/src/pages/FlowEditor/components/Team/tests/mocks/mockTeamMembersData.tsx +++ b/editor.planx.uk/src/pages/FlowEditor/components/Team/tests/mocks/mockTeamMembersData.tsx @@ -4,15 +4,22 @@ export const mockTeamMembersData: TeamMember[] = [ { firstName: "Donella", lastName: "Meadows", - email: "donella@example.com", + email: null, id: 1, - role: "platformAdmin", + role: "teamEditor", }, { firstName: "Bill", lastName: "Sharpe", email: "bill@example.com", id: 2, + role: "platformAdmin", + }, + { + firstName: "Bilbo", + lastName: "Baggins", + email: "bil.bags@email.com", + id: 3, role: "teamEditor", }, ]; 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 cb14bf582f..5ad87a8a6e 100644 --- a/editor.planx.uk/src/pages/FlowEditor/components/Team/types.ts +++ b/editor.planx.uk/src/pages/FlowEditor/components/Team/types.ts @@ -1,14 +1,21 @@ import { Role, User } from "@opensystemslab/planx-core/types"; import React, { SetStateAction } from "react"; -export type TeamMember = Omit & { +export type TeamMember = ActiveTeamMember | ArchivedTeamMember; + +type ArchivedTeamMember = Omit & { role: Role; + email: string | null; }; +type ActiveTeamMember = Omit & { + role: Role; +}; export interface MembersTableProps { members: TeamMember[]; showAddMemberButton?: boolean; showEditMemberButton?: boolean; + showRemoveMemberButton?: boolean; } export interface AddNewEditorModalProps { showModal: boolean; @@ -20,10 +27,17 @@ export interface AddNewEditorFormValues { lastName: string; } +export interface UpdateEditorFormValues { + email: string | null; + firstName: string; + lastName: string; +} +export type ActionType = "add" | "edit" | "remove"; + export interface EditorModalProps { - showModal: boolean; + showModal?: boolean; setShowModal: React.Dispatch>; initialValues?: TeamMember; userId?: number; - actionType: "add" | "edit"; + actionType?: ActionType; } diff --git a/editor.planx.uk/src/pages/FlowEditor/lib/store/team.ts b/editor.planx.uk/src/pages/FlowEditor/lib/store/team.ts index afc0768b98..73c7ca12b4 100644 --- a/editor.planx.uk/src/pages/FlowEditor/lib/store/team.ts +++ b/editor.planx.uk/src/pages/FlowEditor/lib/store/team.ts @@ -31,6 +31,7 @@ export interface TeamStore { updateTeamSettings: (teamSettings: Partial) => Promise; createTeam: (newTeam: { name: string; slug: string }) => Promise; setTeamMembers: (teamMembers: TeamMember[]) => Promise; + deleteUser: (userId: number) => Promise; } export const teamStore: StateCreator< @@ -157,4 +158,13 @@ export const teamStore: StateCreator< setTeamMembers: async (teamMembers: TeamMember[]) => { set(() => ({ teamMembers })); }, + deleteUser: async (userId: number) => { + try { + const { $client } = get(); + const response = await $client.user.delete(userId); + return response; + } catch (error) { + throw new Error(`Unable to remove user. ${error}`); + } + }, }); diff --git a/editor.planx.uk/src/routes/teamMembers.tsx b/editor.planx.uk/src/routes/teamMembers.tsx index c36d7999cc..a302fe0d04 100644 --- a/editor.planx.uk/src/routes/teamMembers.tsx +++ b/editor.planx.uk/src/routes/teamMembers.tsx @@ -56,6 +56,7 @@ const teamMembersRoutes = compose( } = await client.query({ query: GET_USERS_FOR_TEAM_QUERY, variables: { teamSlug }, + fetchPolicy: "no-cache", }); const teamMembers: TeamMember[] = users.map((user) => ({ diff --git a/editor.planx.uk/src/ui/editor/SettingsSection.tsx b/editor.planx.uk/src/ui/editor/SettingsSection.tsx index 98b65108ea..c6497a2bcc 100644 --- a/editor.planx.uk/src/ui/editor/SettingsSection.tsx +++ b/editor.planx.uk/src/ui/editor/SettingsSection.tsx @@ -3,14 +3,9 @@ import { styled } from "@mui/material/styles"; import { contentFlowSpacing } from "@planx/components/shared/Preview/Card"; import React, { ReactNode } from "react"; -interface RootProps extends BoxProps { - background?: boolean; - "data-testid"?: string; -} - const Root = styled(Box, { shouldForwardProp: (prop) => prop !== "background", -})(({ background, theme }) => ({ +})(({ background, theme }) => ({ display: "block", width: "100%", marginTop: theme.spacing(2), @@ -29,18 +24,8 @@ const Root = styled(Box, { }), })); -export default function SettingsSection({ - children, - background, - testId, -}: { - children: ReactNode; - background?: boolean; - testId?: string; -}) { - return ( - - {children} - - ); +export default function SettingsSection( + props: { children: ReactNode; background?: boolean } & Partial, +) { + return {props.children}; }