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:
@@ -3,7 +3,8 @@
|
||||
import { Suspense, useCallback, useEffect, useRef, useState } from "react";
|
||||
import { useSearchParams } from "next/navigation";
|
||||
import { useAction } from "convex/react";
|
||||
import { Button, Card, Link, Spinner } from "@heroui/react";
|
||||
import Link from "next/link";
|
||||
import { Card, Spinner } from "@heroui/react";
|
||||
import { api } from "../../../../../../convex/_generated/api";
|
||||
|
||||
type PageState =
|
||||
@@ -116,18 +117,18 @@ function CompleteView({ email }: { email: string | null }) {
|
||||
</div>
|
||||
|
||||
<div className="flex w-full flex-col gap-2 pt-2">
|
||||
<Button
|
||||
as={Link}
|
||||
<Link
|
||||
href="/account/orders"
|
||||
color="primary"
|
||||
className="w-full bg-[#236f6b] font-medium text-white"
|
||||
size="lg"
|
||||
className="inline-flex items-center justify-center rounded-lg gap-2 px-4 py-3 w-full bg-[#236f6b] font-medium text-white hover:bg-[#1e5f5b] transition-colors text-base"
|
||||
>
|
||||
View your orders
|
||||
</Button>
|
||||
<Button as={Link} href="/shop" variant="ghost" className="w-full">
|
||||
</Link>
|
||||
<Link
|
||||
href="/shop"
|
||||
className="inline-flex items-center justify-center rounded-medium gap-2 px-4 py-3 w-full bg-transparent hover:bg-default-100 text-foreground font-medium transition-colors"
|
||||
>
|
||||
Continue shopping
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
@@ -165,18 +166,18 @@ function IncompleteView() {
|
||||
</div>
|
||||
|
||||
<div className="flex w-full flex-col gap-2 pt-2">
|
||||
<Button
|
||||
as={Link}
|
||||
<Link
|
||||
href="/checkout"
|
||||
color="primary"
|
||||
className="w-full bg-[#236f6b] font-medium text-white"
|
||||
size="lg"
|
||||
className="inline-flex items-center justify-center rounded-lg gap-2 px-4 py-3 w-full bg-[#236f6b] font-medium text-white hover:bg-[#1e5f5b] transition-colors text-base"
|
||||
>
|
||||
Return to checkout
|
||||
</Button>
|
||||
<Button as={Link} href="/shop" variant="ghost" className="w-full">
|
||||
</Link>
|
||||
<Link
|
||||
href="/shop"
|
||||
className="inline-flex items-center justify-center rounded-medium gap-2 px-4 py-3 w-full bg-transparent hover:bg-default-100 text-foreground font-medium transition-colors"
|
||||
>
|
||||
Continue shopping
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
@@ -211,18 +212,18 @@ function ErrorView({ message }: { message: string }) {
|
||||
</div>
|
||||
|
||||
<div className="flex w-full flex-col gap-2 pt-2">
|
||||
<Button
|
||||
as={Link}
|
||||
<Link
|
||||
href="/checkout"
|
||||
color="primary"
|
||||
className="w-full bg-[#236f6b] font-medium text-white"
|
||||
size="lg"
|
||||
className="inline-flex items-center justify-center rounded-lg gap-2 px-4 py-3 w-full bg-[#236f6b] font-medium text-white hover:bg-[#1e5f5b] transition-colors text-base"
|
||||
>
|
||||
Return to checkout
|
||||
</Button>
|
||||
<Button as={Link} href="/" variant="ghost" className="w-full">
|
||||
</Link>
|
||||
<Link
|
||||
href="/"
|
||||
className="inline-flex items-center justify-center rounded-medium gap-2 px-4 py-3 w-full bg-transparent hover:bg-default-100 text-foreground font-medium transition-colors"
|
||||
>
|
||||
Go to homepage
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user