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 | 46x 46x 46x 46x 46x 23x 46x 11x 11x 11x 2x 2x 2x 2x 2x 11x 17x | import { Box, Stack } from "@mui/material";
import GiveText from "@shared/Text/GiveText";
import { groupSeatingRowsByLabel } from "../seating.helpers";
import type { SeatingRow } from "../types";
import SeatMapRow from "./SeatMapRow";
const SEAT_AVAILABLE = "#666665";
const SEAT_SELECTED = "#088750";
const SEAT_TAKEN = "rgba(0, 0, 0, 0.25)";
const BORDER = "#E6E6E3";
type Props = {
seatingRows: SeatingRow[];
value: number[];
onChange: (ids: number[]) => void;
canEdit: boolean;
// rowID → owning ticket name for rows already assigned to OTHER tickets. Those rows
// render disabled so one seating row can only ever belong to one ticket (PAY Builder 031).
takenRows?: Map<number, string>;
};
// Legend item: a 10×10 swatch above its label (Figma Frame 1321317240/241).
const LegendItem = ({ color, label }: { color: string; label: string }) => (
<Stack alignItems="center" gap="4px">
<Box sx={{ width: 10, height: 10, borderRadius: "2px", background: color }} />
<GiveText variant="bodyXS" sx={{ color: SEAT_AVAILABLE }}>
{label}
</GiveText>
</Stack>
);
// AC006/AC007 — restrict this ticket to specific seating rows, rendered as the seat map
// (Figma node 6722-8421). Presentational: the parent owns the form state. Selection is per
// range (each range = one seatingRowID); the seat squares are the visual.
const SeatingRowsField = ({
seatingRows,
value,
onChange,
canEdit,
takenRows,
}: Props) => {
const groups = groupSeatingRowsByLabel(seatingRows);
const hasTakenRows = !!takenRows && takenRows.size > 0;
const toggle = (id: number) => {
// A row owned by another ticket is not selectable (defense — the control is
// already disabled); never let it enter this ticket's selection.
Iif (takenRows?.has(id)) return;
const next = new Set<number>(value);
Iif (next.has(id)) next.delete(id);
else next.add(id);
onChange([...next]);
};
return (
<Stack gap="8px">
{/* Section label — Body S #292928 (matches the other field labels). */}
<GiveText variant="bodyS" sx={{ color: "#292928" }}>
Assign rows for this ticket
</GiveText>
{groups.length === 0 ? (
<GiveText variant="bodyXS" color="secondary">
Add and save seating rows in the event details first.
</GiveText>
) : (
<Stack
sx={{
alignItems: "center",
gap: "12px",
p: "12px",
background: "rgba(245, 245, 243, 0.7)",
border: `1px solid ${BORDER}`,
borderRadius: "8px",
}}
>
{/* Rows wrapper — centered content block (Figma Frame 1321317244). */}
<Stack alignItems="center" gap="4px">
{groups.map((group) => (
<SeatMapRow
key={group.ranges[0].id}
group={group}
value={value}
canEdit={canEdit}
onToggle={toggle}
takenRows={takenRows}
/>
))}
</Stack>
{/* Legend — full-width, top border, items centered (Figma Frame 1321317242). */}
<Stack
direction="row"
justifyContent="center"
gap="16px"
sx={{ alignSelf: "stretch", pt: "12px", borderTop: `1px solid ${BORDER}` }}
>
<LegendItem color={SEAT_AVAILABLE} label="Available" />
<LegendItem color={SEAT_SELECTED} label="Selected" />
{hasTakenRows && (
<LegendItem color={SEAT_TAKEN} label="Assigned" />
)}
</Stack>
</Stack>
)}
{groups.length > 0 && value.length === 0 && (
<GiveText variant="bodyXS" color="error1">
Select at least one seating row for this ticket.
</GiveText>
)}
</Stack>
);
};
export default SeatingRowsField;
|