feat(gro-609): add Stripe details to invoice modal and fix stats date filter

- Add GET /api/invoices/:id/stripe-details endpoint to fetch card last4 and
  payment status from Stripe
- Add getPaymentIntentDetails() to payment service
- Fix stats summary query to filter by startOfMonth
- Add cardLast4, paymentStatus, stripeRefundId transient fields to Invoice type
- Display Stripe details (card last4, payment status, refund status) in modal
- Add stripeRefundId and paymentFailureReason to Invoice schema (was missing in dev types)

Ref: GRO-609
Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
Test User
2026-04-19 01:02:49 +00:00
committed by Flea Flicker [agent]
parent 9479c152eb
commit 14fa7db5b5
4 changed files with 73 additions and 1 deletions
+26
View File
@@ -176,6 +176,19 @@ function InvoiceDetailModal({
const [showRefundDialog, setShowRefundDialog] = useState(false);
const [refundType, setRefundType] = useState<"full" | "partial">("full");
const [partialAmount, setPartialAmount] = useState("");
const [stripeDetails, setStripeDetails] = useState<{ cardLast4: string | null; paymentStatus: string | null; stripeRefundId: string | null } | null>(null);
// Fetch Stripe details when modal opens for paid invoices with a payment intent
useEffect(() => {
if (invoice.status === "paid" && invoice.stripePaymentIntentId) {
fetch(`/api/invoices/${invoice.id}/stripe-details`)
.then((r) => r.ok ? r.json() : null)
.then((data) => { if (data) setStripeDetails(data); })
.catch(() => {});
} else {
setStripeDetails(null);
}
}, [invoice.id, invoice.status, invoice.stripePaymentIntentId]);
// Tip split state: array of {staffId, staffName, pct}
const linkedAppt = invoice.appointmentId
@@ -367,6 +380,19 @@ function InvoiceDetailModal({
/>
{invoice.paidAt && <SummaryRow label="Paid on" value={fmtDate(invoice.paidAt)} />}
{invoice.paymentMethod && <SummaryRow label="Payment" value={invoice.paymentMethod} />}
{stripeDetails && (
<>
{stripeDetails.cardLast4 && (
<SummaryRow label="Card" value={`•••• ${stripeDetails.cardLast4}`} />
)}
{stripeDetails.paymentStatus && (
<SummaryRow label="Stripe status" value={stripeDetails.paymentStatus} />
)}
{stripeDetails.stripeRefundId && (
<SummaryRow label="Refund" value="Refunded" />
)}
</>
)}
</div>
{/* ── Tip Distribution ── */}