All files / src/features/Events/EventDetail/tickets EventTicketCheckInModal.tsx

100% Statements 14/14
100% Branches 4/4
100% Functions 3/3
100% Lines 13/13

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                              14x 14x 14x                         14x         8x 8x       8x   8x                                                   2x 2x                       14x                     14x                         16x      
import { useState } from "react";
import NiceModal, { useModal } from "@ebay/nice-modal-react";
import GivePopup from "@shared/Popup/GivePopup";
import { styled } from "@theme/v2/Provider";
import { useCustomThemeV2 } from "@theme/hooks/useCustomThemeV2";
import { EventTicketRow } from "./ticket.types";
 
export type EventTicketCheckInModalProps = {
  row: EventTicketRow;
  /** Runs on confirm; the caller owns the mutation and its error handling. */
  onConfirm: (row: EventTicketRow) => void;
  isCheckingIn?: boolean;
};
 
/** Copy is spec — frame 7412-195380. */
export const CHECK_IN_MODAL_TITLE = "Check in this guest?";
export const CHECK_IN_MODAL_ACTION = "Yes, Check In";
export const CHECK_IN_SUCCESS_TOAST = "Checked in successfully";
 
/**
 * The confirmation shown before a manual check-in (frame 7412-195380). It exists
 * because the action is one-way: `ConsumeEventTickets*` moves the ticket to
 * `consumed` and nothing in the API moves it back, which is exactly what the body
 * copy tells the merchant.
 *
 * Built on `GivePopup` — the code counterpart of the frame's "Pop Up" component
 * (440px card, icon circle opposite the close button, title and body in the
 * content, right-aligned pill buttons). The mutation stays with the caller so the
 * table row and the detail panel can each check in through their own state.
 */
export const EventTicketCheckInModalBase = ({
  row,
  onConfirm,
  isCheckingIn,
}: EventTicketCheckInModalProps) => {
  const { visible, remove } = useModal();
  const { isMobileView } = useCustomThemeV2();
  // Confirm arms once. The caller's lock is the real double-submit guard; this
  // greys the button out immediately so the beat before the modal is removed
  // doesn't read as still-clickable.
  const [isConfirmed, setIsConfirmed] = useState(false);
 
  return (
    <GivePopup
      type="success"
      iconType="success-regular"
      open={visible}
      onClose={remove}
      isMobile={isMobileView}
      paperSx={isMobileView ? mobilePaperSx : undefined}
      title={CHECK_IN_MODAL_TITLE}
      description={
        <>
          This validates <Emphasis>{row.holderName}</Emphasis>&apos;s ticket{" "}
          <Emphasis>{row.displayId}</Emphasis>. It will be recorded as a manual
          check-in and can&apos;t be undone.
        </>
      }
      buttons={[
        {
          label: "Cancel",
          onClick: remove,
          variant: "ghost",
          dataTestId: "check-in-cancel",
        },
        {
          label: CHECK_IN_MODAL_ACTION,
          onClick: () => {
            setIsConfirmed(true);
            onConfirm(row);
          },
          variant: "filled",
          color: "primary",
          disabled: isCheckingIn || isConfirmed,
          dataTestId: "check-in-confirm",
        },
      ]}
    />
  );
};
 
const EventTicketCheckInModal = NiceModal.create(EventTicketCheckInModalBase);
export default EventTicketCheckInModal;
 
/**
 * The theme's `MuiDialog` override turns every dialog under 600px into a
 * full-height, square-cornered sheet with stacked full-width buttons
 * (`theme/components/dialog.tsx`). This confirmation is a short, one-decision
 * card, so on mobile it keeps `GivePopup`'s compact bottom sheet and the frame's
 * row of right-aligned buttons — the same opt-out `AccessDeniedPopup` and
 * `GiveConfirmationPopUp` apply.
 */
const mobilePaperSx = {
  height: "auto !important",
  maxHeight: "fit-content !important",
  "& .MuiDialogActions-root": {
    flexDirection: "row",
    justifyContent: "flex-end",
    "& .MuiButton-root": {
      width: "fit-content !important",
    },
  },
};
 
/** The holder and the ticket code are picked out of the sentence in the frame. */
const Emphasis = styled("span")(({ theme }) => ({
  color: theme.palette.text?.primary,
}));