Files
edr-platform/apps/edr-passenger-web/backoffice/src/app/payments/SupplementaryChargesModal.tsx
2026-07-16 15:18:01 +03:00

83 lines
4.0 KiB
TypeScript

'use client';
import { useState } from 'react';
import { PlusCircle } from 'lucide-react';
import Modal from '@/components/ui/Modal';
import ActionButton from '@/components/ui/ActionButton';
import { useCreateSupplementaryCharge } from './useSupplementaryCharges';
const REASONS = ['UNDERPAYMENT', 'FARE_CORRECTION', 'CURRENCY_ADJUSTMENT', 'OTHER'];
interface Props {
isOpen: boolean;
onClose: () => void;
}
export default function SupplementaryChargesModal({ isOpen, onClose }: Props) {
const [form, setForm] = useState({ bookingRef: '', amountEtb: '', reason: 'UNDERPAYMENT', notes: '' });
const [formError, setFormError] = useState<string | null>(null);
const [createSuccess, setCreateSuccess] = useState<string | null>(null);
const createMutation = useCreateSupplementaryCharge(() => {
setCreateSuccess('Charge created and payment link sent.');
setForm({ bookingRef: '', amountEtb: '', reason: 'UNDERPAYMENT', notes: '' });
setFormError(null);
setTimeout(() => { setCreateSuccess(null); onClose(); }, 2000);
});
const handleCreate = async () => {
setFormError(null);
const amountMinor = Math.round(parseFloat(form.amountEtb) * 100);
if (!form.bookingRef.trim()) return setFormError('Booking reference is required');
if (!form.amountEtb || isNaN(amountMinor) || amountMinor <= 0) return setFormError('Enter a valid amount');
try {
await createMutation.mutateAsync({ bookingRef: form.bookingRef.trim(), amountMinor, reason: form.reason, notes: form.notes || undefined });
} catch (e: any) {
setFormError(e?.response?.data?.message ?? e?.message ?? 'Failed to create charge');
}
};
return (
<Modal isOpen={isOpen} onClose={onClose} title="Raise Supplementary Charge" size="lg">
<div className="space-y-4">
{createSuccess && (
<div className="rounded-lg bg-green-50 dark:bg-green-900/20 p-3 text-sm text-green-800 dark:text-green-200"> {createSuccess}</div>
)}
{formError && (
<div className="rounded-lg bg-red-50 dark:bg-red-900/20 p-3 text-sm text-red-700 dark:text-red-300">{formError}</div>
)}
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="md:col-span-2">
<label className="label">Booking Reference <span className="text-red-500">*</span></label>
<input className="input" placeholder="e.g. EDR-20240001" value={form.bookingRef} onChange={(e) => setForm({ ...form, bookingRef: e.target.value })} />
</div>
<div>
<label className="label">Amount Owed (ETB) <span className="text-red-500">*</span></label>
<input className="input" type="number" min="0.01" step="0.01" placeholder="e.g. 50.00" value={form.amountEtb} onChange={(e) => setForm({ ...form, amountEtb: e.target.value })} />
</div>
<div>
<label className="label">Reason <span className="text-red-500">*</span></label>
<select className="input" value={form.reason} onChange={(e) => setForm({ ...form, reason: e.target.value })}>
{REASONS.map((r) => <option key={r} value={r}>{r.replace('_', ' ')}</option>)}
</select>
</div>
<div className="md:col-span-2">
<label className="label">Notes (optional)</label>
<textarea className="input resize-none" rows={2} placeholder="e.g. Passenger paid 350 ETB, correct fare is 400 ETB" value={form.notes} onChange={(e) => setForm({ ...form, notes: e.target.value })} />
</div>
</div>
<p className="text-xs text-muted-foreground">
A payment link will be sent to the passenger's registered phone/email. The link expires in 72 hours.
</p>
<div className="flex justify-end gap-2 pt-2 border-t border-muted">
<ActionButton variant="secondary" onClick={onClose}>Cancel</ActionButton>
<ActionButton icon={PlusCircle} onClick={handleCreate} loading={createMutation.isPending}>Raise Charge</ActionButton>
</div>
</div>
</Modal>
);
}