'use client'; import React, { useState } from 'react'; import { formatCurrency } from '../../../shared/utils/currency.js'; import { Card, Table, Select } from '../../../shared/components/index.js'; import { parseUTCDate, formatDateForDisplay, getDaysBetween, isOverdue, getTodayUTC } from '../../../shared/lib/dates.js'; const DATE_LOCALE = 'fr-FR'; /** * Public Invoice Payment Page Component * Allows clients to view and pay invoices via public link */ const PaymentPage = ({ invoice, onPaymentSubmit, stripeEnabled = false, interacEnabled = false, interacEmail = null, interacCredentials = null, paymentStatus = null, token = null, publicLogoWhite = '', publicLogoBlack = '', publicDashboardUrl = '', }) => { const [paymentMethod, setPaymentMethod] = useState( stripeEnabled ? 'stripe' : (interacEnabled ? 'interac' : 'other') ); const [isProcessing, setIsProcessing] = useState(false); // Payment method options for Select component const paymentMethodOptions = [ ...(stripeEnabled ? [{ value: 'stripe', label: "Carte de crédit" }] : []), ...(interacEnabled ? [{ value: 'interac', label: "Virement Interac" }] : []), { value: 'other', label: "Autre" } ]; if (!invoice) { return (

Facture non trouvée

La facture que vous recherchez n'existe pas ou a été supprimée.

); } const clientName = invoice.company_name || `${invoice.first_name} ${invoice.last_name}`; const isPaid = invoice.status === 'paid'; const invoiceIsOverdue = isOverdue(invoice.due_date) && !isPaid; const principalAmount = parseFloat(invoice.total_amount); const interestAmount = parseFloat(invoice.interest_amount || 0); const totalWithInterest = principalAmount + interestAmount; const paidAmount = parseFloat(invoice.paid_amount || 0); const remainingAmount = totalWithInterest - paidAmount; const totalAmount = totalWithInterest; const hasInterest = interestAmount > 0; const handlePayment = async () => { if (onPaymentSubmit) { setIsProcessing(true); try { await onPaymentSubmit(paymentMethod); } finally { setIsProcessing(false); } } }; // Calculate days remaining const daysRemaining = getDaysBetween(getTodayUTC(), invoice.due_date); const dueDateFormatted = formatDateForDisplay(invoice.due_date, DATE_LOCALE); // Table columns configuration for invoice items const invoiceItemsColumns = [ { key: 'name', label: "Description", render: (item) => (

{item.name}

{item.description && (

{item.description}

)}
) }, { key: 'quantity', label: "Quantité", noWrap: false, render: (item) => (
{item.quantity}
) }, { key: 'unit_price', label: "Prix unitaire", noWrap: false, render: (item) => (
{formatCurrency(item.unit_price)}
) }, { key: 'total', label: "Total", headerAlign: 'right', noWrap: false, render: (item) => (
{formatCurrency(item.total)}
) } ]; const hasLogo = publicLogoWhite || publicLogoBlack; const logoHref = publicDashboardUrl && publicDashboardUrl.trim() !== '' ? publicDashboardUrl.trim() : null; return (
{/* Invoice to Pay Header */}
{/* Public logo (black in light theme, white in dark theme); link to dashboard when URL configured */} {hasLogo && (
{logoHref ? ( {publicLogoBlack && ( )} {publicLogoWhite && ( )} ) : ( <> {publicLogoBlack && ( )} {publicLogoWhite && ( )} )}
)} {remainingAmount > 0 && ( <>

Facture à payer

Vous avez une facture à payer de {formatCurrency(totalAmount)} avant le {dueDateFormatted}.

)} {remainingAmount === 0 && ( <>

Facture payée

Vous avez une facture de {formatCurrency(totalAmount)} qui a été payée.

)}
{(paymentStatus === 'success' || paymentStatus === 'cancelled' || (invoiceIsOverdue && !isPaid)) && (
{/* Status Badge */} {paymentStatus === 'success' && (

✓ Paiement effectué avec succès !

Vous recevrez un email de confirmation sous peu.

)} {paymentStatus === 'cancelled' && (

Le paiement a été annulé

Vous pouvez réessayer ci-dessous.

)} {(invoiceIsOverdue && !isPaid) && (

Cette facture est en retard. Veuillez la payer le plus rapidement possible.

)}
)}
{/* Invoice Details */}
{/* Invoice Amount Section */}

Montant de la facture

{formatCurrency(totalAmount)}
{remainingAmount > 0 && (

Payable avant le {dueDateFormatted} ({daysRemaining > 0 ? `${daysRemaining} jours restants` : 'En retard'})

)} {remainingAmount === 0 && (

Payable avant le {dueDateFormatted}

)}
{/* Downloads Section */}

Téléchargements

{isPaid && ( )}
{/* Items Table */} {/* Totals Section */} {invoice.items && invoice.items.length > 0 && (
{/* Subtotal */}
Sous-total {formatCurrency(invoice.items.reduce((sum, item) => sum + parseFloat(item.total || 0), 0))}
{/* Interest (if applicable) */} {hasInterest && (
Intérêt {formatCurrency(interestAmount)}
)} {/* Total */}
Total {formatCurrency(totalWithInterest)}
)} {/* Notes */} {invoice.notes && (

Notes

{invoice.notes}

)} {/* Payment Section */}
{isPaid ? (

Payé en totalité

Payé le {formatDateForDisplay(invoice.paid_at, DATE_LOCALE)}

) : (

Montant à payer

{formatCurrency(remainingAmount)}