All files / src/features/Settings/Team/helpers pahReassignmentRows.ts

100% Statements 17/17
100% Branches 10/10
100% Functions 5/5
100% Lines 14/14

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88      1x               1x 7x 6x 6x                                   1x       3x   2x       2x 1x                   1x                                                     1x       4x 5x    
import { MemberData } from "@customTypes/team.member";
import { PahReassignment } from "@hooks/common/usePahReassignmentStatus";
 
const PRIMARY_ACCOUNT_HOLDER_LABEL = "Primary Account Holder";
 
/**
 * GB-21472 — match the row that belongs to the invited new PAH. Existing
 * members are matched by their user account id; a brand-new email invite is
 * matched by email.
 */
const matchesNewPah =
  (reassignment: PahReassignment) =>
  (row: MemberData): boolean => {
    const newEmail = reassignment.newOwnerEmail?.toLowerCase();
    return (
      (Boolean(reassignment.newOwnerUserAccID) &&
        row.user?.accID === reassignment.newOwnerUserAccID) ||
      (Boolean(newEmail) && row.user?.email?.toLowerCase() === newEmail)
    );
  };
 
/**
 * GB-21472 — build the row that surfaces the invited new PAH inside the
 * "Primary Account Holder" section with an "Invitation Sent" status.
 *
 * When the new PAH is an existing team member we reuse their row (so their name
 * and avatar show); when it is a fresh email we synthesize a minimal row from
 * the invite email (the BE payload has no name for that case yet). Either way
 * the role is presented as "Primary Account Holder" and the member status is
 * forced to "invited" so the existing column renderers show the
 * "Invitation Sent" chip.
 */
export const buildInvitedPahRow = (
  reassignment: PahReassignment | undefined,
  members: MemberData[],
): MemberData | null => {
  if (!reassignment) return null;
 
  const existing = members.find(matchesNewPah(reassignment));
 
  // Existing member: reuse their row (name, avatar) but present them as the
  // pending PAH.
  if (existing) {
    return {
      ...existing,
      roleName: "owner",
      roleDisplayName: PRIMARY_ACCOUNT_HOLDER_LABEL,
      memberStatus: "invited",
    };
  }
 
  // Fresh email invite: synthesize a minimal row (the BE payload has no name
  // for this case yet).
  return {
    ownerStatusName: "",
    createdAt: 0,
    updatedAt: 0,
    joinedAt: 0,
    accID: reassignment.newOwnerUserAccID,
    roleName: "owner",
    roleDisplayName: PRIMARY_ACCOUNT_HOLDER_LABEL,
    title: "",
    userExists: false,
    memberStatus: "invited",
    hasCustomPermissions: false,
    isPasswordGenerated: false,
    user: {
      accID: reassignment.newOwnerUserAccID,
      firstName: "",
      lastName: "",
      email: reassignment.newOwnerEmail,
      imageURL: "",
    },
  } as MemberData;
};
 
/**
 * GB-21472 — keep the invited new PAH out of the "Members" list; without this
 * they would still show there as their previous (e.g. Administrator) role.
 */
export const excludeInvitedPah = (
  members: MemberData[],
  reassignment: PahReassignment | undefined,
): MemberData[] => {
  if (!reassignment) return members;
  return members.filter((row) => !matchesNewPah(reassignment)(row));
};