mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-09-09 02:28:18 +00:00
Merge branch 'alpha' of https://github.com/Tria-plc/edr-platform into alpha
This commit is contained in:
@@ -254,12 +254,16 @@ function BookingsPageContent() {
|
||||
},
|
||||
{
|
||||
key: 'contact', label: 'Primary contact',
|
||||
render: (booking: any) => (
|
||||
<div>
|
||||
<div className="font-medium">{booking.contactPhone || booking.passenger?.phone}</div>
|
||||
<div className="text-sm text-muted-foreground">{booking.contactEmail || booking.passenger?.email}</div>
|
||||
</div>
|
||||
),
|
||||
render: (booking: any) => {
|
||||
const phone = booking.contactPhone || booking.passenger?.phone || '—';
|
||||
const email = booking.contactEmail || booking.passenger?.email || '—';
|
||||
return (
|
||||
<div>
|
||||
<div className="font-medium">{phone}</div>
|
||||
<div className="text-sm text-muted-foreground truncate" title={email}>{email}</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'paymentStatus', label: 'Payment',
|
||||
|
||||
@@ -146,7 +146,13 @@ export default function CurrenciesPage() {
|
||||
|
||||
const actions = [
|
||||
{ label: 'Edit', onClick: handleEdit, variant: 'secondary' as const, icon: Edit },
|
||||
{ label: 'Delete', onClick: (c: CurrencyRate) => setDeleteConfirm(c), variant: 'danger' as const, icon: Trash2 },
|
||||
{
|
||||
label: 'Delete',
|
||||
onClick: (c: CurrencyRate) => setDeleteConfirm(c),
|
||||
variant: 'danger' as const,
|
||||
icon: Trash2,
|
||||
show: (c: CurrencyRate) => c.id !== 'etb-base',
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
|
||||
@@ -93,7 +93,7 @@ export default function PaymentsPage() {
|
||||
switch (key) {
|
||||
case 'reference': return payment.reference || payment.id?.substring(0, 8) || '';
|
||||
case 'booking': return payment.booking?.bookingRef || 'N/A';
|
||||
case 'amount': return formatCurrency(payment.amountMinor, payment.currency);
|
||||
case 'amount': return formatCurrency(payment.booking?.totalMinor ?? payment.amountMinor, 'ETB');
|
||||
case 'method': return payment.method || '';
|
||||
case 'status': return payment.status || '';
|
||||
case 'createdAt': return payment.createdAt ? new Date(payment.createdAt).toLocaleString() : '';
|
||||
@@ -116,7 +116,7 @@ export default function PaymentsPage() {
|
||||
const columns = [
|
||||
{ key: 'reference', label: 'Reference', render: (payment: any) => <span className="font-mono">{payment.reference || payment.id?.substring(0, 8)}</span> },
|
||||
{ key: 'booking', label: 'Booking', render: (payment: any) => payment.booking?.bookingRef || 'N/A' },
|
||||
{ key: 'amount', label: 'Amount', render: (payment: any) => formatCurrency(payment.amountMinor, payment.currency) },
|
||||
{ key: 'amount', label: 'Amount', render: (payment: any) => formatCurrency(payment.booking?.totalMinor ?? payment.amountMinor, 'ETB') },
|
||||
{ key: 'method', label: 'Method', render: (payment: any) => <Badge>{payment.method}</Badge> },
|
||||
{ key: 'status', label: 'Status', render: (payment: any) => <Badge variant="status" status={payment.status}>{payment.status}</Badge> },
|
||||
{ key: 'createdAt', label: 'Created', render: (payment: any) => formatDateTime(payment.createdAt) },
|
||||
@@ -204,7 +204,7 @@ export default function PaymentsPage() {
|
||||
</div>
|
||||
<div className="mt-4 grid grid-cols-3 gap-3">
|
||||
{[
|
||||
{ label: 'Amount', value: formatCurrency(p.amountMinor, p.currency) },
|
||||
{ label: 'Amount', value: formatCurrency(p.booking?.totalMinor ?? p.amountMinor, 'ETB') },
|
||||
{ label: 'Method', value: p.method || '—' },
|
||||
{ label: 'Booking', value: p.booking?.bookingRef || '—' },
|
||||
].map(({ label, value }) => (
|
||||
@@ -222,7 +222,7 @@ export default function PaymentsPage() {
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-3">
|
||||
<div className="bg-muted/40 rounded-lg p-3 col-span-2">
|
||||
<p className="text-xs text-muted-foreground mb-1">Amount</p>
|
||||
<p className="text-xl font-bold">{formatCurrency(p.amountMinor, p.currency || 'ETB')}</p>
|
||||
<p className="text-xl font-bold">{formatCurrency(p.booking?.totalMinor ?? p.amountMinor, 'ETB')}</p>
|
||||
</div>
|
||||
<Field label="Method" value={p.method} />
|
||||
<Field label="Status" value={p.status} />
|
||||
|
||||
@@ -10,6 +10,7 @@ export default function SettingsPage() {
|
||||
const [activeTab, setActiveTab] = useState<Tab>('general');
|
||||
const [seatHoldMinutes, setSeatHoldMinutes] = useState('5');
|
||||
const [holdCutoffHours, setHoldCutoffHours] = useState('2');
|
||||
const [boardingWindowHours, setBoardingWindowHours] = useState('4');
|
||||
const [throttleAuthLimit, setThrottleAuthLimit] = useState('5');
|
||||
const [throttleStrictLimit, setThrottleStrictLimit] = useState('20');
|
||||
const [throttleDefaultLimit, setThrottleDefaultLimit] = useState('100');
|
||||
@@ -24,6 +25,7 @@ export default function SettingsPage() {
|
||||
.then((data) => {
|
||||
if (data?.seat_hold_duration_minutes) setSeatHoldMinutes(data.seat_hold_duration_minutes);
|
||||
if (data?.hold_cutoff_hours_before_departure) setHoldCutoffHours(data.hold_cutoff_hours_before_departure);
|
||||
if (data?.boarding_window_hours_before_departure) setBoardingWindowHours(data.boarding_window_hours_before_departure);
|
||||
if (data?.throttle_auth_limit) setThrottleAuthLimit(data.throttle_auth_limit);
|
||||
if (data?.throttle_strict_limit) setThrottleStrictLimit(data.throttle_strict_limit);
|
||||
if (data?.throttle_default_limit) setThrottleDefaultLimit(data.throttle_default_limit);
|
||||
@@ -39,6 +41,7 @@ export default function SettingsPage() {
|
||||
await systemConfigApi.update({
|
||||
seat_hold_duration_minutes: seatHoldMinutes,
|
||||
hold_cutoff_hours_before_departure: holdCutoffHours,
|
||||
boarding_window_hours_before_departure: boardingWindowHours,
|
||||
throttle_auth_limit: throttleAuthLimit,
|
||||
throttle_strict_limit: throttleStrictLimit,
|
||||
throttle_default_limit: throttleDefaultLimit,
|
||||
@@ -184,6 +187,23 @@ export default function SettingsPage() {
|
||||
Seat holds are rejected when this many hours or fewer remain before departure. Default: 2 hours.
|
||||
</p>
|
||||
</div>
|
||||
<div className="max-w-sm space-y-2">
|
||||
<label className="label" htmlFor="boarding-window">
|
||||
Boarding Window Before Departure (hours)
|
||||
</label>
|
||||
<input
|
||||
id="boarding-window"
|
||||
type="number"
|
||||
min="1"
|
||||
max="24"
|
||||
className="input"
|
||||
value={boardingWindowHours}
|
||||
onChange={(e) => setBoardingWindowHours(e.target.value)}
|
||||
/>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Boarding opens this many hours before departure and closes exactly at departure time. Default: 4 hours.
|
||||
</p>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<div className="flex items-center gap-3">
|
||||
|
||||
@@ -848,7 +848,7 @@ export default function SearchPage() {
|
||||
etc.) are portaled to <body> — see ModernDatePicker — so they aren't
|
||||
capped by this wrapper's own stacking context. ── */}
|
||||
<div
|
||||
className="relative pt-4 pb-4 md:pt-0 md:pb-0 md:absolute md:bottom-8 md:left-0 md:right-0 z-[35] px-4 md:px-6"
|
||||
className="relative pt-4 pb-4 md:pt-0 md:pb-0 md:absolute md:bottom-8 md:left-0 md:right-0 z-[30] px-4 md:px-6"
|
||||
ref={widgetRef}
|
||||
>
|
||||
<div className="max-w-6xl mx-auto">
|
||||
|
||||
Reference in New Issue
Block a user