Merge pull request #1228 from Tria-plc/freight_feature/usermanagement

feat(freight): surface scheduling clock and gate pay during drain
This commit is contained in:
marshal
2026-08-10 22:14:44 +03:00
committed by GitHub
2 changed files with 49 additions and 1 deletions

View File

@@ -44,6 +44,27 @@ function formatRelative(iso: string, nowMs: number): string {
return past ? `${span} ago` : `in ${span}`;
}
/**
* Length of a window as "1h 30m" / "45m". Null unless both ends are real and
* ordered — the pay window is configurable per schedule, so this is read off the
* actual stamps rather than assuming any fixed duration.
*/
function formatDuration(
from: string | null | undefined,
to: string | null | undefined,
): string | null {
if (!from || !to) return null;
const fromMs = new Date(from).getTime();
const toMs = new Date(to).getTime();
if (!Number.isFinite(fromMs) || !Number.isFinite(toMs)) return null;
const minutes = Math.round((toMs - fromMs) / 60_000);
if (minutes <= 0) return null;
const hours = Math.floor(minutes / 60);
const rest = minutes % 60;
if (!hours) return `${rest}m`;
return rest ? `${hours}h ${rest}m` : `${hours}h`;
}
function Row({
label,
value,
@@ -98,8 +119,18 @@ export function BookingSchedulingWindowCard({
return () => clearInterval(interval);
}, []);
// How long the customer actually had to pay: start → the raw deadline, NOT the
// drain end (the drain is settlement grace, not payable time).
const windowDuration = formatDuration(
booking.selectedForBatchAt,
booking.paymentDeadline,
);
const hasAnything =
Boolean(schedule) || Boolean(payWindowEndsAt) || Boolean(booking.holdExpiresAt);
Boolean(schedule) ||
Boolean(payWindowEndsAt) ||
Boolean(booking.selectedForBatchAt) ||
Boolean(booking.holdExpiresAt);
if (!hasAnything) return null;
const payWindowClosed = payWindowEndsAt
@@ -157,6 +188,18 @@ export function BookingSchedulingWindowCard({
</Group>
) : null}
{booking.selectedForBatchAt ? (
<Row
label="Payment window started"
value={formatStamp(booking.selectedForBatchAt) ?? "—"}
hint={
windowDuration
? `${windowDuration} window`
: formatRelative(booking.selectedForBatchAt, nowMs)
}
/>
) : null}
{payWindowEndsAt ? (
<Row
label="Payment window ends"

View File

@@ -206,6 +206,11 @@ export interface BookingDetail {
trainScheduleStatus?: string | null;
/** The allocated train's identity + clock, attached by the detail endpoint. */
trainScheduleSummary?: BookingTrainScheduleSummary | null;
/**
* When the batch engine picked this booking and opened its pay window — the
* start paired with `paymentDeadline` (both are set and cleared together).
*/
selectedForBatchAt?: string | null;
/** End of this booking's pay window (batch/offer deadline). */
paymentDeadline?: string | null;
/**