Enhance BookingRequestsPage layout and styling

This commit is contained in:
marshalyordanos
2026-07-20 22:10:48 +03:00
parent 43df24142c
commit 8aaccf3f36
2 changed files with 122 additions and 29 deletions

View File

@@ -58,6 +58,8 @@ import {
type ColumnDef,
} from "@edr/ui-common";
import "./booking-requests-table.css";
/** Booking kind: one-time vs general-contract bookings. Now a filter, not a tab. */
type BookingKind = "ONE_TIME" | "GENERAL_CONTRACT";
@@ -282,6 +284,8 @@ export default function BookingRequestsPage() {
const columns: ColumnDef<BookingListRow>[] = [
{
id: "booking",
size: 60,
minSize: 40,
header: () => <span className={bookingTable.headerCell}>Booking</span>,
cell: ({ row }) => {
const b = row.original;
@@ -303,6 +307,8 @@ export default function BookingRequestsPage() {
},
{
id: "contract",
size: 60,
minSize: 40,
header: () => <span className={bookingTable.headerCell}>Contract</span>,
cell: ({ row }) => {
const ref = row.original.contractReference;
@@ -329,6 +335,8 @@ export default function BookingRequestsPage() {
},
{
id: "bookingKind",
size: 60,
minSize: 40,
header: () => <span className={bookingTable.headerCell}>Type</span>,
cell: ({ row }) => {
const isGeneral = row.original.bookingKind === "GENERAL_CONTRACT";
@@ -346,6 +354,8 @@ export default function BookingRequestsPage() {
},
{
id: "route",
size: 60,
minSize: 40,
header: () => <span className={bookingTable.headerCell}>Route</span>,
cell: ({ row }) => {
const b = row.original;
@@ -376,8 +386,8 @@ export default function BookingRequestsPage() {
},
{
id: "status",
size: 200,
minSize: 180,
size: 60,
minSize: 40,
header: () => <span className={bookingTable.headerCell}>Status</span>,
cell: ({ row }) => (
<div className="py-1">
@@ -388,13 +398,11 @@ export default function BookingRequestsPage() {
/>
</div>
),
meta: {
headerClassName: "min-w-[11rem]",
cellClassName: "min-w-[11rem]",
},
},
{
id: "scheduled",
size: 60,
minSize: 40,
header: () => <span className={bookingTable.headerCell}>Scheduled</span>,
cell: ({ row }) => (
<span className="inline-flex items-center gap-1.5 text-sm text-muted-foreground">
@@ -405,6 +413,8 @@ export default function BookingRequestsPage() {
},
{
id: "priority",
size: 60,
minSize: 40,
header: () => <span className={bookingTable.headerCell}>Priority</span>,
cell: ({ row }) => (
<BookingPriorityBadge score={row.original.priorityScore} />
@@ -412,7 +422,8 @@ export default function BookingRequestsPage() {
},
{
id: "actions",
size: 140,
size: 60,
minSize: 40,
cell: ({ row }) => (
<BookingActionsMenu
row={row.original}
@@ -702,28 +713,26 @@ export default function BookingRequestsPage() {
/>
</Box>
) : (
<Box style={{ overflowX: "auto" }} w="100%">
<DataTable
columns={columns}
data={rows}
status={isLoading ? "loading" : isError ? "error" : "success"}
onRowClick={handleRowClick}
pagination={{
pageIndex: pagination.pageIndex,
pageSize: pagination.pageSize,
pageCount,
totalCount: total,
}}
tableOptions={{
state: { pagination },
onPaginationChange: setPagination,
manualPagination: true,
pageCount,
}}
containerClassName="border-0 shadow-none bg-transparent"
footer={DataTableFooter}
/>
</Box>
<DataTable
columns={columns}
data={rows}
status={isLoading ? "loading" : isError ? "error" : "success"}
onRowClick={handleRowClick}
pagination={{
pageIndex: pagination.pageIndex,
pageSize: pagination.pageSize,
pageCount,
totalCount: total,
}}
tableOptions={{
state: { pagination },
onPaginationChange: setPagination,
manualPagination: true,
pageCount,
}}
containerClassName="edr-booking-requests-table border-0 shadow-none bg-transparent"
footer={DataTableFooter}
/>
)}
</Stack>
</Card>

View File

@@ -0,0 +1,84 @@
/*
* Scoped to .edr-booking-requests-table — the DataTable container div on the
* backoffice booking-requests list only; no other DataTable is affected.
* Mirrors the portal's /bookings table (bookings-table.css): horizontal
* scroll on the container, a sticky header row, and a sticky/shadowed
* action column — with a compact 4060px column width band (content
* beyond that is clipped with an ellipsis) instead of the portal's
* content-sized columns.
*/
.edr-booking-requests-table {
overflow-x: auto;
}
/*
* width: max-content — the table is exactly as wide as its columns need,
* never squeezed to fit the viewport; the container scrolls instead.
* min-width: 100% keeps it filling the card when content is narrow.
*/
.edr-booking-requests-table table {
table-layout: auto;
width: max-content;
min-width: 100%;
}
/* Compact column band: 40px floor, 60px ceiling, ellipsis past that. */
.edr-booking-requests-table th,
.edr-booking-requests-table td:not([colspan]) {
min-width: 40px;
max-width: 60px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/*
* Full-width rows (loading skeleton / error / empty state) span every
* column via colspan — leave their sizing and wrapping alone.
*/
.edr-booking-requests-table td[colspan] {
max-width: none;
white-space: normal;
}
/* Sticky header row. */
.edr-booking-requests-table thead th {
position: sticky;
top: 0;
z-index: 1;
}
/*
* Fixed, sticky action column. Overrides the inline width DataTable stamps
* from tanstack's column size (`size: 140` on the actions column) — hence
* !important. `:not([colspan])` keeps the full-width error/empty rows out.
*/
.edr-booking-requests-table th:last-child,
.edr-booking-requests-table td:last-child:not([colspan]) {
width: 60px !important;
min-width: 60px;
max-width: 60px;
position: sticky;
right: 0;
box-shadow: -12px 0 16px -6px rgba(16, 32, 47, 0.3);
}
/*
* Sticky cells sit above the scrolling ones, so they need their own opaque
* background or the columns underneath show through.
*/
.edr-booking-requests-table td:last-child:not([colspan]) {
background: #f5f8fb;
z-index: 2;
}
/* Row hover uses the tailwind `hover:bg-accent` class on the <tr>. */
.edr-booking-requests-table tbody tr:hover td:last-child:not([colspan]) {
background: var(--accent, #f4fbf8);
}
/* Header cell is sticky on both axes — it must outrank the body's sticky column. */
.edr-booking-requests-table th:last-child {
background: #f4f7fa;
z-index: 3;
}