feat(orders): implement QA audit fixes — return flow, refund webhook, TS cleanup

Convex backend (AUDIT-5–10):
- schema: add returnLabelUrl, returnTrackingNumber, returnCarrier fields +
  by_return_tracking_number_and_carrier and by_stripe_payment_intent_id indexes
- orders: markReturnReceived now sets status="completed"; add getOrderByPaymentIntent
  and applyReturnAccepted internal helpers
- returnActions: add acceptReturn action — creates Shippo return label (is_return:true),
  persists label data, sends return label email to customer
- stripeActions: handle refund.updated webhook to auto-mark orders refunded via Stripe Dashboard
- shippoWebhook: add getOrderByReturnTracking; applyTrackingUpdate extended with
  isReturnTracking flag (return events use return_tracking_update type, skip delivered transition)
- emails: add sendReturnLabelEmail; fulfillmentActions: createShippingLabel action

Admin UI (AUDIT-1–6):
- OrderActionsBar: full rewrite per authoritative action matrix; remove UpdateStatusDialog;
  add AcceptReturnButton for delivered+returnRequested state
- AcceptReturnButton: new action component matching CreateLabelButton pattern
- FulfilmentCard: add returnLabelUrl prop; show "Return label" row; rename outbound
  label to "Outbound label" when both are present
- statusConfig: add return_accepted to OrderEventType and EVENT_TYPE_LABELS
- orders detail page and all supporting cards/components

Storefront & shared (TS fixes):
- checkout/success, CheckoutErrorState, OrderReviewStep, PaymentStep: replace
  Button as/color/isLoading/variant="flat" with HeroUI v3-compatible props
- ReviewList: isLoading → isPending for HeroUI v3 Button
- packages/utils: add return and completed entries to ORDER_STATUS_LABELS/COLORS

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-07 17:59:29 +03:00
parent 8e4309892c
commit 3d50cb895c
32 changed files with 3046 additions and 45 deletions

View File

@@ -1,6 +1,7 @@
"use client";
import { Button, Link } from "@heroui/react";
import { Button } from "@heroui/react";
import Link from "next/link";
/**
* Checkout error state: error message + retry button + back-to-cart link.
@@ -17,17 +18,15 @@ export function CheckoutErrorState({
<div className="flex flex-col items-center gap-4 py-12 text-center">
<p className="text-lg font-medium text-danger">{message}</p>
<div className="flex flex-col gap-2 w-full md:w-auto md:flex-row">
<Button color="primary" onPress={onRetry} className="w-full md:w-auto">
<Button variant="primary" onPress={onRetry} className="w-full md:w-auto">
Try again
</Button>
<Button
as={Link}
<Link
href="/cart"
variant="flat"
className="w-full md:w-auto"
className="inline-flex items-center justify-center rounded-medium gap-2 px-4 py-2 w-full md:w-auto bg-default-100 hover:bg-default-200 text-foreground font-medium transition-colors"
>
Back to cart
</Button>
</Link>
</div>
</div>
);

View File

@@ -119,7 +119,7 @@ export function OrderReviewStep({
<div className="flex flex-col gap-2">
<Button
color="primary"
variant="primary"
className="w-full bg-[#236f6b] font-medium text-white"
size="lg"
onPress={handleContinue}

View File

@@ -80,7 +80,7 @@ export function PaymentStep({
</Alert>
<div className="flex flex-col gap-2 md:flex-row">
<Button
color="primary"
variant="primary"
className="w-full bg-[#236f6b] font-medium text-white md:w-auto"
onPress={initSession}
>
@@ -147,7 +147,7 @@ function CheckoutForm({
</Alert>
<div className="flex flex-col gap-2 md:flex-row">
<Button
color="primary"
variant="primary"
className="w-full bg-[#236f6b] font-medium text-white md:w-auto"
onPress={onSessionExpired}
>
@@ -237,12 +237,12 @@ function CheckoutForm({
<div className="flex flex-col gap-2">
<Button
color="primary"
variant="primary"
className="w-full bg-[#236f6b] font-medium text-white"
size="lg"
onPress={handleSubmit}
isDisabled={isSubmitting || !checkout.canConfirm}
isLoading={isSubmitting}
isPending={isSubmitting}
>
{isSubmitting ? "Processing…" : `Pay ${total.amount}`}
</Button>

View File

@@ -40,7 +40,7 @@ export function ReviewList({ reviews, total, hasMore, isLoading, onLoadMore }: P
<Button
variant="ghost"
onPress={onLoadMore}
isLoading={isLoading}
isPending={isLoading}
className="w-full md:w-auto text-[var(--foreground)] border border-[var(--separator)]"
>
Show more reviews