mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-26 18:42:49 +00:00
@@ -37,6 +37,7 @@ function BookingsPageContent() {
|
||||
const [selectedBooking, setSelectedBooking] = useState<any>(null);
|
||||
const [generateTicketBooking, setGenerateTicketBooking] = useState<any>(null);
|
||||
const [generateTicketForm, setGenerateTicketForm] = useState({ paymentReference: '', paymentMethod: '', notes: '' });
|
||||
const [generateTicketTouched, setGenerateTicketTouched] = useState({ paymentReference: false, paymentMethod: false });
|
||||
const [deleteConfirmOpen, setDeleteConfirmOpen] = useState(false);
|
||||
const [bookingToDelete, setBookingToDelete] = useState<any>(null);
|
||||
const [deleteError, setDeleteError] = useState<string | null>(null);
|
||||
@@ -75,6 +76,7 @@ function BookingsPageContent() {
|
||||
setSelectedBooking(null);
|
||||
setGenerateTicketBooking(null);
|
||||
setGenerateTicketForm({ paymentReference: '', paymentMethod: '', notes: '' });
|
||||
setGenerateTicketTouched({ paymentReference: false, paymentMethod: false });
|
||||
},
|
||||
});
|
||||
|
||||
@@ -276,7 +278,7 @@ function BookingsPageContent() {
|
||||
|
||||
const actions = [
|
||||
{ label: 'View Details', onClick: (b: any) => setSelectedBooking(b), variant: 'secondary' as const, icon: Eye },
|
||||
{ label: 'Generate Ticket', onClick: (b: any) => { setGenerateTicketForm({ paymentReference: '', paymentMethod: '', notes: '' }); setGenerateTicketBooking(b); }, variant: 'secondary' as const, icon: Ticket, show: (b: any) => !(b.status === 'CONFIRMED' && b.paymentIntent?.status === 'SUCCEEDED') },
|
||||
{ label: 'Generate Ticket', onClick: (b: any) => { setGenerateTicketForm({ paymentReference: '', paymentMethod: '', notes: '' }); setGenerateTicketTouched({ paymentReference: false, paymentMethod: false }); setGenerateTicketBooking(b); }, variant: 'secondary' as const, icon: Ticket, show: (b: any) => !(b.status === 'CONFIRMED' && b.paymentIntent?.status === 'SUCCEEDED') },
|
||||
{ label: 'Delete', onClick: (b: any) => { setDeleteError(null); setDeleteCascade(false); setDeleteCascadeChecked(false); setBookingToDelete(b); setDeleteConfirmOpen(true); }, variant: 'danger' as const, icon: Trash2 },
|
||||
];
|
||||
|
||||
@@ -558,7 +560,7 @@ function BookingsPageContent() {
|
||||
{/* Generate Ticket Modal */}
|
||||
<Modal
|
||||
isOpen={!!generateTicketBooking}
|
||||
onClose={() => { setGenerateTicketBooking(null); setGenerateTicketForm({ paymentReference: '', paymentMethod: '', notes: '' }); forceConfirmMutation.reset(); }}
|
||||
onClose={() => { setGenerateTicketBooking(null); setGenerateTicketForm({ paymentReference: '', paymentMethod: '', notes: '' }); setGenerateTicketTouched({ paymentReference: false, paymentMethod: false }); forceConfirmMutation.reset(); }}
|
||||
title="Generate Ticket"
|
||||
size="md"
|
||||
>
|
||||
@@ -573,22 +575,27 @@ function BookingsPageContent() {
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="label">Payment Reference / Transaction ID</label>
|
||||
<label className="label">Payment Reference / Transaction ID <span className="text-red-500">*</span></label>
|
||||
<input
|
||||
type="text"
|
||||
className="input"
|
||||
className={`input${generateTicketTouched.paymentReference && !generateTicketForm.paymentReference ? ' border-red-400 dark:border-red-500' : ''}`}
|
||||
placeholder="e.g. TXN-123456 or vendor receipt number"
|
||||
value={generateTicketForm.paymentReference}
|
||||
onChange={(e) => setGenerateTicketForm(f => ({ ...f, paymentReference: e.target.value }))}
|
||||
onBlur={() => setGenerateTicketTouched(t => ({ ...t, paymentReference: true }))}
|
||||
/>
|
||||
{generateTicketTouched.paymentReference && !generateTicketForm.paymentReference && (
|
||||
<p className="text-xs text-red-500 mt-1">Payment reference is required</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="label">Payment Method</label>
|
||||
<label className="label">Payment Method <span className="text-red-500">*</span></label>
|
||||
<select
|
||||
className="input"
|
||||
className={`input${generateTicketTouched.paymentMethod && !generateTicketForm.paymentMethod ? ' border-red-400 dark:border-red-500' : ''}`}
|
||||
value={generateTicketForm.paymentMethod}
|
||||
onChange={(e) => setGenerateTicketForm(f => ({ ...f, paymentMethod: e.target.value }))}
|
||||
onBlur={() => setGenerateTicketTouched(t => ({ ...t, paymentMethod: true }))}
|
||||
>
|
||||
<option value="">Select method…</option>
|
||||
<option value="TELEBIRR">Telebirr</option>
|
||||
@@ -599,6 +606,9 @@ function BookingsPageContent() {
|
||||
<option value="CARD">Card</option>
|
||||
<option value="WALLET">Wallet</option>
|
||||
</select>
|
||||
{generateTicketTouched.paymentMethod && !generateTicketForm.paymentMethod && (
|
||||
<p className="text-xs text-red-500 mt-1">Payment method is required</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
@@ -620,19 +630,23 @@ function BookingsPageContent() {
|
||||
<div className="flex justify-end gap-2 pt-2 border-t border-muted">
|
||||
<ActionButton
|
||||
variant="secondary"
|
||||
onClick={() => { setGenerateTicketBooking(null); setGenerateTicketForm({ paymentReference: '', paymentMethod: '', notes: '' }); forceConfirmMutation.reset(); }}
|
||||
onClick={() => { setGenerateTicketBooking(null); setGenerateTicketForm({ paymentReference: '', paymentMethod: '', notes: '' }); setGenerateTicketTouched({ paymentReference: false, paymentMethod: false }); forceConfirmMutation.reset(); }}
|
||||
>
|
||||
Cancel
|
||||
</ActionButton>
|
||||
<ActionButton
|
||||
onClick={() => forceConfirmMutation.mutate({
|
||||
bookingId: generateTicketBooking.id,
|
||||
data: {
|
||||
paymentReference: generateTicketForm.paymentReference || undefined,
|
||||
paymentMethod: generateTicketForm.paymentMethod || undefined,
|
||||
notes: generateTicketForm.notes || undefined,
|
||||
},
|
||||
})}
|
||||
onClick={() => {
|
||||
setGenerateTicketTouched({ paymentReference: true, paymentMethod: true });
|
||||
if (!generateTicketForm.paymentReference || !generateTicketForm.paymentMethod) return;
|
||||
forceConfirmMutation.mutate({
|
||||
bookingId: generateTicketBooking.id,
|
||||
data: {
|
||||
paymentReference: generateTicketForm.paymentReference,
|
||||
paymentMethod: generateTicketForm.paymentMethod,
|
||||
notes: generateTicketForm.notes || undefined,
|
||||
},
|
||||
});
|
||||
}}
|
||||
disabled={forceConfirmMutation.isPending}
|
||||
>
|
||||
{forceConfirmMutation.isPending ? 'Generating…' : 'Confirm & Generate Ticket'}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import Link from 'next/link';
|
||||
import Image from 'next/image';
|
||||
import ThemeToggle from './ThemeToggle';
|
||||
|
||||
// AppSidebar (which carries the logo) is `hidden lg:flex` — invisible on
|
||||
// mobile, so mobile pages had no branding/way back to home at all. This is
|
||||
@@ -19,6 +20,9 @@ export default function MobileTopBar() {
|
||||
priority
|
||||
/>
|
||||
</Link>
|
||||
<div className="ml-auto">
|
||||
<ThemeToggle />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user