mirror of
https://github.com/Sea-Haven-Industries/shoc-frontend-new.git
synced 2026-10-02 05:03:12 +00:00
feat(team-members): warn when the invite to a corrected email could not be sent
This commit is contained in:
parent
ca42d6e59f
commit
383d5c3c6c
5 changed files with 97 additions and 1 deletions
|
|
@ -88,9 +88,12 @@ export function mapTeamMemberListItems(rawItems: unknown[]): TeamMemberListItem[
|
|||
export function mapTeamMemberDetail(raw: unknown): TeamMemberDetail {
|
||||
const record = (typeof raw === "object" && raw !== null ? raw : {}) as Record<string, unknown>;
|
||||
const listItem = mapTeamMemberListItem(record);
|
||||
// Present only on an update that re-sent a pending member's invite.
|
||||
const inviteEmailSent = record.inviteEmailSent ?? record.InviteEmailSent;
|
||||
return {
|
||||
...listItem,
|
||||
serviceAreas: readStringArray(record, "serviceAreas", "ServiceAreas"),
|
||||
permissions: readPermissions(record),
|
||||
...(typeof inviteEmailSent === "boolean" && { inviteEmailSent }),
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,6 +47,8 @@ export interface TeamPermissionValue {
|
|||
export interface TeamMemberDetail extends TeamMemberListItem {
|
||||
serviceAreas: string[];
|
||||
permissions: TeamPermissionValue[];
|
||||
/** Set only after an update that changed a pending member's email and re-sent their invite. */
|
||||
inviteEmailSent?: boolean;
|
||||
}
|
||||
|
||||
export interface UpdateTeamMemberInput {
|
||||
|
|
|
|||
|
|
@ -23,6 +23,15 @@ export function useTeamMemberDetail(
|
|||
});
|
||||
}
|
||||
|
||||
export const UPDATED_INVITE_EMAIL_FAILED_MESSAGE =
|
||||
"Team member updated, but the invite to their new email couldn't be sent. Use Resend invite.";
|
||||
|
||||
function updatedMessage(member: TeamMemberDetail): string {
|
||||
return member.inviteEmailSent === true
|
||||
? `Team member updated. A new invite was sent to ${member.email}.`
|
||||
: "Team member updated.";
|
||||
}
|
||||
|
||||
interface UpdateVariables {
|
||||
id: string | number;
|
||||
input: UpdateTeamMemberInput;
|
||||
|
|
@ -36,7 +45,11 @@ export function useUpdateTeamMember(): UseMutationResult<TeamMemberDetail, Error
|
|||
onSuccess: (member) => {
|
||||
void queryClient.invalidateQueries({ queryKey: queryKeys.teamMembers.all });
|
||||
void queryClient.setQueryData(queryKeys.teamMembers.detail(member.id), member);
|
||||
toast.success("Team member updated.");
|
||||
if (member.inviteEmailSent === false) {
|
||||
toast.warning(UPDATED_INVITE_EMAIL_FAILED_MESSAGE);
|
||||
return;
|
||||
}
|
||||
toast.success(updatedMessage(member));
|
||||
},
|
||||
onError: (error: Error) => {
|
||||
toast.error(error.message || "Failed to update team member");
|
||||
|
|
|
|||
|
|
@ -54,4 +54,10 @@ describe("mapTeamMemberDetail", () => {
|
|||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps the invite email result only when the update reported one", () => {
|
||||
expect(mapTeamMemberDetail({ id: "u1", inviteEmailSent: false }).inviteEmailSent).toBe(false);
|
||||
expect(mapTeamMemberDetail({ Id: "u1", InviteEmailSent: true }).inviteEmailSent).toBe(true);
|
||||
expect(mapTeamMemberDetail({ id: "u1" })).not.toHaveProperty("inviteEmailSent");
|
||||
});
|
||||
});
|
||||
|
|
|
|||
72
src/test/domain/team-members/use-update-team-member.test.tsx
Normal file
72
src/test/domain/team-members/use-update-team-member.test.tsx
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
import type { ReactNode } from "react";
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||
import { act, renderHook, waitFor } from "@testing-library/react";
|
||||
import { toast } from "react-toastify";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { teamMembersApi } from "@/domain/team-members/api/team-members-api";
|
||||
import type {
|
||||
TeamMemberDetail,
|
||||
UpdateTeamMemberInput,
|
||||
} from "@/domain/team-members/types/team-member";
|
||||
import {
|
||||
UPDATED_INVITE_EMAIL_FAILED_MESSAGE,
|
||||
useUpdateTeamMember,
|
||||
} from "@/domain/team-members/use-cases/use-team-member-detail";
|
||||
|
||||
vi.mock("react-toastify", () => ({
|
||||
toast: { success: vi.fn(), warning: vi.fn(), error: vi.fn() },
|
||||
}));
|
||||
|
||||
vi.mock("@/domain/team-members/api/team-members-api", () => ({
|
||||
teamMembersApi: { update: vi.fn() },
|
||||
}));
|
||||
|
||||
const UPDATED = {
|
||||
id: "user-7",
|
||||
name: "Taylor Reed",
|
||||
email: "taylor.reed@example.com",
|
||||
pendingRegistration: true,
|
||||
serviceAreas: ["East"],
|
||||
permissions: [],
|
||||
} as unknown as TeamMemberDetail;
|
||||
|
||||
function wrapper({ children }: { children: ReactNode }) {
|
||||
const client = new QueryClient({ defaultOptions: { mutations: { retry: false } } });
|
||||
return <QueryClientProvider client={client}>{children}</QueryClientProvider>;
|
||||
}
|
||||
|
||||
async function update(updated: TeamMemberDetail) {
|
||||
vi.mocked(teamMembersApi.update).mockResolvedValue(updated);
|
||||
const { result } = renderHook(() => useUpdateTeamMember(), { wrapper });
|
||||
act(() => result.current.mutate({ id: "user-7", input: {} as UpdateTeamMemberInput }));
|
||||
await waitFor(() => expect(result.current.isSuccess).toBe(true));
|
||||
}
|
||||
|
||||
describe("useUpdateTeamMember", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it("confirms an ordinary update", async () => {
|
||||
await update(UPDATED);
|
||||
|
||||
expect(toast.success).toHaveBeenCalledWith("Team member updated.");
|
||||
expect(toast.warning).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("tells the admin a new invite went to the corrected email", async () => {
|
||||
await update({ ...UPDATED, inviteEmailSent: true });
|
||||
|
||||
expect(toast.success).toHaveBeenCalledWith(
|
||||
"Team member updated. A new invite was sent to taylor.reed@example.com.",
|
||||
);
|
||||
});
|
||||
|
||||
it("warns the admin to re-send when the new invite could not be emailed", async () => {
|
||||
await update({ ...UPDATED, inviteEmailSent: false });
|
||||
|
||||
expect(toast.warning).toHaveBeenCalledWith(UPDATED_INVITE_EMAIL_FAILED_MESSAGE);
|
||||
expect(toast.success).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Reference in a new issue