mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-26 18:42:49 +00:00
remove reopen delay minutes from global rules and update related types
- Removed the field from and related components. - Updated to reflect the removal of the reopen delay input field. - Modified to include new train number fields: and . - Added interface to manage active schedules with trade direction. - Introduced interface to track wagon shortages in bookings. - Updated logic to ensure consistent UI state representation. - Created migrations to drop the column and add and columns to the table. - Added tests for the new booking window display logic and wagon planning functionality.
This commit is contained in:
@@ -15,6 +15,8 @@ export function DataTable<TData, TValue>({
|
||||
data,
|
||||
status,
|
||||
onRowClick,
|
||||
rowStyle,
|
||||
rowClassName,
|
||||
tableOptions,
|
||||
pagination,
|
||||
footer,
|
||||
@@ -115,11 +117,15 @@ export function DataTable<TData, TValue>({
|
||||
onRowClick(row.original);
|
||||
}}
|
||||
role={onRowClick ? "button" : ""}
|
||||
className={
|
||||
style={rowStyle?.(row.original)}
|
||||
className={[
|
||||
onRowClick
|
||||
? "cursor-pointer hover:bg-accent hover:text-foreground "
|
||||
: ""
|
||||
}
|
||||
? "cursor-pointer hover:bg-accent hover:text-foreground"
|
||||
: "",
|
||||
rowClassName?.(row.original) ?? "",
|
||||
]
|
||||
.join(" ")
|
||||
.trim()}
|
||||
>
|
||||
{row.getVisibleCells().map((cell) => (
|
||||
<Table.Td
|
||||
|
||||
@@ -21,6 +21,10 @@ export interface DataTableProps<TData, TValue> {
|
||||
data: TData[];
|
||||
status?: "loading" | "error" | "success";
|
||||
onRowClick?: (row: TData) => void;
|
||||
/** Per-row inline style (e.g. data-driven background tints via CSS variables). */
|
||||
rowStyle?: (row: TData) => React.CSSProperties | undefined;
|
||||
/** Per-row extra class, appended after the built-in clickable-row classes. */
|
||||
rowClassName?: (row: TData) => string | undefined;
|
||||
tableOptions?: Omit<
|
||||
TableOptions<TData>,
|
||||
"data" | "columns" | "getCoreRowModel"
|
||||
|
||||
@@ -69,3 +69,10 @@ export * from "./components/select";
|
||||
export * from "./components/switch";
|
||||
export * from "./components/separator";
|
||||
export * from "./components/field";
|
||||
|
||||
export { bookingWindowUiState } from "./lib/booking-window-display";
|
||||
export type {
|
||||
BookingWindowUiKind,
|
||||
BookingWindowStateInput,
|
||||
BookingWindowUiState,
|
||||
} from "./lib/booking-window-display";
|
||||
|
||||
108
packages/ui-common/src/lib/booking-window-display.ts
Normal file
108
packages/ui-common/src/lib/booking-window-display.ts
Normal file
@@ -0,0 +1,108 @@
|
||||
/**
|
||||
* Single source of truth for how a booking window row is presented to a
|
||||
* customer or staff list: which status badge to show and which deadline (if
|
||||
* any) to count down to.
|
||||
*
|
||||
* The badge and the countdown MUST be derived together. They used to be
|
||||
* computed independently (badge from `isOpenNow`, countdown from
|
||||
* `windowPhase`), which let them contradict each other — an export train that
|
||||
* filled mid-window kept `windowPhase='OPEN'` (space can free again if a pay
|
||||
* window lapses) while `bookingWindowStatus='FULL'`, so the card showed an
|
||||
* "Upcoming" badge above a live "Window closes in …" countdown.
|
||||
*
|
||||
* Phase/status matrix this resolves (server fields on the schedule row):
|
||||
* - windowPhase: PRE_WINDOW → OPEN → DOC_REVIEW → PAYMENT → DONE
|
||||
* (export skips the review/payment phases; CLOSED_FOR_DAY is legacy)
|
||||
* - bookingWindowStatus: OPEN | CLOSED | FULL — whether the booking desk
|
||||
* actually accepts bookings right now.
|
||||
*/
|
||||
|
||||
export type BookingWindowUiKind =
|
||||
/** Bookable right now (phase OPEN and the desk flag agrees). */
|
||||
| "OPEN"
|
||||
/** Train has no capacity left — not bookable; may reopen if a reservation expires. */
|
||||
| "FULL"
|
||||
/** Announced, opens at `countdownTo`. */
|
||||
| "PRE_WINDOW"
|
||||
/** Window closed, staff reviewing documents (import cycle). */
|
||||
| "DOC_REVIEW"
|
||||
/** Batch ran, selected customers are paying (import cycle). */
|
||||
| "PAYMENT"
|
||||
/** Terminal or not bookable for any other reason. */
|
||||
| "CLOSED";
|
||||
|
||||
export interface BookingWindowStateInput {
|
||||
windowPhase?: string | null;
|
||||
bookingWindowStatus?: string | null;
|
||||
windowOpensAt?: string | null;
|
||||
windowClosesAt?: string | null;
|
||||
docReviewEndsAt?: string | null;
|
||||
paymentPhaseEndsAt?: string | null;
|
||||
}
|
||||
|
||||
export interface BookingWindowUiState {
|
||||
kind: BookingWindowUiKind;
|
||||
/** ISO deadline a countdown may tick toward; null = show no countdown. */
|
||||
countdownTo: string | null;
|
||||
/** True only when the customer can book right now. */
|
||||
isBookable: boolean;
|
||||
}
|
||||
|
||||
export function bookingWindowUiState(
|
||||
w: BookingWindowStateInput,
|
||||
): BookingWindowUiState {
|
||||
const phase = w.windowPhase ?? null;
|
||||
const status = w.bookingWindowStatus ?? null;
|
||||
|
||||
if (phase === "DONE" || phase === "CLOSED_FOR_DAY") {
|
||||
return { kind: "CLOSED", countdownTo: null, isBookable: false };
|
||||
}
|
||||
|
||||
// Mid-cycle phases win over the FULL flag: the batch may have tentatively
|
||||
// filled the train, but an unpaid reservation can still expire and free
|
||||
// space, so "document review" / "payment" is the truthful state here.
|
||||
if (phase === "DOC_REVIEW") {
|
||||
return {
|
||||
kind: "DOC_REVIEW",
|
||||
countdownTo: w.docReviewEndsAt ?? null,
|
||||
isBookable: false,
|
||||
};
|
||||
}
|
||||
if (phase === "PAYMENT") {
|
||||
return {
|
||||
kind: "PAYMENT",
|
||||
countdownTo: w.paymentPhaseEndsAt ?? null,
|
||||
isBookable: false,
|
||||
};
|
||||
}
|
||||
|
||||
// Outside the resolving phases a FULL train is simply not bookable — no
|
||||
// countdown either: ticking toward "closes in" would promise a window the
|
||||
// customer cannot use.
|
||||
if (status === "FULL") {
|
||||
return { kind: "FULL", countdownTo: null, isBookable: false };
|
||||
}
|
||||
|
||||
if (phase === "PRE_WINDOW") {
|
||||
return {
|
||||
kind: "PRE_WINDOW",
|
||||
countdownTo: w.windowOpensAt ?? null,
|
||||
isBookable: false,
|
||||
};
|
||||
}
|
||||
|
||||
if (phase === "OPEN") {
|
||||
if (status === "OPEN") {
|
||||
return {
|
||||
kind: "OPEN",
|
||||
countdownTo: w.windowClosesAt ?? null,
|
||||
isBookable: true,
|
||||
};
|
||||
}
|
||||
// Phase says OPEN but the desk flag disagrees (CLOSED): not bookable, and
|
||||
// no countdown that pretends otherwise.
|
||||
return { kind: "CLOSED", countdownTo: null, isBookable: false };
|
||||
}
|
||||
|
||||
return { kind: "CLOSED", countdownTo: null, isBookable: false };
|
||||
}
|
||||
Reference in New Issue
Block a user