mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-27 00:52:50 +00:00
feat: add payment modal to the invoice detail page
This commit is contained in:
@@ -15,12 +15,15 @@ import {
|
||||
Text,
|
||||
Title,
|
||||
} from "@mantine/core";
|
||||
import { ArrowLeft, CreditCard, Download, ExternalLink, Info, Receipt } from "lucide-react";
|
||||
import { ArrowLeft, CreditCard, Download, ExternalLink, Receipt } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
|
||||
import { api } from "@/services/api";
|
||||
import { invoicesService } from "@/services/invoices.service";
|
||||
import { paymentsService, type PaymentMethod } from "@/services/payments.service";
|
||||
import { warehouseInvoicesService } from "@/services/warehouse-invoices.service";
|
||||
import { PaymentMethodModal } from "@/pages/bookings/BookingDetailPage/components/PaymentMethodModal";
|
||||
import { saveBlob } from "@/utils/download";
|
||||
import { formatCurrency } from "@/lib/currency";
|
||||
import { BORDER, INK, MUTED } from "../contracts/contract-ui";
|
||||
@@ -53,14 +56,29 @@ export default function InvoiceDetailPage() {
|
||||
api.invoices.get.queryOptions({ input: { id } }),
|
||||
);
|
||||
|
||||
const payMutation = useMutation(
|
||||
api.invoices.pay.mutationOptions({
|
||||
onSuccess: (res) => {
|
||||
const url = res.clientAction?.url;
|
||||
if (url) window.location.href = url;
|
||||
},
|
||||
}),
|
||||
);
|
||||
const [payModalOpen, setPayModalOpen] = useState(false);
|
||||
|
||||
// Extracted for payMutation callbacks — guaranteed defined when they run
|
||||
// (guarded by the early return below).
|
||||
const invSource = invoice?.source;
|
||||
const invSourceId = invoice?.sourceId;
|
||||
|
||||
const payMutation = useMutation({
|
||||
mutationFn: async (method: PaymentMethod) => {
|
||||
const bookingId =
|
||||
invSource === "warehouse"
|
||||
? (await warehouseInvoicesService.get(id)).bookingId ?? invSourceId!
|
||||
: invSourceId!;
|
||||
return api.payments.initiate.call({ bookingId, method });
|
||||
},
|
||||
onSuccess: (data, method) => {
|
||||
const redirectUrl =
|
||||
data?.clientAction?.type === "REDIRECT" && data.clientAction.url
|
||||
? data.clientAction.url
|
||||
: paymentsService.checkoutUrl({ bookingId: invSourceId!, method });
|
||||
window.location.href = redirectUrl;
|
||||
},
|
||||
});
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
@@ -94,9 +112,7 @@ export default function InvoiceDetailPage() {
|
||||
const lines = invoice.lines ?? [];
|
||||
|
||||
const handlePay = () => {
|
||||
const returnUrl = `${window.location.origin}/payment/success`;
|
||||
const failureUrl = `${window.location.origin}/payment/failure`;
|
||||
payMutation.mutate({ id, payload: { returnUrl, failureUrl } });
|
||||
setPayModalOpen(true);
|
||||
};
|
||||
|
||||
const hasReceipt = Number(invoice.paidAmount) > 0;
|
||||
@@ -221,12 +237,6 @@ export default function InvoiceDetailPage() {
|
||||
</Group>
|
||||
</Group>
|
||||
|
||||
{payMutation.isError && (
|
||||
<Alert color="red" icon={<Info size={16} />} title="Payment could not be started">
|
||||
Please try again, or contact support if the problem persists.
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
{/* Summary */}
|
||||
<Paper withBorder radius="lg" p="lg" style={{ borderColor: BORDER }}>
|
||||
<SimpleGrid cols={{ base: 1, sm: 2, md: 4 }} spacing="lg">
|
||||
@@ -330,6 +340,27 @@ export default function InvoiceDetailPage() {
|
||||
</Table>
|
||||
</Box>
|
||||
</Paper>
|
||||
|
||||
<PaymentMethodModal
|
||||
opened={payModalOpen}
|
||||
onClose={() => {
|
||||
if (!payMutation.isPending) {
|
||||
setPayModalOpen(false);
|
||||
payMutation.reset();
|
||||
}
|
||||
}}
|
||||
amountLabel={formatCurrency(Number(invoice.totalAmount), invoice.currency)}
|
||||
currency={invoice.currency}
|
||||
processing={payMutation.isPending}
|
||||
error={
|
||||
payMutation.isError
|
||||
? payMutation.error instanceof Error
|
||||
? payMutation.error.message
|
||||
: "Could not start payment. Please try again."
|
||||
: null
|
||||
}
|
||||
onConfirm={(method) => payMutation.mutate(method)}
|
||||
/>
|
||||
</Stack>
|
||||
</Box>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user