enhance phase countdown and booking window descriptions for clarity

This commit is contained in:
Marshal
2026-07-03 23:11:04 +00:00
parent 1f3368dcc3
commit c2649dae3c
2 changed files with 48 additions and 14 deletions

View File

@@ -45,22 +45,50 @@ function windowLabel(w: MyBookingWindow): string {
}
/**
* The deadline + label for whichever phase the window is currently in. Phases
* run: window open (closes at windowClosesAt) → document review (docReviewEndsAt)
* → payment (paymentPhaseEndsAt). Returns null when no phase is timing down.
* The countdown for whichever phase the window is currently in. Phases run:
* pre-window (opens at windowOpensAt) → open (closes at windowClosesAt)
* document review (docReviewEndsAt) → payment (paymentPhaseEndsAt).
*
* `label` describes the deadline being counted down to; `expiredText` names the
* NEXT step so that when a deadline lapses between the 60s refetches the row
* announces what comes next ("Booking opening now…", "Review starting…") rather
* than the bare word "Expired". Returns null when no phase is timing down.
*/
function phaseCountdown(
w: MyBookingWindow,
): { label: string; deadline: string } | null {
): { label: string; deadline: string; expiredText: string } | null {
switch (w.windowPhase) {
case "PRE_WINDOW":
if (w.windowOpensAt)
return {
label: "Booking opens in",
deadline: w.windowOpensAt,
expiredText: "Booking opening now…",
};
return null;
case "OPEN":
if (w.windowClosesAt) return { label: "Window closes in", deadline: w.windowClosesAt };
if (w.windowClosesAt)
return {
label: "Window closes in",
deadline: w.windowClosesAt,
expiredText: "Document review starting…",
};
return null;
case "DOC_REVIEW":
if (w.docReviewEndsAt) return { label: "Document review ends in", deadline: w.docReviewEndsAt };
if (w.docReviewEndsAt)
return {
label: "Document review ends in",
deadline: w.docReviewEndsAt,
expiredText: "Payment starting…",
};
return null;
case "PAYMENT":
if (w.paymentPhaseEndsAt) return { label: "Payment due in", deadline: w.paymentPhaseEndsAt };
if (w.paymentPhaseEndsAt)
return {
label: "Payment due in",
deadline: w.paymentPhaseEndsAt,
expiredText: "Payment window closing…",
};
return null;
default:
return null;
@@ -141,9 +169,11 @@ interface UpcomingWindowsSectionProps {
}
/**
* The customer's upcoming/open booking windows on their active-contract
* lanes. Import trains open a window on one booking day; export trains open
* 24h before departure. Hidden entirely when there is nothing to show.
* All announced upcoming/open booking windows, shown to every customer
* regardless of whether they hold a contract on the lane. Import trains open a
* window on one booking day; export trains open 24h before departure. Rows on a
* lane the customer has an active contract for carry a "Book now" action;
* others route to the contract list. Hidden entirely when nothing is announced.
*/
export const UpcomingWindowsSection = memo(function UpcomingWindowsSection({
windows,
@@ -162,7 +192,7 @@ export const UpcomingWindowsSection = memo(function UpcomingWindowsSection({
Booking Windows
</Text>
<Text fz={13} c="edr-muted">
Upcoming and open booking windows on your contract lanes
Upcoming and open booking windows across all lanes
</Text>
</Box>
</Group>
@@ -211,6 +241,7 @@ export const UpcomingWindowsSection = memo(function UpcomingWindowsSection({
<CountdownTimer
deadline={cd.deadline}
label={cd.label}
expiredText={cd.expiredText}
size="xs"
/>
</Box>

View File

@@ -47,13 +47,16 @@ export interface PriceLineItem {
}
/**
* An upcoming/open booking window on one of the signed-in customer's
* active-contract lanes. Import trains open a window on one booking day;
* An announced upcoming/open booking window, shown to every signed-in customer
* regardless of contract. Import trains open a window on one booking day;
* export trains open 24h before departure (first come, first served).
*/
export interface MyBookingWindow {
scheduleId: string;
/** Contract whose route this window belongs to, when the row carries it. */
/**
* The customer's active contract on this lane, when they hold one — enables
* "Book now" to target it. Null for lanes they have no contract on.
*/
contractId: string | null;
/** ONE_TIME contracts can't draw down against a window — button is hidden. */
contractKind: "ONE_TIME" | "GENERAL" | null;