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