All files / src/features/Events/EventDetail/host HostToolbarActions.tsx

72% Statements 18/25
100% Branches 6/6
28.57% Functions 2/7
75% Lines 18/24

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 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156                                        9x 9x 9x                                         9x 8x 8x 8x   8x   8x   8x     8x 8x   8x     3x                                                 3x                 1x                               5x                                                                               9x        
import { MouseEvent, useState } from "react";
import NiceModal from "@ebay/nice-modal-react";
import {
  DotsThreeVerticalIcon,
  EnvelopeOpenIcon,
  PlusIcon,
} from "@phosphor-icons/react";
import { ACTION_DENY_MESSAGE } from "@constants/permissions";
import ContextualMenu from "@shared/ContextualMenu/ContextualMenu";
import { ContextualMenuOptionProps } from "@shared/ContextualMenu/ContextualMenu.types";
import GiveButton from "@shared/Button/GiveButton";
import GiveIconButton from "@shared/IconButton/GiveIconButton";
import GiveTooltip from "@shared/Tooltip/GiveTooltip";
import { useTeamPermissions } from "@features/Permissions/AccessControl/hooks";
import { useCustomThemeV2 } from "@theme/hooks/useCustomThemeV2";
import {
  EVENT_ASSIGN_HOST_MODAL,
  EVENT_INVITE_HOST_MODAL,
} from "modals/modal_names";
 
export const ASSIGN_HOST_ACTION = "Assign Host";
export const INVITE_HOST_ACTION = "Invite Host";
const ACTIONS_ARIA_LABEL = "Host actions";
 
type Props = {
  eventId?: string;
};
 
/**
 * The Host tab's toolbar actions (frames 8119-109601 / 8124-60014).
 *
 * Two actions now, because an event's door staff can come from two places: a
 * host the merchant already has (Assign, the primary — it needs no email and no
 * waiting) or a new one (Invite). The pair is what drives the icon change on
 * this row: Assign takes the plus, Invite takes the envelope it used to share.
 *
 * A 390px row fits the search field and one 32px control, so the pair collapses
 * into an overflow sheet there.
 *
 * Both are member writes: inviting creates an account member, and assigning
 * writes an existing member's assignment set — so each carries the gate its own
 * call is subject to, and reports a denial the way Settings → Team does.
 */
const HostToolbarActions = ({ eventId }: Props) => {
  const { isMobileView } = useCustomThemeV2();
  const { isAddAllowed, isUpdateAccessPolicyAllowed } = useTeamPermissions();
  const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
 
  const closeMenu = () => setAnchorEl(null);
 
  const openAssignModal = () =>
    NiceModal.show(EVENT_ASSIGN_HOST_MODAL, { eventId });
  const openInviteModal = () =>
    NiceModal.show(EVENT_INVITE_HOST_MODAL, { eventId });
 
  const canAssign = Boolean(eventId) && isUpdateAccessPolicyAllowed;
  const canInvite = Boolean(eventId) && isAddAllowed;
 
  if (isMobileView) {
    // Frame 8124-60544 — the sheet leads with the label and trails the glyph,
    // the same arrangement the event's own (…) menu uses.
    const options: ContextualMenuOptionProps[] = [
      {
        text: ASSIGN_HOST_ACTION,
        IconRight: PlusIcon,
        disabled: !canAssign,
        tooltipTitle: ACTION_DENY_MESSAGE,
        disableTooltip: canAssign,
        onClick: () => {
          closeMenu();
          openAssignModal();
        },
      },
      {
        text: INVITE_HOST_ACTION,
        IconRight: EnvelopeOpenIcon,
        disabled: !canInvite,
        tooltipTitle: ACTION_DENY_MESSAGE,
        disableTooltip: canInvite,
        onClick: () => {
          closeMenu();
          openInviteModal();
        },
      },
    ];
 
    return (
      <>
        <GiveIconButton
          Icon={DotsThreeVerticalIcon}
          variant="ghost"
          size="large"
          aria-label={ACTIONS_ARIA_LABEL}
          data-testid="event-hosts-mobile-actions"
          onClick={(event: MouseEvent<HTMLElement>) =>
            setAnchorEl(event.currentTarget)
          }
        />
        <ContextualMenu
          anchorEl={anchorEl}
          handleClose={closeMenu}
          // Below v2_sm this is a bottom sheet, where the blurred tertiary
          // surface reads as a grey wash — the mockups' sheet is plain white.
          color="primary"
          texture="solid"
          options={options}
        />
      </>
    );
  }
 
  return (
    <>
      <GiveTooltip
        disableHoverListener={canAssign}
        title={ACTION_DENY_MESSAGE}
        fluidWidth
      >
        <GiveButton
          variant="filled"
          size="large"
          startIcon={<PlusIcon size={18} />}
          label={ASSIGN_HOST_ACTION}
          disabled={!canAssign}
          onClick={openAssignModal}
          sx={BUTTON_SX}
        />
      </GiveTooltip>
      <GiveTooltip
        disableHoverListener={canInvite}
        title={ACTION_DENY_MESSAGE}
        fluidWidth
      >
        <GiveButton
          variant="outline"
          size="large"
          startIcon={<EnvelopeOpenIcon size={18} />}
          label={INVITE_HOST_ACTION}
          disabled={!canInvite}
          onClick={openInviteModal}
          sx={BUTTON_SX}
        />
      </GiveTooltip>
    </>
  );
};
 
export default HostToolbarActions;
 
// The toolbar row is a flex container that would otherwise stretch both
// buttons; `nowrap` keeps each label on one line as the row narrows.
const BUTTON_SX = {
  width: "fit-content !important",
  whiteSpace: "nowrap",
};