06dab1e790
Backend: PayStation.lookup (session view + quote in one read); ExitFlow.exitForBooth reuses the reader path's paid+grace validation (no booth-only unpaid bypass) and signs vehicle_exit + pulses an exit relay; printExitVoucher reprints the paid ticket id barcode; site_config.exit_voucher_default (migration 0002) drives the default. Routes: GET /api/session/:id, POST /api/exit, POST /api/voucher. Web: BoothPayModal (entry/now/duration/total, tender, 'Printo biletë dalje'), SnapshotStrip (entry/exit evidence), api.ts client fns, SiteSettings toggle.
31 lines
1.2 KiB
TypeScript
31 lines
1.2 KiB
TypeScript
// Small formatting helpers for the booth. Money is integer MINOR units (never a
|
|
// float — matches the tariff/ledger model); duration is whole minutes.
|
|
|
|
/** Format integer minor units + ISO-4217 currency as a major-unit string. */
|
|
export function formatMoney(amountMinor: number, currency: string): string {
|
|
const major = amountMinor / 100;
|
|
try {
|
|
return new Intl.NumberFormat(undefined, { style: "currency", currency }).format(major);
|
|
} catch {
|
|
// Unknown/garbled currency code — fall back to a plain number + the code.
|
|
return `${major.toFixed(2)} ${currency}`;
|
|
}
|
|
}
|
|
|
|
/** Human duration between two ISO times, e.g. "2h 14m" / "47m" / "0m". */
|
|
export function formatDuration(fromIso: string, toIso: string): string {
|
|
const ms = Date.parse(toIso) - Date.parse(fromIso);
|
|
if (!Number.isFinite(ms) || ms < 0) return "—";
|
|
const mins = Math.floor(ms / 60_000);
|
|
const h = Math.floor(mins / 60);
|
|
const m = mins % 60;
|
|
return h > 0 ? `${h}h ${m}m` : `${m}m`;
|
|
}
|
|
|
|
/** Local time-of-day HH:MM:SS from an ISO string. */
|
|
export function formatTime(iso: string | null): string {
|
|
if (!iso) return "—";
|
|
const d = new Date(iso);
|
|
return Number.isNaN(d.getTime()) ? "—" : d.toTimeString().slice(0, 8);
|
|
}
|