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>'s ticket{" "}
<Emphasis>{row.displayId}</Emphasis>. It will be recorded as a manual
check-in and can'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,
}));
|