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 | 7x 7x 7x 7x 122x 180x 122x 90x 90x 13x 13x 90x 122x | import { Stack } from "@mui/material";
import { DotsThreeIcon } from "@phosphor-icons/react";
import { ActionsComponent } from "@common/Table/components/ActionsComponent";
import { GiveMemberNameCell } from "@features/Settings/Team/components/GiveMemberNameCell";
import {
ColumnBaseType,
TableColumnType,
} from "componentsV2/Table/Table.types";
import { EventHostRow } from "./host.types";
type Params = {
onOpenMenu: (event: React.MouseEvent<HTMLElement>, row: EventHostRow) => void;
/** The row with a write in flight, if any — see PENDING_ROW_SX. */
pendingId?: number | null;
};
/**
* Columns for the two host sections (frames 8119-110578 / 8119-109601).
*
* Both sections draw the same row: the identity cell and an overflow menu. What
* differs is what the menu offers — an assigned host can be unassigned or
* revoked, a pending one re-sent or withdrawn — and that is decided from the
* row when the menu opens, not by the column. So one set of columns serves both,
* and the identity cell tells them apart itself through `row.status`.
*
* The assigned row's inline "Revoke access" button is gone: with a second
* destructive-adjacent action (Unassign) the pair belongs behind the same kebab
* the pending rows already use, which is what the mockups now draw.
*
* `GiveMemberNameCell` renders the empty name as a blank line, which is what an
* invited host's row wants anyway — the email carries the row.
*/
/**
* The mockups have no column-header strip — rows sit directly under each
* section title. The V2 table renders its header row unconditionally, and with
* empty titles that is a blank padded band with a bottom border (the stray rule
* under "Assigned Host"). Zero the header cells out rather than `display:
* none`-ing them: a fixed-layout table reads its column widths from the first
* row's cells, and removing them from the box tree entirely makes the browser
* fall back to equal column widths — which parked the actions column (and its
* button) in the middle of the row. The border override needs `!important`
* because the header row styles its cells through an `& th` rule, which
* outranks a plain cell-level declaration.
*/
const HIDE_HEADER_CELL = {
padding: 0,
height: 0,
borderBottom: "none !important",
"& > *": { display: "none" },
};
/**
* A definite width for the trailing action cell. Left as the numeric default,
* `getWidth` resolves the actions column to ~5% of the table — too narrow for
* even the 32px control. Strings bypass that formula; the identity column has no
* width and takes the rest, which is what lets a long email truncate late.
*/
const ACTION_COLUMN_WIDTH = "72px";
/**
* The row's only feedback while a write is in flight.
*
* Both actions live behind a kebab that closes itself on click, so without this
* the sequence a merchant sees for Unassign is: menu closes → nothing → the row
* disappears when the refetch lands. Unassign is two round trips (read the
* member's product set, then rewrite it), so "nothing" is long enough to read
* as a dead click. `pointerEvents: none` also keeps the kebab from being
* reopened, which is the case `isRowPending` guards from the other side.
*/
const PENDING_ROW_SX = { opacity: 0.5, pointerEvents: "none" } as const;
export const useEventHostColumns = ({ onOpenMenu, pendingId }: Params) => {
const pendingSx = (row: EventHostRow) =>
pendingId === row.id ? PENDING_ROW_SX : undefined;
const columns: TableColumnType[] = [
{
key: "name",
title: "",
columnType: "avatar-name" as ColumnBaseType,
sx: HIDE_HEADER_CELL,
renderColumn: (row: EventHostRow) => (
<Stack direction="row" flex={1} minWidth={0} sx={pendingSx(row)}>
<GiveMemberNameCell
imageURL={row.imageURL}
firstName={row.name}
email={row.email}
/>
</Stack>
),
},
{
key: "actions",
title: "",
columnType: "actions" as ColumnBaseType,
columnWidth: ACTION_COLUMN_WIDTH,
sx: HIDE_HEADER_CELL,
renderColumn: (row: EventHostRow) => (
// Full-width: the cell's flex wrapper sizes children to their content,
// so without it `justifyContent` has no room to push the control to the
// row's right edge.
<Stack
direction="row"
justifyContent="flex-end"
width="100%"
sx={pendingSx(row)}
>
<ActionsComponent
title="More"
onClick={(e) => {
e.stopPropagation();
onOpenMenu(e, row);
}}
icon={() => <DotsThreeIcon fill="regular" size={20} />}
/>
</Stack>
),
},
];
return { columns };
};
|